Skip to content

justinbarclay/.emacs.d

Repository files navigation

./emacs-dragon.png

Table of Contents

About

“Instead of imagining that our main task is to instruct a computer what to do, let us concentrate rather on explaining to human beings what we want a computer to do.” - Donald Knuth This Config file is inspired by:

Note for making Elisp Blocks

I’ve forgotten this before so this seems like the perfect place to put it but C-c C-v d or org-babel-demarcate-block creates a code-block for the language of your choice

Setup

Turn off garbage collection

This sets the garbage collection threshold to 100mb Reset garbage collection to emacs default after 5s

(setq gc-cons-threshold 1000000000)
(run-with-idle-timer
 5 nil
 (lambda ()
   (setq-default gc-cons-threshold (* 1024 1024 100))
   (message "gc-cons-threshold restored to %S"
            gc-cons-threshold)))

Turn off bytecompiler warnings

They are really noisy and annoying right now. And nothing I can deal with.

(setq native-comp-deferred-compilation-deny-list '())
(setq native-comp-async-report-warnings-errors nil)

Unload some built-in packages

Other dependencies need updated packages and Elpaca is not good about unloading them. So we have to do it for Elpaca.

(when (featurep 'jsonrpc)
    (unload-feature 'jsonrpc))

Move customizations to their own file

(setq custom-file (expand-file-name "customs.el" user-emacs-directory))

(add-hook 'after-init-hook (lambda () (load custom-file 'noerror)))

Let’s make the Emacs start up run fast

(setq-default lexical-binding t
              load-prefer-newer t)

Some recommendations by https://github.com/hlissner/doom-emacs/wiki/FAQ#how-is-dooms-startup-so-fast

(defvar doom--file-name-handler-alist file-name-handler-alist)
(setq file-name-handler-alist nil)

Package repositories

Define custom package repositories besides ELPA. If I am being honest with myself, Marmalade and Tromey are probably not necessary repositories.

(setq package-enable-at-startup nil)
(setq package-user-dir "~/.emacs.d/elpa")
(setq load-prefer-newer t)
(setq package-archives
      '(("melpa" . "http://melpa.org/packages/")
        ("melpa-stable" . "http://stable.melpa.org/packages/")
        ("gnu" . "https://elpa.gnu.org/packages/")
        ("nongnu" . "https://elpa.nongnu.org/nongnu/")))

Personal information

(setq-default user-full-name "Justin Barclay"
              user-mail-address "[email protected]")

Function for making README.org work

Tangle and Compile init file

(defun my/tangle-dotfiles ()
  "If the current file is this file, the code blocks are tangled"
  (interactive)
  (when (equal (buffer-file-name)
   (expand-file-name "~/.emacs.d/README.org"))
    (org-babel-tangle '() (expand-file-name "~/.emacs.d/init.el"))))
    ;;(byte-compile-file "~/.emacs.d/init.el")
(add-hook 'after-save-hook #'my/tangle-dotfiles)

Vars

Let’s describe some variables to help determine how to configure Emacs

OS

(defvar jb/os-linux-p (eq system-type 'gnu/linux))
(defvar jb/os-windows-p (eq system-type 'windows-nt))
(defvar jb/os-macos-p (eq system-type 'darwin))

Package Manager

I’ve decided to experiment with a completely new package manager. The main purpose of this is to better work with git repositories as the main source of packaging system, without relying on quelpa as that can inhibit load times.

Bootstrapping elpaca

(defvar elpaca-installer-version 0.8)
(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 :depth 1
                              :files (:defaults "elpaca-test.el" (: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)
    (when (< emacs-major-version 28) (require 'subr-x))
    (condition-case-unless-debug err
        (if-let* ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*"))
                  ((zerop (apply #'call-process `("git" nil ,buffer t "clone"
                                                  ,@(when-let* ((depth (plist-get order :depth)))
                                                      (list (format "--depth=%d" depth) "--no-single-branch"))
                                                  ,(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)))
            (progn (message "%s" (buffer-string)) (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))
;; Install use-package support
(elpaca elpaca-use-package
  ;; Enable :elpaca use-package keyword.
  (elpaca-use-package-mode)
  ;; Assume :elpaca t unless otherwise specified.
  (setq elpaca-use-package-by-default t))
(when jb/os-windows-p (setq elpaca-queue-limit 20))
;; Block until current queue processed.
(elpaca-wait)

Tent Pole

For big emacs packages, that help define the experience of Emacs itself

use-package

I use Jon Wiegley’s use-package for dependency management. Let’s bootstrap use-package so it can download everything else as we need it.

Performance improvements

It’s a god send, UsePackage landed in Emacs master, no bootstrapping required, just up and go.

(require 'use-package)
(setq use-package-always-ensure t)
(setq use-package-verbose nil)
(setq use-package-always-defer t)
(setq use-package-enable-imenu-support t)

Generate reports based on use-package

(setq use-package-compute-statistics t)
(setq use-package-minimum-reported-time 0.01)

Using use-package

The plan is to use a copious amount of deferral to speed up emacs boot time. Use the :init keyword to execute code before a package is loaded. It accepts one or more forms, up until the next keyword :config can be used to execute code after a package is loaded. The :ensure keyword causes the package(s) to be installed automatically if not already present on your system (set (setq use-package-always-ensure t)) You can override package deferral with the :demand keyword. Thus, even if you use :bind, using :demand will force loading to occur immediately and not establish an autoload for the bound key. In almost all cases you don’t need to manually specify :defer t. This is implied whenever :bind or :mode or :interpreter is used.

Make sure gpg-keys are up to date

(use-package gnu-elpa-keyring-update)

Supporting built in features

As stolen from progfolio

(defmacro use-feature (name &rest args)
  "Like `use-package' but accounting for asynchronous installation.
  NAME and ARGS are in `use-package'."
  (declare (indent defun))
  `(use-package ,name
     :ensure nil
     ,@args))

Debugging

The :disabled keyword can turn off a module you’re having difficulties with, or stop loading something you’re not using at the present time:

(use-package ess-site
  :disabled
  :commands R)

When byte-compiling your .emacs file, disabled declarations are omitted from the output entirely, to accelerate startup times.

Benchmark-init

This is hidden here to load right after we have use-package to be able to benchmark startup

(use-package benchmark-init
  :demand t
  :init
  (benchmark-init/activate)
  :config
  ;; To disable collection of benchmark data after init is done.
  (add-hook 'window-setup-hook 'benchmark-init/deactivate))

seq

(defun +elpaca-unload-seq (e)
  (and (featurep 'seq) (unload-feature 'seq t))
  (elpaca--continue-build e))

;; You could embed this code directly in the reicpe, I just abstracted it into a function.
(defun +elpaca-seq-build-steps ()
  (append (butlast (if (file-exists-p (expand-file-name "seq" elpaca-builds-directory))
                       elpaca--pre-built-steps elpaca-build-steps))
          (list '+elpaca-unload-seq 'elpaca--activate-package)))

(use-package seq
  :init
  (require 'seq)
  :ensure `(seq :build ,(+elpaca-seq-build-steps)))

Org Mode

Org

(use-package org
  :defer t
  :bind
  (("C-c a" . org-agenda)
   ("C-c c" . org-capture))
  :init
  (global-unset-key "\C-c\C-v\C-c")
  :hook (org-mode . visual-line-mode)
  :config
  (setq org-src-tab-acts-natively nil)
  (defun jb/org-narrow-to-parent ()
    "Narrow buffer to the current subtree."
    (interactive)
    (widen)
    (org-up-element)
    (save-excursion
      (save-match-data
        (org-with-limited-levels
         (narrow-to-region
          (progn
            (org-back-to-heading t) (point))
          (progn (org-end-of-subtree t t)
                 (when (and (org-at-heading-p) (not (eobp))) (backward-char 1))
                 (point)))))))
  (defun jb/org-clear-results ()
    (interactive)
    (org-babel-remove-result-one-or-many 't))
  ;; As liberally borrowed from:
  ;; https://github.com/Fuco1/.emacs.d/blob/76e80dd07320b079fa26db3af6096d8d8a4f3bb9/files/org-defs.el#L1863C1-L1922C57     
 (defun my-org-archive-file ()
  "Get the archive file for the current org buffer."
  (car (org-archive--compute-location org-archive-location)))

 (defadvice org-archive-subtree (around fix-hierarchy activate)
   (let* ((fix-archive-p (and (not current-prefix-arg)
                              (not (use-region-p))))
          (afile (my-org-archive-file))
          (buffer (or (find-buffer-visiting afile) (find-file-noselect afile)))
          ;; Get all the parents and their tags, we will try to
          ;; recreate the same situation in the archive buffer.
          ;; TODO: make this customizable.
          (parents-and-tags (save-excursion
                              (let (parents)
                                (while (org-up-heading-safe)
                                  (push (list :heading (org-get-heading t t t t)
                                              :tags (org-get-tags nil :local))
                                        parents))
                                parents))))
     ad-do-it
     (when fix-archive-p
       (with-current-buffer buffer
         (goto-char (point-max))
         (while (org-up-heading-safe))
         (let* ((olpath (org-entry-get (point) "ARCHIVE_OLPATH"))
                ;; TODO: Factor out dash.el
                (path (and olpath
                           (--map
                            (replace-regexp-in-string "^/" "" it)
                            (s-slice-at "/\\sw" olpath))))
                (level 1)
                tree-text)
           (when olpath
             (org-mark-subtree)
             (setq tree-text (buffer-substring (region-beginning) (region-end)))
             (let (this-command) (org-cut-subtree))
             (goto-char (point-min))
             (save-restriction
               (widen)
               (-each path
                 (lambda (heading)
                   (if (re-search-forward
                        (rx-to-string
                         `(: bol (repeat ,level "*") (1+ " ") ,heading)) nil t)
                       (progn
                         (org-narrow-to-subtree)
                         (org-set-tags (plist-get (car parents-and-tags) :tags)))
                     (goto-char (point-max))
                     (unless (looking-at "^")
                       (insert "\n"))
                     (insert (make-string level ?*)
                             " "
                             heading)
                     (org-set-tags (plist-get (car parents-and-tags) :tags))
                     (end-of-line)
                     (insert "\n"))
                   (pop parents-and-tags)
                   (cl-incf level)))
               (widen)
               (org-end-of-subtree t t)
               (org-paste-subtree level tree-text))))))))

  (defun run-org-block ()
    (interactive)
    (save-excursion
      (goto-char
       (org-babel-find-named-block
        (completing-read "Code Block: " (org-babel-src-block-names))))
      (org-babel-execute-src-block-maybe)))
  (setq org-directory "~/dev/diary")
  (setq org-agenda-files (list (concat org-directory "/personal/calendar.org")
                               (concat org-directory "/work/calendar.org")
                               (concat org-directory "/personal/tasks.org")
                               (concat org-directory "/work/tasks.org"))
        org-todo-keywords
        '((sequence "TODO(t)" "INPROGRESS(i)" "|" "DONE(d)")
          ("WAITING(w@/!)" "HOLD(h@/!)" "|" "CANCELLED(c@/!)" "PHONE" "MEETING"))

        org-todo-keyword-faces
        '(("TODO" :foreground "red" :weight regular)
          ("INPROGRESS" :foreground "blue" :weight regular)
          ("DONE" :foreground "forest green" :weight regular)
          ("WAITING" :foreground "orange" :weight regular)
          ("BLOCKED" :foreground "magenta" :weight regular)
          ("CANCELLED" :foreground "forest green" :weight regular))
        org-log-into-drawer 't
        org-startup-truncated nil
        org-default-notes-file (concat org-directory "/notes.org")
        org-export-html-postamble nil
        org-hide-leading-stars 't
        org-startup-folded 'overview
        org-startup-indented 't)
   ;; Add ts language support
  (add-to-list 'org-src-lang-modes '("tsx" . tsx-ts))
  (add-to-list 'org-src-lang-modes '("typescript" . typescript-ts))
  (add-to-list 'org-src-lang-modes '("jsx" . jsx-ts))
  (add-to-list 'org-src-lang-modes '("javascript" . javascript-ts))
  (add-to-list 'org-src-lang-modes '("ruby" . ruby-ts))
  (add-to-list 'org-src-lang-modes '("dot" . graphviz-dot))
   ;; `org-babel-do-load-languages' significantly slows loading time,
   ;; so let's run this well after we've loaded
  (run-at-time "1 min" nil (lambda ()
                             (org-babel-do-load-languages 'org-babel-load-languages
                                                          '((shell . t)
                                                            (dot . t)
                                                            (js . t)
                                                            (sql . t)
                                                            (python . t)
                                                            (ruby . t))))))

Org Contrib

(use-package org-contrib
  :after org)

ox-md

(use-feature ox-md
  :ensure nil
  :after org)

org-pdftools

Now that we have that installed we can pull in org-pdftools from github

(use-package org-pdftools
  :hook (org-mode . org-pdftools-setup-link))

Ob-Restclient

(use-package ob-restclient
  :config
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((restclient . t))))

Org-toc

After the installation, every time you’ll be saving an org file, the first headline with a :TOC: tag will be updated with the current table of contents.

To add a TOC tag, you can use the command org-set-tags-command (C-c C-q).

In addition to the simple :TOC: tag, you can also use the following tag formats:

You can also use @ as separator, instead of _.

(use-package toc-org
  :hook (org-mode . toc-org-mode))

Org Modern

(use-package org-modern
  :hook (org-mode . org-modern-mode))

Custom Org Functions

These functions expand on the abilities of org-babel and ob-restclient mode and as such need both of these modes loaded before they’ll work.

;; generated-curl-command is used to communicate state across several function calls
(setq generated-curl-command nil)

(defvar org-babel-default-header-args:restclient-curl
  `((:results . "raw"))
  "Default arguments for evaluating a restclient block.")

;; Lambda function reified to a named function, stolen from restclient
(defun gen-restclient-curl-command (method url headers entity)
  (let ((header-args
         (apply 'append
                (mapcar (lambda (header)
                          (list "-H" (format "%s: %s" (car header) (cdr header))))
                        headers))))
    (setq generated-curl-command
          (concat
           "#+BEGIN_SRC sh\n"
           "curl "
           (mapconcat 'shell-quote-argument
                      (append '("-i")
                              header-args
                              (list (concat "-X" method))
                              (list url)
                              (when (> (string-width entity) 0)
                                (list "-d" entity)))
                      " ")
           "\n#+END_SRC"))))

(defun org-babel-execute:restclient-curl (body params)
  "Execute a block of Restclient code to generate a curl command with org-babel.
This function is called by `org-babel-execute-src-block'"
  (message "executing Restclient source code block")
  (with-temp-buffer
    (let ((results-buffer (current-buffer))
          (restclient-same-buffer-response t)
          (restclient-same-buffer-response-name (buffer-name))
          (display-buffer-alist
           (cons
            '("\\*temp\\*" display-buffer-no-window (allow-no-window . t))
            display-buffer-alist)))

      (insert (buffer-name))
      (with-temp-buffer
        (dolist (p params)
          (let ((key (car p))
                (value (cdr p)))
            (when (eql key :var)
              (insert (format ":%s = %s\n" (car value) (cdr value))))))
        (insert body)
        (goto-char (point-min))
        (delete-trailing-whitespace)
        (goto-char (point-min))
        (restclient-http-parse-current-and-do 'gen-restclient-curl-command))
      generated-curl-command)))

;; Make it easy to interactively generate curl commands
(defun jb/gen-curl-command ()
  (interactive)
  (let ((info (org-babel-get-src-block-info)))
    (if (equalp "restclient" (car info))
        (org-babel-execute-src-block t (cons "restclient-curl"
                                             (cdr info)))
        (message "I'm sorry, I can only generate curl commands for a restclient block."))))

Organize your life

This section includes tooling for organizing ones work or personal life. Generally the tools and setup is pretty straight forward but should that not be the case I’ll add more details about the purpose and how to use.

Org Agenda customizations

(use-feature org-agenda
  :config
  (defun air-org-skip-subtree-if-priority (priority)
    "Skip an agenda subtree if it has a priority of PRIORITY.

PRIORITY may be one of the characters ?A, ?B, or ?C."
    (let ((subtree-end (save-excursion (org-end-of-subtree t)))
          (pri-value (* 1000 (- org-lowest-priority priority)))
          (pri-current (org-get-priority (thing-at-point 'line t))))
      (if (= pri-value pri-current)
          subtree-end
        nil)))
  ;; (setq initial-buffer-choice (lambda () (org-agenda nil "d")
  ;;                               (buffer-find "*Org Agenda*")))
  (setq org-agenda-window-setup 'only-window
        org-agenda-custom-commands
        '(("d" "Today"
           ((tags-todo "SCHEDULED<\"<+1d>\"&PRIORITY=\"A\"" ;Priority tasks available to do today
                       ((org-agenda-skip-function
                         '(org-agenda-skip-entry-if 'todo 'done))
                        (org-agenda-overriding-header "High-priority unfinished tasks:")))
            (agenda "" ((org-agenda-span 'day)
                        (org-scheduled-delay-days -14)
                        (org-agenda-overriding-header "Schedule")))
            (tags-todo "SCHEDULED<\"<+1d>\"" ;All tasks available today
                       ((org-agenda-skip-function
                         '(or (org-agenda-skip-entry-if 'done)
                              (air-org-skip-subtree-if-priority ?A)))
                        (org-agenda-overriding-header "Tasks:"))))))))
elegant-agenda-mode
(use-package elegant-agenda-mode
  :hook (org-agenda-mode . elegant-agenda-mode))

org-alert

Have alerts pop up from your org agenda

(use-package org-alert)

DOCT

Declarative Org Capture Templates

(use-package doct
  :after org
  :commands (doct)
  :init (setq org-capture-templates
              (doct `(("Personal" :keys "p" :children
                       (("Todo"   :keys "t"
                         :template ("* TODO %^{Description}"
                                    "SCHEDULED: %U")
                         :headline "Tasks" :file ,(concat org-directory "/personal/tasks.org"))
                        ("Notes"  :keys "n"
                         :template ("* %^{Description}"
                                    ":PROPERTIES:"
                                    ":Created: %U"
                                    ":END:")
                         :headline "Notes" :file ,(concat org-directory "/personal/tasks.org"))
                        ("Appointment"  :keys "a"
                         :template ("* %^{Description}"
                                    "SCHEDULED: %T"
                                    ":PROPERTIES:"
                                    ":calendar-id: [email protected]"
                                    ":END:")
                                    :file ,(concat org-directory "/personal/calendar.org"))
                        ("Emails" :keys "e"
                         :template "* TODO [#A] Reply: %a :@home:"
                         :headline "Emails" :file "~/org/personal/tasks.org")))

                      ("Work"    :keys "w"
                       :children
                       (("Todo"  :keys "t"
                         :template ("* TODO %^{Description}"
                                    ":PROPERTIES:"
                                    ":Scheduled: %U"
                                    ":END:")
                         :headline "Tasks" :file ,(concat org-directory "/work/tasks.org"))
                        ("Notes"  :keys "n"
                         :template ("* %^{Description}"
                                    ":PROPERTIES:"
                                    ":Created: %U"
                                    ":END:")
                         :headline "Notes" :file ,(concat org-directory "/work/tasks.org"))
                        ("Emails" :keys "e"
                         :template "* TODO [#A] Reply: %a :@work:"
                         :headline "Emails" :file ,(concat org-directory "/work/tasks.org"))
                        ("Trello" :keys "r"
                         :template ("* TODO [#B] %a " "SCHEDULED: %U")
                         :headline "Tasks" :file ,(concat org-directory "/work/tasks.org"))
                        ("Appointment"  :keys "a"
                         :template ("* %^{Description}"
                                    "SCHEDULED: %T"
                                    ":PROPERTIES:"
                                    ":calendar-id: [email protected]"
                                    ":END:")
                         :file ,(concat org-directory "/work/calendar.org"))))))))

Org Fancy Priorities

Change priority cookies from alphanumeric cookies into symbols and explicitly colours them

(use-package org-fancy-priorities
  :hook
  (org-mode . org-fancy-priorities-mode)
  :config
  '((?A :foreground "red")
    (?B :foreground "orange")
    (?C :foreground "blue"))
  (setq org-fancy-priorities-list '("" "" "" "")))

Org Roam

(use-package org-roam
  :init
  (setq org-roam-v2-ack t)
  :custom
  (org-roam-directory "~/dev/diary")
  (org-roam-completion-everywhere t)
  :bind (("C-c n l" . org-roam-buffer-toggle)
         ("C-c n f" . org-roam-node-find)
         ("C-c n i" . org-roam-node-insert))
  :config
  (org-roam-setup))

Org Roam UI

(use-package org-roam-ui
    :after org-roam
    :config
    (setq org-roam-ui-sync-theme t
          org-roam-ui-follow t
          org-roam-ui-update-on-save t
          org-roam-ui-open-on-start t))

Org Noter

(use-package org-noter
  :custom (org-noter-supported-mode '(doc-view-mode pdf-mode-view)))

Org Download

(use-package org-download
  :after org
  :hook (org-mode . org-download-enable))

Org Transclusion

(use-package org-transclusion
  :after org)
(use-package org-transclusion-http
  :after org-transclusion)

org auto clock

(use-package org-auto-clock
  :after org
  :commands org-auto-clock-mode
  :ensure (:type git :host github :repo "justinbarclay/org-auto-clock")
  :init
  (org-auto-clock-mode)
  :custom
  (org-clock-idle-time 20)
  (org-auto-clock-projects '("tidal-wave" "application-inventory"))
  (org-auto-clock-project-name-function #'projectile-project-name))

Ltex

lsp

(use-package lsp-ltex)

EShell

(use-feature eshell
  :config
  (progn
    (eval-after-load 'esh-opt
      '(progn
         (require 'em-prompt)
         (require 'em-term)
         (require 'em-cmpl)
         (setenv "PAGER" "cat")
         (add-to-list 'eshell-visual-commands "ssh")
         (add-to-list 'eshell-visual-commands "htop")
         (add-to-list 'eshell-visual-commands "top")
         (add-to-list 'eshell-visual-commands "tail")
         (add-to-list 'eshell-visual-commands "vim")
         (add-to-list 'eshell-visual-commands "npm")

         (add-to-list 'eshell-command-completions-alist
                      '("gunzip" "gz\\'"))
         (add-to-list 'eshell-command-completions-alist
                      '("tar" "\\(\\.tar|\\.tgz\\|\\.tar\\.gz\\)\\'"))))))

Eat

(use-package eat
  :init
  (add-hook 'eshell-load-hook #'eat-eshell-mode)
  :hook (eat-mode . (lambda () (setq display-line-numbers nil))))

Magit

(use-package transient)
;; Magit is an Emacs interface to Git.
;; (It's awesome)
;; https://github.com/magit/magit
(use-package magit
  :commands magit-get-top-dir
  ;; :ensure-system-package git
  :bind (("C-c g" . magit-status))
  :hook
  (git-commit-mode . magit-commit-mode-init)
  :bind (:map magit-mode-map
              ("c" . magit-maybe-commit))
  :init
  (progn
    ;; magit extensions
    ;; stops the invalid style showing up.
    ;; From: http://git.io/rPBE0Q
    (defun magit-commit-mode-init ()
      "Force a new line to be inserted into a commit window"
      (when (looking-at "\n"))
     (open-line 1))
    (defun magit-maybe-commit (&optional show-options)
      "Runs magit-commit unless prefix is passed"
      (interactive "P")
      (if show-options
          (magit-key-mode-popup-committing)
        (magit-commit)))
    ;; make magit status go full-screen but remember previous window
    ;; settings
    ;; from: http://whattheemacsd.com/setup-magit.el-01.html
    (advice-add 'magit-status :around #'(lambda (orig-fun &rest args)
                                          (window-configuration-to-register :m)
                                          (apply orig-fun args)
                                          (delete-other-windows)))

    (advice-add 'git-commit-commit :after #'(lambda (&rest _)
                                              (delete-window)))

    (advice-add 'git-commit-abort :after #'(lambda (&rest _)
                                             (delete-window))))
  :config
  (progn
    ;; restore previously hidden windows
    (advice-add 'magit-quit-window
                :around
                #'(lambda (oldfun)
                    (let ((current-mode major-mode))
                      (funcall oldfun)
                      (when (eq 'magit-status-mode current-mode)
                        (jump-to-register :m)))))
    ;; magit settings
    (setq
     ;; don't put "origin-" in front of new branch names by default
     magit-default-tracking-name-function #'magit-default-tracking-name-branch-only
     ;; open magit status in same window as current buffer
     magit-status-buffer-switch-function #'switch-to-buffer
     ;; highlight word/letter changes in hunk diffs
     magit-diff-refine-hunk t
     ;; ask me if I want to include a revision when rewriting
     magit-rewrite-inclusive 'ask
     ;; ask me to save buffers
     magit-save-some-buffers nil
     ;; pop the process buffer if we're taking a while to complete
     magit-process-popup-time 10
     ;; ask me if I want a tracking upstream
     magit-set-upstream-on-push 'askifnotset)))

Magit forges

(use-package forge
  :after magit
  :init
  (setq gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3"))

Magit TODO

Due to how hl-todo specified versions, we have to do a full clone and specify the version, as mentioned here

(use-package hl-todo
  :ensure (hl-todo :depth nil :version (lambda (&rest _args) "1.9.0")))
(use-package magit-todos
  :hook (magit-mode . magit-todos-mode))

pos-tip

(use-package pos-tip)

Email (mu4e)

(use-feature mu4e
  :commands (mu4e mu4e-update-mail-and-index)
  :bind (:map mu4e-headers-mode-map
              ("q" . kill-current-buffer))
  :after org
  :config
  (setq
   mu4e-headers-skip-duplicates  t
   mu4e-view-show-images t
   mu4e-view-show-addresses t
   mu4e-use-fancy-chars t
   mu4e-compose-format-flowed nil
   mu4e-date-format "%y/%m/%d"
   mu4e-headers-date-format "%Y/%m/%d"
   mu4e-change-filenames-when-moving t
   mu4e-attachments-dir "~/Downloads"
   mu4e-maildir       "~/Maildir/"   ;; top-level Maildir
   ;; note that these folders below must start with /
   ;; the paths are relative to maildir root

   ;; this setting allows to re-sync and re-index mail
   ;; by pressing U
   mu4e-get-mail-command "mbsync -a"

   mu4e-completing-read-function 'completing-read
   mu4e-context-policy 'pick-first
   mu4e-contexts (list
                  (make-mu4e-context
                   :name "fastmail"
                   :match-func
                   (lambda (msg)
                     (when msg
                       (string-prefix-p "/fastmail" (mu4e-message-field msg :maildir))))
                   :vars '((user-mail-address . "[email protected]")
                           (user-full-name    . "Justin Barclay")
                           (mu4e-drafts-folder  . "/fastmail/Drafts")
                           (mu4e-sent-folder  . "/fastmail/Sent")
                           (mu4e-refile-folder  . "/fastmail/Archive")
                           (sendmail-program . "msmtp")
                           (send-mail-function . smtpmail-send-it)
                           (message-sendmail-f-is-evil . t)
                           (message-sendmail-extra-arguments . ("--read-envelope-from"))
                           (message-send-mail-function . message-send-mail-with-sendmail)
                           (smtpmail-default-smtp-server . "smtp.fastmail.com")
                           (smtpmail-smtp-server  . "smtp.fastmail.com")
                           (mu4e-trash-folder  . "/fastmail/Trash")))))

  (display-line-numbers-mode -1))

Mu4e Dashboard

(push 'mu4e elpaca-ignored-dependencies)

(use-package mu4e-dashboard
  :ensure (:type git :host github :repo "rougier/mu4e-dashboard")
  :bind ("C-c d" . mu4e-dashboard)
  :after mu4e
  :hook
  (mu4e-dashboard-mode . (lambda () (display-line-numbers-mode -1)))
  :custom
  (mu4e-dashboard-file "~/.emacs.d/dashboards/mu4e-dashboard.org")
  :config
  (defun mu4e-dashboard-edit ()
    (interactive)
    (let ((edit-buffer "*edit-mu4e-dashboard*"))
      (when (get-buffer edit-buffer)
        (kill-buffer (get-buffer edit-buffer)))
      (make-indirect-buffer (current-buffer) edit-buffer)
      (switch-to-buffer-other-window (get-buffer edit-buffer))
      (org-mode 1)))
  (display-line-numbers-mode -1)
  (flyspell-mode -1))

Mu4e Theading

(use-package mu4e-thread-folding
  :ensure (:type git :host github :repo "rougier/mu4e-thread-folding")
  :hook
  ((mu4e-headers-mode . mu4e-thread-folding-mode)
   (mu4e-headers-mode . (lambda () (display-line-numbers-mode -1))))
  :config
  (add-to-list 'mu4e-header-info-custom
               '(:empty . (:name "Empty"
                                 :shortname ""
                                 :function (lambda (msg) "  "))))
  :custom
  (mu4e-headers-fields '((:empty         .    2)
                         (:human-date    .   12)
                         (:flags         .    6)
                         (:mailing-list  .   10)
                         (:from          .   22)
                         (:subject       .   nil)))
  :bind (:map mu4e-headers-mode-map
              ("<tab>"     . mu4e-headers-toggle-at-point)
              ("<left>"    . mu4e-headers-fold-at-point)
              ("<S-left>"  . mu4e-headers-fold-all)
              ("<right>"   . mu4e-headers-unfold-at-point)
              ("<S-right>" . mu4e-headers-unfold-all)))

Elfeed

(use-package elfeed
 :custom
 (elfeed-feeds
      '(("http://nullprogram.com/feed/" emacs)
        ("https://sachachua.com/blog/feed/" emacs)
        ("https://macowners.club/posts/index.xml" emacs)
        ("https://fasterthanli.me/index.xml" tech rust)
        ("https://justinbarclay.ca/index.xml" mine)
        ("https://blog.1password.com/index.xml" security authentication)
        ("https://www.michaelgeist.ca/blog/feed/" canada law)
        ("https://popehat.substack.com/feed" law)
        ("https://www.joelonsoftware.com/feed/" tech)
        ("https://xeiaso.net/blog.rss" tech nix)
        ("https://byorgey.wordpress.com/feed/" functional-programming)
        ("https://mjg59.dreamwidth.org/" tech)
        ("https://oxide.computer/blog/feed" tech company))))

Look and Feel

Window

Natural colouring from emacs chrome

(when jb/os-macos-p
  (setq default-frame-alist '((ns-appearance . dark) (ns-transparent-titlebar . t) (ns-appearance . 'nil))))

Simplify the UI

Remove the tool bar

(tool-bar-mode -1)

Remove the menu bar

(menu-bar-mode -1)

Remove scroll bars

(when (fboundp 'scroll-bar-mode)
  (scroll-bar-mode -1))

Scrolling

(pixel-scroll-precision-mode 1)

Emacs should not take focus when it launches

(when (display-graphic-p) ; Start full screen
  (add-to-list 'default-frame-alist '(fullscreen . t))
  (x-focus-frame nil))

Formatting window title

(setq-default frame-title-format "%b (%f)")

Precision Framing

Emacs keeps a margin between itself and other windows, but if we enable pixelwise resizing it keeps everything nice and snug

(setq frame-resize-pixelwise 't)

Font

I’m a big fan of the Cascadia font from Microsoft lately. It’s looks pretty good and has great ligature support.

(let ((font-name (if jb/os-windows-p
                     "CaskaydiaCove NFM"
                   "CaskaydiaMono Nerd Font Mono")))
  (set-face-attribute 'default nil
                      :family font-name :height 170 :weight 'normal))

Ligatures

Ok we know our font supports ligatures, let’s set that up.

(use-package ligature
  :defer t
  :config
  ;; Enable the "www" ligature in every possible major mode
  (ligature-set-ligatures 't '("www"))
  ;; Enable traditional ligature support in eww-mode, if the
  ;; `variable-pitch' face supports it
  (ligature-set-ligatures 'eww-mode '("ff" "fi" "ffi"))
  ;; Enable all Cascadia Code ligatures in programming modes
  (ligature-set-ligatures 'prog-mode '("|||>" "<|||" "<==>" "<!--" "####" "~~>" "***" "||=" "||>"
                                       ":::" "::=" "=:=" "===" "==>" "=!=" "=>>" "=<<" "=/=" "!=="
                                       "!!." ">=>" ">>=" ">>>" ">>-" ">->" "->>" "-->" "---" "-<<"
                                       "<~~" "<~>" "<*>" "<||" "<|>" "<$>" "<==" "<=>" "<=<" "<->"
                                       "<--" "<-<" "<<=" "<<-" "<<<" "<+>" "</>" "###" "#_(" "..<"
                                       "..." "+++" "/==" "///" "_|_" "www" "&&" "^=" "~~" "~@" "~="
                                       "~>" "~-" "**" "*>" "*/" "||" "|}" "|]" "|=" "|>" "|-" "{|"
                                       "[|" "]#" "::" ":=" ":>" ":<" "$>" "==" "=>" "!=" "!!" ">:"
                                       ">=" ">>" ">-" "-~" "-|" "->" "--" "-<" "<~" "<*" "<|" "<:"
                                       "<$" "<=" "<>" "<-" "<<" "<+" "</" "#{" "#[" "#:" "#=" "#!"
                                       "##" "#(" "#?" "#_" "%%" ".=" ".-" ".." ".?" "+>" "++" "?:"
                                       "?=" "?." "??" ";;" "/*" "/=" "/>" "//" "__" "~~" "(*" "*)"
                                       "\\\\" "://"))
  ;; Enables ligature checks globally in all buffers. You can also do it
  ;; per mode with `ligature-mode'.
  :init
  (global-ligature-mode t))

Lets improve our mapping for unicode-fonts

(use-package unicode-fonts
   :defer 't
   :config
   (unicode-fonts-setup))

Emojis

We can not place this in early-init because it requires the GUI to be initialized

(cond
 (jb/os-macos-p
  (progn
    (set-fontset-font "fontset-default" 'symbol "Apple Color Emoji" nil 'prepend)
    (set-fontset-font "fontset-default" 'emoji "Apple Color Emoji" nil 'prepend)))
 ((or jb/os-linux-p
      jb/os-windows-p)
  (progn
    (set-fontset-font "fontset-default" 'symbol "Segoe UI Emoji" nil 'prepend)
    (set-fontset-font "fontset-default" 'emoji "Segoe UI Emoji" nil 'prepend)))
 nil)

Misc

No cursor blinking, it’s distracting

(blink-cursor-mode 0)
;; These settings relate to how emacs interacts with your operating system
(setq-default ;; makes killing/yanking interact with the clipboard
 select-enable-clipboard t

 ;; I'm actually not sure what this does but it's recommended?
 select-enable-primary t

 ;; Save clipboard strings into kill ring before replacing them.
 ;; When one selects something in another program to paste it into Emacs,
 ;; but kills something in Emacs before actually pasting it,
 ;; this selection is gone unless this variable is non-nil
 ;;save-interprogram-paste-before-kill nil ;; This is disabled because it crashes emacs.

 ;; Shows all options when running apropos. For more info,
 ;; https://www.gnu.org/software/emacs/manual/html_node/emacs/Apropos.html
 apropos-do-all t

 ;; Mouse yank commands yank at point instead of at click.
 mouse-yank-at-point t)

My name isn’t “Tinker”, so I don’t need a bell.

(setq-default ring-bell-function 'ignore)
;; Changes all yes/no questions to y/n type
(fset 'yes-or-no-p 'y-or-n-p)

;; shell scripts
(setq-default sh-basic-offset 2)
(setq-default sh-indentation 2)

;; No need for ~ files when editing
(setq-default create-lockfiles nil)

;; Go straight to scratch buffer on startup
(setq-default inhibit-startup-message t)

Note: Disabling the BPA makes redisplay faster, but might produce incorrect display reordering of bidirectional text with embedded parentheses

(setq bidi-inhibit-bpa t)

We want to unbind C-l so we can use it later

(keymap-global-unset "C-l")

Performance

Increase the size of read-process-output-max from default of 4k to 1Mb

(setq-default read-process-output-max (* 1024 1024)) ;; 1mb

Line Numbers

As of Emacs 26.0 we have native, perfomant, support for line numbers

(global-display-line-numbers-mode)
(set-default 'display-line-numbers-type 't)
(set-default 'display-line-numbers-current-absolute 't)

Theme

Elegant Emacs

(use-package nano-theme
  :ensure (nano-theme :type git :host github :repo "rougier/nano-theme"))

Lambda Themes

(use-package lambda-themes
  :ensure (:type git :host github :repo "lambda-emacs/lambda-themes")
  :custom
  (lambda-themes-set-italic-comments nil)
  (lambda-themes-set-italic-keywords nil)
  (lambda-themes-set-variable-pitch nil)
  :config
  ;; load preferred theme
  (load-theme 'lambda-light))

Catpuccin

(use-package catppuccin-theme)

Add on doom theme

(use-package doom-everblush-theme
  :ensure (doom-everblush-theme :type git :host github :repo "Everblush/doomemacs"))

Doom Themes

Let’s use Doom’s version instead

(use-package doom-themes
  :ensure (:type git :host github :repo "justinbarclay/themes" :ref "laserwave-hc")
  :init
  (load-theme 'doom-laserwave-high-contrast t)
  (setq doom-themes-enable-bold t    ; if nil, bold is universally disabled
        doom-themes-enable-italic t) ; if nil, italics is universally disabled
  ;; Corrects (and improves) org-mode's native fontification.
  (doom-themes-org-config))

Icons

Nerd Icons

(use-package nerd-icons)

Completion

(use-package nerd-icons-completion
  :after (nerd-icons marginalia)
  :hook (marginalia-mode . nerd-icons-completion-marginalia-setup))

iBuffer

(use-package nerd-icons-ibuffer
  :hook (ibuffer-mode . nerd-icons-ibuffer-mode))

Modeline

Doom-modeline

(use-package doom-modeline
  :hook
  (elpaca-after-init . doom-modeline-mode)
  :custom
  (doom-modeline-buffer-file-name-style 'relative-to-project))

lambda-modeline

(use-package lambda-line
  :ensure (:type git :host github :repo "lambda-emacs/lambda-line")
  :custom
  (lambda-line-position 'top) ;; Set position of status-line
  (lambda-line-abbrev t) ;; abbreviate major modes
  (lambda-line-hspace "  ")  ;; add some cushion
  (lambda-line-prefix t) ;; use a prefix symbol
  (lambda-line-prefix-padding nil) ;; no extra space for prefix
  (lambda-line-status-invert t)  ;; no invert colors
  (lambda-line-git-diff-mode-line nil)
  (lambda-line-gui-ro-symbol  "") ;; symbols
  (lambda-line-gui-mod-symbol "")
  (lambda-line-gui-rw-symbol  "")
  (lambda-line-space-top +.25)  ;; padding on top and bottom of line
  (lambda-line-space-bottom -.25)
  (lambda-line-symbol-position 0.1) ;; adjust the vertical placement of symbol
  (lambda-line-syntax t)
  :hook (elpaca-after-init . lambda-line-mode)
  :config
  (require 'nerd-icons)
  (setq lambda-line-flycheck-label (format " %s" (nerd-icons-mdicon "nf-md-alarm_light")))
  (setq lambda-line-vc-symbol (format " %s" (nerd-icons-mdicon "nf-md-git")))
  ;; activate lambda-line
  ;; set divider line in footer
  (when (eq lambda-line-position 'top)
    (setq-default mode-line-format (list "%_"))
    (setq mode-line-format (list "%_"))))

Rainbow delimiters

(use-package rainbow-delimiters
  :hook (prog-mode . rainbow-delimiters-mode)
  :config
   (custom-set-faces
    '(rainbow-delimiters-depth-0-face ((t (:foreground "saddle brown"))))
    '(rainbow-delimiters-depth-1-face ((t (:foreground "dark orange"))))
    '(rainbow-delimiters-depth-2-face ((t (:foreground "deep pink"))))
    '(rainbow-delimiters-depth-3-face ((t (:foreground "chartreuse"))))
    '(rainbow-delimiters-depth-4-face ((t (:foreground "deep sky blue"))))
    '(rainbow-delimiters-depth-5-face ((t (:foreground "yellow"))))
    '(rainbow-delimiters-depth-6-face ((t (:foreground "orchid"))))
    '(rainbow-delimiters-depth-7-face ((t (:foreground "spring green"))))
    '(rainbow-delimiters-depth-8-face ((t (:foreground "sienna1"))))
    '(rainbow-delimiters-unmatched-face ((t (:foreground "black"))))))

undo

Vundo

Sometimes you have to quickly jump through you undo-history

(use-package vundo
  :custom
  (vundo-glyph-alist vundo-unicode-symbols))

undo-fu

(use-package undo-fu)

undo-fu-sessions

And sometimes you want that undo-history to persist over a couple of days

(use-package undo-fu-session
  :init
  (undo-fu-session-global-mode)
  :custom
  (undo-fu-session-file-limit 10))

ibuffer

ibuffer

Keybindings We’re prettying up ibuffer after

This code is liberally stolen from https://github.com/seagle0128/.emacs.d/blob/master/lisp/init-ibuffer.el (April 12, 2019)

(use-feature ibuffer
  :commands (ibuffer-current-buffer
             ibuffer-find-file
             ibuffer-do-sort-by-alphabetic)
  :bind ("C-x C-b" . ibuffer)
  :init
  (setq ibuffer-filter-group-name-face '(:inherit (font-lock-string-face bold)))
  (setq ibuffer-formats '((mark modified read-only locked
                                " " (icon 2 2 :left :elide) (name 18 18 :left :elide)
                                " " (size 9 -1 :right)
                                " " (mode 16 16 :left :elide) " " filename-and-process)
                          (mark " " (name 16 -1) " " filename)))
  :config
  (with-eval-after-load 'consult
    (defalias 'ibuffer-find-file 'consult-find-file)))

ibuffer-projectile

(use-package ibuffer-projectile
  :init
  (add-hook 'ibuffer-hook
            (lambda ()
              (ibuffer-projectile-set-filter-groups)
              (unless (eq ibuffer-sorting-mode 'alphabetic)
                (ibuffer-do-sort-by-alphabetic))))
  :config
  (setq ibuffer-projectile-prefix (concat
                                   (nerd-icons-octicon "nf-oct-file_directory"
                                                       :face ibuffer-filter-group-name-face
                                                       :v-adjust 0.1
                                                       :height 1.0)
                                   " ")))

Directory Management

dired

(use-feature dired
  :bind (:map dired-mode-map
              ("RET" . dired-find-alternate-file)
              ("a" . dired-find-file)))

Dirvish

(use-package dirvish
  :custom
  ;; Go back home? Just press `bh'
  (dirvish-bookmark-entries
   '(("h" "~/" "Home")
     ("m" "~/dev/tidal/application-inventory/" "MMP")
     ("t" "~/dev/tidal/tidal-wave" "Tidal Wave")))
  (dirvish-header-line-format '(:left (path) :right (free-space)))
  (dirvish-mode-line-format ; it's ok to place string inside
   '(:left (sort file-time " " file-size symlink) :right (omit yank index)))
  ;; Don't worry, Dirvish is still performant even you enable all these attributes
  (dirvish-attributes '(nerd-icons file-size collapse subtree-state vc-state git-msg))
  ;; Maybe the icons are too big to your eyes
  (dirvish-nerd-icons-height 0.8)
  ;; In case you want the details at startup like `dired'
  ;; (dirvish-hide-details nil)
  :config
  (dirvish-peek-mode)
  (dirvish-override-dired-mode)
  ;; Dired options are respected except a few exceptions, see *In relation to Dired* section above
  (setq dired-dwim-target t)
  (setq delete-by-moving-to-trash t)
  ;; Enable mouse drag-and-drop files to other applications
  (setq dired-mouse-drag-files t)                   ; added in Emacs 29
  (setq mouse-drag-and-drop-region-cross-program t) ; added in Emacs 29
  ;; Make sure to use the long name of flags when exists
  ;; eg. use "--almost-all" instead of "-A"
  ;; Otherwise some commands won't work properly
  (setq dired-listing-switches
        "-l --almost-all --human-readable --time-style=long-iso --group-directories-first --no-group")
  :bind
  ;; Bind `dirvish|dirvish-side|dirvish-dwim' as you see fit
  (("C-c f" . dirvish)
   ;; Dirvish has all the keybindings (except `dired-summary') in `dired-mode-map' already
   :map dirvish-mode-map
   ("a"   . dirvish-quick-access)
   ("f"   . dirvish-file-info-menu)
   ("y"   . dirvish-yank-menu)
   ("N"   . dirvish-narrow)
   ("^"   . dired-up-directory)
   ("h"   . dirvish-history-jump) ; remapped `describe-mode'
   ("s"   . dirvish-quicksort)    ; remapped `dired-sort-toggle-or-edit'
   ("TAB" . dirvish-subtree-toggle)
   ("M-n" . dirvish-history-go-forward)
   ("M-p" . dirvish-history-go-backward)
   ("M-l" . dirvish-ls-switches-menu)
   ("M-m" . dirvish-mark-menu)
   ("M-f" . dirvish-toggle-fullscreen)
   ("M-s" . dirvish-setup-menu)
   ("M-e" . dirvish-emerge-menu)
   ("M-j" . dirvish-fd-jump)))

OS Specific

MacOS

In OS X, when Emacs is started from the GUI it inherits a default set of environment variables. Let’s fix that. Currently turned off due to debugging issues

(use-package exec-path-from-shell
  :if jb/os-macos-p
  :defer 1
  :init
  (exec-path-from-shell-initialize)
  :custom
  (exec-path-from-shell-arguments '("-l")))

Windows

(when jb/os-windows-p
  (setq package-check-signature nil)
  (require 'gnutls)
  (add-to-list 'gnutls-trustfiles (expand-file-name "~/.cert/cacert.pm"))
  (add-hook 'comint-output-filter-functions 'comint-strip-ctrl-m))

Linux && Darwin

(when (not jb/os-windows-p)
  (use-package envrc
    :defer 2
    :config
    (envrc-global-mode)))

Navigating

Uniquify

Ensure that buffers have unique file names

(use-feature uniquify
  :config
  (setq uniquify-buffer-name-style 'forward))

Recentf

Turn on recent file mode so that you can more easily switch to recently edited files when you first start emacs

(use-feature recentf
  :init
  (recentf-mode)
  :custom ((recentf-save-file (concat user-emacs-directory ".recentf"))
           (recentf-max-menu-items 40)))

Projectile

(use-package projectile
  :defer 1
  :bind (("C-s p" . projectile-ripgrep))
  :commands
  (projectile-find-file projectile-switch-project projectile-ripgrep)
  :config
  (projectile-global-mode)
  (setq projectile-completion-system 'auto)
  (setq projectile-enable-caching t)
  (add-to-list 'projectile-globally-ignored-directories "~")

  (setq projectile-switch-project-action #'magit-status)

  (define-key projectile-mode-map (kbd "C-c p") '("projectile" . projectile-command-map))

  (defvar projectile-other-window-map
    (let ((map (make-sparse-keymap)))
      (define-key map (kbd "a") '("find-other-file-other-window" . projectile-find-other-file-other-window))
      (define-key map (kbd "b") '("switch-to-buffer-other-window" . projectile-switch-to-buffer-other-window))
      (define-key map (kbd "C-o") '("display-buffer" . projectile-display-buffer))
      (define-key map (kbd "d") '("find-dir-other-window" . projectile-find-dir-other-window))
      (define-key map (kbd "D") '("dired-other-window" . projectile-dired-other-window))
      (define-key map (kbd "f") '("find-file-other-window" . projectile-find-file-other-window))
      (define-key map (kbd "g") '("find-file-dwim-other-window" . projectile-find-file-dwim-other-window))
      (define-key map (kbd "t") '("find-implementation-or-test-other-window" . projectile-find-implementation-or-test-other-window))
      map))

  (defvar projectile-other-frame-map
    (let ((map (make-sparse-keymap)))
      (define-key map (kbd "a") '("find-other-file-other-frame" . projectile-find-other-file-other-frame))
      (define-key map (kbd "b") '("switch-to-buffer-other-frame" . projectile-switch-to-buffer-other-frame))
      (define-key map (kbd "d") '("find-dir-other-frame" . projectile-find-dir-other-frame))
      (define-key map (kbd "D") '("dired-other-frame" . projectile-dired-other-frame))
      (define-key map (kbd "f") '("find-file-other-frame" . projectile-find-file-other-frame))
      (define-key map (kbd "g") '("find-file-dwim-other-frame" . projectile-find-file-dwim-other-frame))
      (define-key map (kbd "t") '("find-implementation-or-test-other-frame" . projectile-find-implementation-or-test-other-frame))
      map))

  (defvar projectile-search-map
    (let ((map (make-sparse-keymap)))
      (define-key map (kbd "g") '("grep" . projectile-grep))
      (define-key map (kbd "r") '("ripgrep" . projectile-ripgrep))
      (define-key map (kbd "s") '("ag" . projectile-ag))
      (define-key map (kbd "x") '("find-references" . projectile-find-references))
      map))

  (which-key-add-keymap-based-replacements projectile-command-map
    "4" (cons "other-window" projectile-other-window-map)
    "5" (cons "other-frame" projectile-other-frame-map)
    "s" (cons "search" projectile-search-map)))

Treemacs

(use-package treemacs
  :config
  (progn
    (setq treemacs-follow-after-init          t
          treemacs-width                      35
          treemacs-indentation                2
          treemacs-git-integration            t
          treemacs-collapse-dirs              3
          treemacs-silent-refresh             nil
          treemacs-change-root-without-asking nil
          treemacs-sorting                    'alphabetic-desc
          treemacs-show-hidden-files          t
          treemacs-never-persist              nil
          treemacs-is-never-other-window      nil
          treemacs-goto-tag-strategy          'prefetch-index)
    (treemacs-follow-mode t)
    (treemacs-filewatch-mode t)
    (setq treemacs-icons-hash (make-hash-table :size 200 :test #'equal)
          treemacs-icon-fallback (concat
                                  "  "
                                  (nerd-icons-faicon "nf-fa-file_o"
                                                     :face 'nerd-icons-dsilver
                                                     :height 0.9
                                                     :v-adjust -0.05)
                                  " ")
          treemacs-icon-text treemacs-icon-fallback)
    (dolist (item nerd-icons/octicon-alist)
      (let* ((extension (car item))
             (func (cadr item))
             (args (append (list (caddr item))
                           '(:height 0.9 :v-adjust -0.05)
                           (cdddr item)))
             (icon (apply func args))
             (key (s-replace-all '(("^" . "") ("\\" . "") ("$" . "") ("." . "")) extension))
             (value (concat "  " icon " ")))
        (ht-set! treemacs-icons-hash (s-replace-regexp "\\?" "" key) value)
        (ht-set! treemacs-icons-hash (s-replace-regexp ".\\?" "" key) value))))
  :bind
  (:map global-map
        ([f8]        . treemacs-toggle)
        ("M-0"       . treemacs-select-window)))

Treemacs Pojectile

(use-package treemacs-projectile
  :config
  (setq treemacs-header-function #'treemacs-projectile-create-header))

ace-window

Allows one to switch to a buffer based by using numbers, instead of cycling with C-x o. ace-window only kicks in if there are more than two buffers open.

(use-package ace-window
  :bind ("C-x o" . ace-window))

wind-move

(use-feature windmove-mode
  :ensure nil
  :commands (windmove-left windmove-right windmove-up windmove-down)
  :init
  (defvar-keymap windmove-custom-mode-map
    :repeat (:exit (ignore))
    "<down>" #'windmove-down
    "<up>" #'windmove-up
    "<left>" #'windmove-left
    "<right>" #'windmove-right)
  (set-keymap-parent windmove-custom-mode-map window-prefix-map)
  (keymap-global-set "C-x w" windmove-custom-mode-map))

Repeat-Mode

(use-feature repeat-mode
  :init (repeat-mode))

Searching

Unbind C-s from isearch and make it the universal search command

(unbind-key "C-s")

With Ripgrep

(use-package rg
  :bind (("C-s r" . rg)))

With Isearch and Avy

(use-package avy
  :bind (("C-s a" . #'avy-goto-char-timer))
  :custom
  (avy-enter-times-out 't)
  (avy-timeout-seconds 1))
(use-feature isearch
  :bind (("C-s i" . isearch-forward-regexp)
         :map isearch-mode-map
              ("M-j" . avy-isearch)))

Occur

(use-feature occur
  :bind ("C-s o" . occur))

Multi Occur

(use-feature multi-occur
  :init
  (defun get-buffers-matching-mode (mode)
    "Returns a list of buffers where their major-mode is equal to MODE"
    (let ((buffer-mode-matches '()))
      (dolist (buf (buffer-list))
        (with-current-buffer buf
          (when (eq mode major-mode)
            (push buf buffer-mode-matches))))
      buffer-mode-matches))
  (defun multi-occur-in-this-mode ()
    (interactive)
    (multi-occur (get-buffers-matching-mode major-mode)
                 (car (occur-read-primary-args))))
  :bind ("C-s m" . multi-occur-in-this-mode))

Editing

General config to make editing text feel nice

(use-feature emacs
  :config

Buffer Encoding

(prefer-coding-system 'utf-8)
(set-default-coding-systems 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)

Newline and indent

(define-key global-map (kbd "RET") 'newline-and-indent)

Truncate lines

(setq-default truncate-lines t)

Highlight current line

(global-hl-line-mode 1)

Highlight matching parens

(show-paren-mode 1)

No tabs

I prefer spaces like some sort of monster

(setq-default indent-tabs-mode nil)

Set base indentation

It’s all about space efficiency

(setq-default tab-width 2)

(setq-default c-basic-offset 2)

Save place in file

Remember where point was when I come back to a file

(save-place-mode 1)
;; keep track of saved places in ~/.emacs.d/places
(setq save-place-file (concat user-emacs-directory "places"))

Comment or Uncomment region

(global-set-key (kbd "C-;") 'comment-or-uncomment-region)

Backups

Emacs can automatically create backup files. This tells Emacs to put all backups in ~/.emacs.d/backups. More info.

(setq backup-directory-alist `(("." . ,(concat user-emacs-directory
                                               "backups"))))
(setq auto-save-default nil)
(setq backup-by-copying t)

Disable Superfluous warnings

(setq-default warning-suppress-log-types '((copilot copilot-no-mode-indent)))

Mark Manipulation

Moving around with the mark should be really simple. It’s such a super power that I want to use it more and to do that, I need to bind it to something easier, something already reserved for popping the mark. So we can upgrade xref-go-back to be a DWIM function.

Here is an example of and another examp I guess it is corfu being slow due to too many candidates?

(unbind-key "M-,")

(defun pop-mark-dwim ()
  "If xref history exist, use that to move around and if not pop off the global mark stack."
  (interactive)
  (condition-case nil
      (xref-go-back)
    (user-error
     (pop-global-mark))))

(bind-key "M-," #'pop-mark-dwim)

end emacs specific config

)

Parens Balancing

(use-package smartparens
  :hook (prog-mode . smartparens-mode)
  :bind (:map smartparens-mode-map ("M-<backspace>" . 'backward-kill-word)))

Config

(use-feature smartparens-config
  :after smartparens)

Whitespace

Emacs doesn’t handle trailing spaces or anything like that very well by default, it’s far too aggressive for my tastes, so we’ll use ws-butler to fix this.

(use-package ws-butler
  :commands (ws-butler-mode)
  :hook (prog-mode . ws-butler-mode))

Code Folding

Emacs by default doesn’t have a good story for folding text so we have to add one.

(use-package origami
  :defer t
  :bind ("C-<tab>" . origami-recursively-toggle-node)
  :hook (prog-mode . origami-mode))

Hungry Delete

(use-package hungry-delete
  :hook (prog-mode . global-hungry-delete-mode))

Multiple Cursors

Thank you Magnar Sveen! I’ve put this at the top, because I use this almost everyday and wish it existed in more places.

(use-package multiple-cursors
  :bind
  (("C->" . mc/mark-next-like-this)
   ("C-<" . mc/mark-previous-like-this))
  :commands (mc/mark-next-like-this mc/mark-previous-like-this))

Syntax and Error checking

Flycheck posframe

Load this before we load Flycheck

(use-package flycheck-posframe
  :hook ((flycheck-mode . flycheck-posframe-mode)
         (lsp-mode . (lambda () (flycheck-posframe-mode 0)))
         (post-command . flycheck-posframe-monitor-post-command))
  :custom
  (flycheck-posframe-warning-prefix "")
  (flycheck-posframe-error-prefix "")
  (flycheck-posframe-info-prefix "")
  :config
  (defun flycheck-posframe-monitor-post-command ()
    (when (not (flycheck-posframe-check-position))
      (posframe-hide flycheck-posframe-buffer)))
  (set-face-attribute 'flycheck-posframe-info-face nil :inherit 'font-lock-variable-name-face)
  (set-face-attribute 'flycheck-posframe-warning-face nil :inherit 'warning)
  (set-face-attribute 'flycheck-posframe-error-face nil :inherit 'error))
  

Flycheck package

(use-package flycheck-package
  :init
  (use-package package-lint))

Flycheck

(use-package flycheck
  :init
  (defun flycheck-node-modules-executable-find (executable)
    (or
     (let* ((base (locate-dominating-file buffer-file-name "node_modules"))
            (cmd  (if base (expand-file-name (concat "node_modules/.bin/" executable)  base))))
       (if (and cmd (file-exists-p cmd))
           cmd))
     (flycheck-default-executable-find executable)))

  (defun flycheck-node-modules-hook ()
    "Look inside node modules for the specified checker"
    (setq-local flycheck-executable-find #'flycheck-node-modules-executable-find))

  (global-flycheck-mode)
  :hook
  ((typescript-ts-base-mode
    js-base-mode
    web-mode
    css-ts-mode
    less-css-mode) .  #'flycheck-node-modules-hook)
  :custom
  (checkdoc-force-docstrings-flag nil)
  ;; (flycheck-javascript-eslint-executable "eslint_d")
  ;; (flycheck-typescript-tslint-executable "eslint_d")
  (flycheck-check-syntax-automatically '(save idle-change idle-buffer-switch mode-enabled))
  (flycheck-standard-error-navigation nil)
  (flycheck-stylelintrc ".stylelintrc.json"))

Flyspell

(use-feature flyspell
  :hook ((prog-mode . flyspell-prog-mode)
         (text-mode . flyspell-mode))
  :config (setq flyspell-issue-message-flag nil))

Integrated Debugging

PGMacs

(use-package pgmacs
  :ensure (:type git :host github :repo "emarsden/pgmacs"))

Dape

(use-package dape
  :ensure (:fetcher git
  :url "https://github.com/svaante/dape"))

Completion

Minibuffer

Vertico

(use-package vertico
  :init
  (vertico-mode)
  :bind (:map vertico-map
              ("<escape>" . #'keyboard-escape-quit))
  :config
  (vertico-multiform-mode)

  ;; Custom candidate transforms
  (defun +completion-category-highlight-files (cand)
    (let ((len (length cand)))
      (when (and (> len 0)
                 (eq (aref cand (1- len)) ?/))
        (add-face-text-property 0 len 'dired-directory 'append cand)))
    cand)

  (defun +completion-category-highlight-commands (cand)
    (let ((len (length cand)))
      (when (and (> len 0)
                 (with-current-buffer (nth 1 (buffer-list)) ; get buffer before minibuffer
                   (or (eq major-mode (intern cand)) ; check major mode
                       (seq-contains-p local-minor-modes (intern cand))
                       (seq-contains-p global-minor-modes (intern cand))))) ; check minor modes
        (add-face-text-property 0 len '(:foreground "red") 'append cand))) ; choose any color or face you like
    cand)

  (defun +completion-category-truncate-files (cand)
    (if-let ((type (get-text-property 0 'multi-category cand))
             ((eq (car-safe type) 'file))
             (response (ivy-rich-switch-buffer-shorten-path cand 30)))
        response
      cand))

  ;; Custom sorters
  (defun sort-directories-first (files)
    (setq files (vertico-sort-history-length-alpha files))
    (nconc (seq-filter (lambda (x) (string-suffix-p "/" x)) files)
           (seq-remove (lambda (x) (string-suffix-p "/" x)) files)))

  ;; Extend vertico-multiform abilities
  (defvar +vertico-transform-functions nil)
  (defun +vertico-transform (args)
    (dolist (fun (ensure-list +vertico-transform-functions) args)
      (setcar args (funcall fun (car args)))))
  (advice-add #'vertico--format-candidate :filter-args #'+vertico-transform)

  (setq vertico-multiform-commands
        '((describe-symbol (vertico-sort-function . vertico-sort-alpha))))

  (setq vertico-multiform-categories
        '((symbol (vertico-sort-function . vertico-sort-alpha))
          (command (+vertico-transform-functions . +completion-category-highlight-commands))
          (file (vertico-sort-function . sort-directories-first)
                (+vertico-transform-functions . +completion-category-highlight-files))
          (multi-category (+vertico-transform-functions . +completion-category-truncate-files)))))
savehist

Save minibuffer history for better integration with orderless

(use-feature savehist
  :init
  (savehist-mode))
Marginalia

Marginalia looks and acts great, however as an old grey(ing) beard, I got used to some of the aesthetics of `ivy-rich` so I would like to bring some of these back.

(defun ivy-rich-switch-buffer-user-buffer-p (buffer)
  "Check whether BUFFER-NAME is a user buffer."
  (let ((buffer-name
         (if (stringp buffer)
             buffer
           (buffer-name buffer))))
    (not (string-match "^\\*" buffer-name))))

(defun ivy-rich--local-values (buffer args)
  (let ((buffer (get-buffer buffer)))
    (if (listp args)
        (mapcar #'(lambda (x) (buffer-local-value x buffer)) args)
      (buffer-local-value args buffer))))

(defun ivy-rich-switch-buffer-indicators (candidate)
  (let* ((buffer (get-buffer candidate))
         (process-p (get-buffer-process buffer)))
    (cl-destructuring-bind
        (filename directory read-only)
        (ivy-rich--local-values candidate '(buffer-file-name default-directory buffer-read-only))
      (let ((modified (if (and (buffer-modified-p buffer)
                               (null process-p)
                               (ivy-rich-switch-buffer-user-buffer-p candidate))
                          "*"
                        ""))
            (readonly (if (and read-only (ivy-rich-switch-buffer-user-buffer-p candidate))
                          "!"
                        ""))
            (process (if process-p
                         "&"
                       ""))
            (remote (if (file-remote-p (or filename directory))
                        "@"
                      "")))
        (format "%s%s%s%s" remote readonly modified process)))))

(defun ivy-rich-switch-buffer-shorten-path (file len)
  "Shorten the path of FILE until the length of FILE <= LEN.
  For example, a path /a/b/c/d/e/f.el will be shortened to
     /a/…/c/d/e/f.el
  or /a/…/d/e/f.el
  or /a/…/e/f.el
  or /a/…/f.el."
  (if (> (length file) len)
      (let ((new-file (replace-regexp-in-string "/?.+?/\\(\\(…/\\)?.+?\\)/.*" "" file nil nil 1)))
        (if (string= new-file file)
            file
          (ivy-rich-switch-buffer-shorten-path new-file len)))
    file))

(defun +marginalia-buffer-get-directory-name (cand)
  (let ((name (buffer-file-name cand)))
    (if name
        (file-name-directory name)
      (buffer-local-value 'list-buffers-directory cand))))

(defun +marginalia-display-project-name (cand)
  (if-let ((dir (+marginalia-buffer-get-directory-name cand))
           (message dir))
      (projectile-project-name
       (projectile-project-root dir))
    "-"))

(defun +marginalia-category-truncate-files (cand)
  (if-let ((type (get-text-property 0 'multi-category cand))
           ((eq (car-safe type) 'file)))
      (ivy-rich-switch-buffer-shorten-path cand 30)
    cand))

(defun +marginalia-truncate-helper (cand)
  (if-let ((func (alist-get (vertico--metadata-get 'category)
                            +marginalia-truncation-func-overrides))
               (shortened-candidate (funcall func cand)))
      shortened-candidate
    cand))
(use-package marginalia
  :config
  (setq marginalia-max-relative-age 0)
  (setq marginalia-align 'left)
  (defvar +marginalia-truncation-func-overrides
    `((file . ,#'+marginalia-category-truncate-files)
      (multi-category . ,#'+marginalia-category-truncate-files))
    "Alist mapping category to truncate functions.")

  (defun marginalia--align (cands)
  "Align annotations of CANDS according to `marginalia-align'."
  (cl-loop for (cand . ann) in cands do
           (when-let (align (text-property-any 0 (length ann) 'marginalia--align t ann))
             (setq marginalia--cand-width-max
                   (max marginalia--cand-width-max
                        (+ (string-width (+marginalia-truncate-helper cand))
                           (compat-call string-width ann 0 align))))))
  (setq marginalia--cand-width-max (* (ceiling marginalia--cand-width-max
                                               marginalia--cand-width-step)
                                      marginalia--cand-width-step))
  (cl-loop for (cand . ann) in cands collect
           (progn
             (when-let (align (text-property-any 0 (length ann) 'marginalia--align t ann))
               (put-text-property
                align (1+ align) 'display
                `(space :align-to
                        ,(pcase-exhaustive marginalia-align
                           ('center `(+ center ,marginalia-align-offset))
                           ('left `(+ left ,(+ marginalia-align-offset marginalia--cand-width-max 2)))
                           ('right `(+ right ,(+ marginalia-align-offset 1
                                                 (- (compat-call string-width ann 0 align)
                                                    (string-width ann)))))))
                ann))
             (list (+marginalia-truncate-helper cand) "" ann))))

  (defun marginalia-annotate-buffer (cand)
    "Annotate buffer CAND with modification status, file name and major mode."
    (when-let (buffer (get-buffer cand))
      (marginalia--fields
       ((file-size-human-readable (buffer-size buffer)) :face 'marginalia-number :width -10)
       ((ivy-rich-switch-buffer-indicators buffer) :face 'error :width 3)
       ((+marginalia-display-project-name buffer) :face 'success :width 15)
       ((ivy-rich-switch-buffer-shorten-path
         (+marginalia-buffer-get-directory-name
          buffer)
         30)
        :face 'marginalia-file-name))))
  :bind
  (("M-A" . marginalia-cycle))
  :init
  (marginalia-mode))
Orderless
(use-package orderless
  :config
  (defun prot-orderless-literal-dispatcher (pattern _index _total)
    "Literal style dispatcher using the equals sign as a suffix.
It matches PATTERN _INDEX and _TOTAL according to how Orderless
parses its input."
    (when (string-suffix-p "=" pattern)
      `(orderless-literal . ,(substring pattern 0 -1))))
  :custom

  (completion-styles '(orderless basic))      ; Use orderless
  (completion-category-overrides
   '((file (styles basic ; For `tramp' hostname completion with `vertico'
                   partial-completion
                   orderless))))
  (orderless-component-separator 'orderless-escapable-split-on-space)

  (orderless-matching-styles
   '(orderless-literal
     orderless-prefixes
     orderless-initialism
     orderless-regexp))

  (orderless-style-dispatchers '(prot-orderless-literal-dispatcher)))
Consult
(use-package consult
  ;; Replace bindings. Lazily loaded due by `use-package'.
  :bind ;; C-c bindings (mode-specific-map)
  (("C-s b" . consult-line)
   ;; C-x bindings (ctl-x-map)
   ("C-x b" . consult-buffer)                ;; orig. switch-to-buffer
   ("C-x r b" . consult-bookmark)            ;; orig. bookmark-jump
   ;; Custom M-# bindings for fast register access
   ("M-#" . consult-register-load)
   ("M-'" . consult-register-store)          ;; orig. abbrev-prefix-mark (unrelated)
   ("C-M-#" . consult-register)
   ;; Other custom bindings
   ("M-y" . consult-yank-pop)                ;; orig. yank-pop
   ("<help> a" . consult-apropos)            ;; orig. apropos-command
   ;; M-g bindings (goto-map)
   ("M-g g" . consult-goto-line)             ;; orig. goto-line
   ("M-g M-g" . consult-goto-line)           ;; orig. goto-line
   ("M-g o" . consult-outline)               ;; Alternative: consult-org-heading
   ;; M-s bindings (search-map)
   ("M-s d" . consult-find)
   ("M-s D" . consult-locate)
   ("M-s r" . consult-ripgrep)

   ("M-s u" . consult-focus-lines))

  ;; Enable automatic preview at point in the *Completions* buffer. This is
  ;; relevant when you use the default completion UI.
  :hook (completion-list-mode . consult-preview-at-point-mode)

  :init
  ;; Use Consult to select xref locations with preview
  (setq xref-show-xrefs-function #'consult-xref
        xref-show-definitions-function #'consult-xref)

  ;; Configure other variables and modes in the :config section,
  ;; after lazily loading the package.
  :config

  ;; Optionally configure preview. The default value
  ;; is 'any, such that any key triggers the preview.
  ;; For some commands and buffer sources it is useful to configure the
  ;; :preview-key on a per-command basis using the `consult-customize' macro.
  (consult-customize
   consult-theme :preview-key '(:debounce 0.2 any)
   consult-ripgrep consult-git-grep consult-grep
   consult-bookmark consult-recent-file consult-xref
   consult--source-bookmark consult--source-file-register
   consult--source-recent-file consult--source-project-recent-file
   ;; :preview-key "M-."
   :preview-key '(:debounce 0.4 any))
  (autoload 'projectile-project-root "projectile")
  (setq consult-project-function (lambda (_) (projectile-project-root))))
Embark
(use-package embark
  :bind
  (("C-." . embark-act)         ;; pick some comfortable binding
   ("C-;" . embark-dwim)        ;; good alternative: M-.
   ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings'

  :init
  ;; Optionally replace the key help with a completing-read interface
  (setq prefix-help-command #'embark-prefix-help-command)
  :config
  ;; Hide the mode line of the Embark live/completions buffers
  (add-to-list 'display-buffer-alist
               '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
                 nil
                 (window-parameters (mode-line-format . none)))))

Consult users will also want the embark-consult package.

(use-package embark-consult
  :after (embark consult)
  :demand t ; only necessary if you have the hook below
  ;; if you want to have consult previews as you move around an
  ;; auto-updating embark collect buffer
  :hook
  (embark-collect-mode . consult-preview-at-point-mode))
Consult Omni
(use-package consult-omni
  :ensure (:type git :host github :repo "armindarvish/consult-omni" :branch "main" :files (:defaults "sources/*.el"))
  :commands (consult-omni-multi consult-omni-apps)
  :custom
  ;; General settings that apply to all sources
  (consult-omni-show-preview t) ;;; show previews
  (consult-omni-preview-key "C-o") ;;; set the preview key to C-o
  (consult-omni-group-by :source)
  :config
  ;; Load Sources Core code
  (require 'consult-omni-sources)
  ;; Load Embark Actions
  (require 'consult-omni-embark)
  (when jb/os-macos-p
    (add-to-list 'consult-omni-apps-paths "/Applications/Nix Apps"))
  
  ;; Either load all source modules or a selected list

   ;;; Select a list of modules you want to aload, otherwise all sources all laoded
  (setq consult-omni-sources-modules-to-load
        (list 'consult-omni-apps
              'consult-omni-buffer
               'consult-omni-calc
               'consult-omni-dict
               'consult-omni-fd
               'consult-omni-google
               'consult-omni-google-autosuggest
               'consult-omni-gptel
               'consult-omni-line-multi
               ;;'consult-omni-org-agenda
               'consult-omni-ripgrep
               'consult-omni-ripgrep-all
               'consult-omni-wikipedia
               'consult-omni-youtube))

  (consult-omni-sources-load-modules)
   ;;; set multiple sources for consult-omni-multi command. Change these lists as needed for different interactive commands. Keep in mind that each source has to be a key in `consult-omni-sources-alist'.
  (setq consult-omni-multi-sources '("calc"
                                     "File"
                                     "Buffer"
                                     ;; "Bookmark"
                                     "Apps"
                                     "gptel"
                                     "Dictionary"
                                     "Google"
                                     "Wikipedia"
                                     ;; "elfeed"
                                     "mu4e"
                                     "buffers text search"
                                     "Org Agenda"
                                     ;; "GitHub"
                                     "YouTube"))
                                     ;; "Invidious"))

  ;;; Pick you favorite autosuggest command.
  (setq consult-omni-default-autosuggest-command #'consult-omni-dynamic-google-autosuggest) ;;or any other autosuggest source you define

  ;;; Set your shorthand favorite interactive command
  (setq consult-omni-default-interactive-command #'consult-omni-multi))

Ivy Ecosystem

As Stolen from http://cestlaz.github.io/posts/using-emacs-6-swiper/ (January 10, 2017) it looks like counsel is a requirement for swiper

Ivy-rich

Let’s pretty up ivy This is stolen wholesale from Centaur Emacs. https://github.com/seagle0128/.emacs.d/blob/master/lisp/init-ivy.el

(use-package ivy-rich
  :defines (all-the-icons-icon-alist
            all-the-icons-dir-icon-alist
            bookmark-alist)
  :functions (all-the-icons-icon-for-file
              all-the-icons-icon-for-mode
              all-the-icons-icon-family
              all-the-icons-match-to-alist
              all-the-icons-faicon
              all-the-icons-octicon
              all-the-icons-dir-is-submodule)
  :preface
  (defun ivy-rich-bookmark-name (candidate)
    (car (assoc candidate bookmark-alist)))

  (defun ivy-rich-buffer-icon (candidate)
    "Display buffer icons in `ivy-rich'."
    (when (display-graphic-p)
      (let* ((buffer (get-buffer candidate))
             (buffer-file-name (buffer-file-name buffer))
             (major-mode (buffer-local-value 'major-mode buffer))
             (icon (if (and buffer-file-name
                            (all-the-icons-match-to-alist buffer-file-name
                                                          all-the-icons-icon-alist))
                       (all-the-icons-icon-for-file (file-name-nondirectory buffer-file-name)
                                                    :height 0.9 :v-adjust -0.05)
                     (all-the-icons-icon-for-mode major-mode :height 0.9 :v-adjust -0.05))))
        (if (symbolp icon)
            (setq icon (all-the-icons-faicon "file-o" :face 'all-the-icons-dsilver :height 0.9 :v-adjust -0.05))
          icon))))

  (defun ivy-rich-file-icon (candidate)
    "Display file icons in `ivy-rich'."
    (when (display-graphic-p)
      (let* ((path (concat ivy--directory candidate))
             (file (file-name-nondirectory path))
             (icon (cond ((file-directory-p path)
                          (cond
                           ((and (fboundp 'tramp-tramp-file-p)
                                 (tramp-tramp-file-p default-directory))
                            (all-the-icons-octicon "file-directory" :height 0.93 :v-adjust 0.01))
                           ((file-symlink-p path)
                            (all-the-icons-octicon "file-symlink-directory" :height 0.93 :v-adjust 0.01))
                           ((all-the-icons-dir-is-submodule path)
                            (all-the-icons-octicon "file-submodule" :height 0.93 :v-adjust 0.01))
                           ((file-exists-p (format "%s/.git" path))
                            (all-the-icons-octicon "repo" :height 1.0 :v-adjust -0.01))
                           (t (let ((matcher (all-the-icons-match-to-alist candidate all-the-icons-dir-icon-alist)))
                                (apply (car matcher) (list (cadr matcher) :height 0.93 :v-adjust 0.01))))))
                         ((string-match "^/.*:$" path)
                          (all-the-icons-material "settings_remote" :height 0.9 :v-adjust -0.2))
                         ((not (string-empty-p file))
                          (all-the-icons-icon-for-file file :height 0.9 :v-adjust -0.05)))))
        (if (symbolp icon)
            (setq icon (all-the-icons-faicon "file-o" :face 'all-the-icons-dsilver :height 0.9 :v-adjust -0.05))
          icon))))
  :hook ((ivy-mode . ivy-rich-mode)
         (ivy-rich-mode . (lambda ()
                            (setq ivy-virtual-abbreviate
                                  (or (and ivy-rich-mode 'abbreviate) 'name)))))
  :init
  ;; For better performance
  (setq ivy-rich-parse-remote-buffer nil)

  (setq ivy-rich-display-transformers-list
        '(ivy-switch-buffer
          (:columns
           ((ivy-rich-buffer-icon)
            (ivy-rich-candidate (:width 30))
            (ivy-rich-switch-buffer-size (:width 7))
            (ivy-rich-switch-buffer-indicators (:width 4 :face error :align right))
            (ivy-rich-switch-buffer-major-mode (:width 12 :face warning))
            (ivy-rich-switch-buffer-project (:width 15 :face success))
            (ivy-rich-switch-buffer-path (:width (lambda (x) (ivy-rich-switch-buffer-shorten-path x (ivy-rich-minibuffer-width 0.3))))))
           :predicate
           (lambda (cand) (get-buffer cand)))
          ivy-switch-buffer-other-window
          (:columns
           ((ivy-rich-buffer-icon)
            (ivy-rich-candidate (:width 30))
            (ivy-rich-switch-buffer-size (:width 7))
            (ivy-rich-switch-buffer-indicators (:width 4 :face error :align right))
            (ivy-rich-switch-buffer-major-mode (:width 12 :face warning))
            (ivy-rich-switch-buffer-project (:width 15 :face success))
            (ivy-rich-switch-buffer-path (:width (lambda (x) (ivy-rich-switch-buffer-shorten-path x (ivy-rich-minibuffer-width 0.3))))))
           :predicate
           (lambda (cand) (get-buffer cand)))
          counsel-switch-buffer
          (:columns
           ((ivy-rich-buffer-icon)
            (ivy-rich-candidate (:width 30))
            (ivy-rich-switch-buffer-size (:width 7))
            (ivy-rich-switch-buffer-indicators (:width 4 :face error :align right))
            (ivy-rich-switch-buffer-major-mode (:width 12 :face warning))
            (ivy-rich-switch-buffer-project (:width 15 :face success))
            (ivy-rich-switch-buffer-path (:width (lambda (x) (ivy-rich-switch-buffer-shorten-path x (ivy-rich-minibuffer-width 0.3))))))
           :predicate
           (lambda (cand) (get-buffer cand)))
          persp-switch-to-buffer
          (:columns
           ((ivy-rich-buffer-icon)
            (ivy-rich-candidate (:width 30))
            (ivy-rich-switch-buffer-size (:width 7))
            (ivy-rich-switch-buffer-indicators (:width 4 :face error :align right))
            (ivy-rich-switch-buffer-major-mode (:width 12 :face warning))
            (ivy-rich-switch-buffer-project (:width 15 :face success))
            (ivy-rich-switch-buffer-path (:width (lambda (x) (ivy-rich-switch-buffer-shorten-path x (ivy-rich-minibuffer-width 0.3))))))
           :predicate
           (lambda (cand) (get-buffer cand)))
          counsel-M-x
          (:columns
           ((counsel-M-x-transformer (:width 50))
            (ivy-rich-counsel-function-docstring (:face font-lock-doc-face))))
          counsel-describe-function
          (:columns
           ((counsel-describe-function-transformer (:width 50))
            (ivy-rich-counsel-function-docstring (:face font-lock-doc-face))))
          counsel-describe-variable
          (:columns
           ((counsel-describe-variable-transformer (:width 50))
            (ivy-rich-counsel-variable-docstring (:face font-lock-doc-face))))
          counsel-find-file
          (:columns
           ((ivy-rich-file-icon)
            (ivy-read-file-transformer)))
          counsel-file-jump
          (:columns
           ((ivy-rich-file-icon)
            (ivy-rich-candidate)))
          counsel-dired
          (:columns
           ((ivy-rich-file-icon)
            (ivy-read-file-transformer)))
          counsel-dired-jump
          (:columns
           ((ivy-rich-file-icon)
            (ivy-rich-candidate)))
          counsel-git
          (:columns
           ((ivy-rich-file-icon)
            (ivy-rich-candidate)))
          counsel-recentf
          (:columns
           ((ivy-rich-file-icon)
            (ivy-rich-candidate (:width 0.8))
            (ivy-rich-file-last-modified-time (:face font-lock-comment-face))))
          counsel-bookmark
          (:columns
           ((ivy-rich-bookmark-type)
            (ivy-rich-bookmark-name (:width 40))
            (ivy-rich-bookmark-info)))
          counsel-projectile-switch-project
          (:columns
           ((ivy-rich-file-icon)
            (ivy-rich-candidate)))
          counsel-projectile-find-file
          (:columns
           ((ivy-rich-file-icon)
            (counsel-projectile-find-file-transformer)))
          counsel-projectile-find-dir
          (:columns
           ((ivy-rich-file-icon)
            (counsel-projectile-find-dir-transformer)))
          treemacs-projectile
          (:columns
           ((ivy-rich-file-icon)
            (ivy-rich-candidate))))))

Ivy

(use-package ivy
  :hook (after-init . ivy-mode)
  :config
  (progn
    (setq ivy-use-virtual-buffers t)
    (setq ivy-initial-inputs-alist nil)
    (counsel-mode)
    (ivy-rich-mode)))

Counsel

(use-package counsel
  :after ivy
  :config
  (setq counsel-grep-base-command
        "rg -i -M 120 --no-heading --line-number --color never '%s' %s")
  (setq ivy-initial-inputs-alist nil)
  :bind
  (("M-x" . counsel-M-x)
    ("C-x C-f" . counsel-find-file)
    ("C-c p f" . counsel-projectile-find-file)
    ("C-c p d" . counsel-projectile-find-dir)
    ("C-c p p" . counsel-projectile-switch-project)
    ("<f1> f" . counsel-describe-function)
    ("<f1> v" . counsel-describe-variable)
    ("<f1> l" . counsel-load-library)
    ("<f2> i" . counsel-info-lookup-symbol)
    ("<f2> u" . counsel-unicode-char)
    ("C-c k" . counsel-rg)))

swiper

(use-package swiper :tangle no
  :after ivy
  :bind ("C-s" . swiper))

Code Completion

Corfu

(use-package corfu
  :init
  (global-corfu-mode)
  :hook (corfu-mode . corfu-popupinfo-mode)
  :config
  (setq corfu-auto-delay 0.1
        corfu-auto 't
        corfu-auto-prefix 1
        corfu-min-width 40
        corfu-min-height 20)

  ;; You can also enable Corfu more generally for every minibuffer, as
  ;; long as no other completion UI is active. If you use Mct or
  ;; Vertico as your main minibuffer completion UI, the following
  ;; snippet should yield the desired result.
  (defun corfu-enable-always-in-minibuffer ()
    "Enable Corfu in the minibuffer if Vertico/Mct are not active."
    (unless (or (bound-and-true-p mct--active) ; Useful if I ever use MCT
                (bound-and-true-p vertico--input))
      (setq-local corfu-auto nil) ; Ensure auto completion is disabled
      (corfu-mode 1)))
  (custom-set-faces '(corfu-current ((t :inherit region :background "#2d2844"))))
  (custom-set-faces '(corfu-popupinfo ((t :inherit corfu-default))))
  (add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1))

Kind Icon

Things are always better when they are prettier, let’s add icons to our in buffer completions and make them pretty. Nothing requires kind-icon, so nothing really brings it into scope. So, we can’t just stuff everything into config. We need a way to trigger calling, and loading, kind-icons. So, we add kind-icon to corfu’s formatters within the init block.

(use-package kind-icon
  :init
  (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter)
  :custom
  (kind-icon-default-face 'corfu-default)  ; Have background color be the same as `corfu' face background
  (kind-icon-default-style '(:padding 0 :stroke 0 :margin 0 :radius 0 :height 0.8 :scale 1.0)))

Cape

Cape adds some more completion backends for us, allowing us to get file and dabbrev completions where appropriate. We also have the possibility of using company completions with some helper functions given from cape.

(use-package cape
  :init
  ;; Add `completion-at-point-functions', used by `completion-at-point'.
  (add-to-list 'completion-at-point-functions #'cape-dabbrev)
  (add-to-list 'completion-at-point-functions #'cape-file)
  (add-to-list 'completion-at-point-functions #'cape-dict))

Company

(use-package company
  :commands (company-complete-common
             company-complete-common-or-cycle
             company-manual-begin
             company-grab-line)
  :hook (elpaca-after-init . global-company-mode)
  :bind (("C-<shift>-<tab>" . company-manual-begin)
         :map company-active-map
         ("C->" . #'company-filter-candidates)
         ("C-/" . #'company-other-backend))
  :config
  (require 'company-files)
  (setq company-minimum-prefix-length 1
        company-tooltip-limit 14
        company-idle-delay 0.1
        company-selection-wrap-around t
        company-tooltip-align-annotations t
        company-require-match 'never
        company-global-modes '(not erc-mode
                                   circe-mode
                                   message-mode
                                   help-mode
                                   gud-mode
                                   vterm-mode)
        company-frontends '(company-pseudo-tooltip-frontend  ; always show candidates in overlay tooltip
                            company-echo-metadata-frontend)  ; show selected candidate docs in echo area

        ;; Buffer-local backends will be computed when loading a major mode, so
        ;; only specify a global default here.
        company-backends '((company-capf :separate company-dabbrev)
                           company-files
                           company-yasnippet)

        ;; These auto-complete the current selection when
        ;; `company-auto-commit-chars' is typed. This is too magical. We
        ;; already have the much more explicit RET and TAB.
        company-auto-commit nil

        ;; Make `company-dabbrev' fully case-sensitive, to improve UX with
        ;; domain-specific words with particular casing.
        company-dabbrev-ignore-case nil
        company-dabbrev-downcase nil)
  (add-to-list 'company-files--regexps "file:\\(\\(?:\\.\\{1,2\\}/\\|~/\\|/\\)[^\]\n]*\\)"))

Company Box

(use-package company-box
  :hook (company-mode . company-box-mode)
  :init
  (require 'nerd-icons)
  (setq company-box-icons-alist 'company-box-icons-nerd-icons
        company-box-icons-nerd-icons
      `((Unknown        . ,(nerd-icons-codicon  "nf-cod-text_size"           :face  'font-lock-warning-face))
        (Text           . ,(nerd-icons-codicon  "nf-cod-text_size"           :face  'font-lock-doc-face))
        (Method         . ,(nerd-icons-codicon  "nf-cod-symbol_method"       :face  'font-lock-function-name-face))
        (Function       . ,(nerd-icons-codicon  "nf-cod-symbol_method"       :face  'font-lock-function-name-face))
        (Constructor    . ,(nerd-icons-codicon  "nf-cod-triangle_right"      :face  'font-lock-function-name-face))
        (Field          . ,(nerd-icons-codicon  "nf-cod-symbol_field"        :face  'font-lock-type-face))
        (Variable       . ,(nerd-icons-codicon  "nf-cod-symbol_variable"     :face  'font-lock-type-face))
        (Class          . ,(nerd-icons-codicon  "nf-cod-symbol_class"        :face  'font-lock-type-face))
        (Interface      . ,(nerd-icons-codicon  "nf-cod-symbol_interface"    :face  'font-lock-type-face))
        (Module         . ,(nerd-icons-codicon  "nf-cod-file_submodule"      :face  'font-lock-preprocessor-face))
        (Property       . ,(nerd-icons-codicon  "nf-cod-symbol_property"     :face  'font-lock-variable-name-face))
        (Unit           . ,(nerd-icons-codicon  "nf-cod-symbol_ruler"        :face  'font-lock-constant-face))
        (Value          . ,(nerd-icons-codicon  "nf-cod-symbol_field"        :face  'font-lock-builtin-face))
        (Enum           . ,(nerd-icons-codicon  "nf-cod-symbol_enum"         :face  'font-lock-builtin-face))
        (Keyword        . ,(nerd-icons-codicon  "nf-cod-symbol_keyword"      :face  'font-lock-keyword-face))
        (Snippet        . ,(nerd-icons-codicon  "nf-cod-notebook_template"      :face  'font-lock-string-face))
        (Color          . ,(nerd-icons-codicon  "nf-cod-symbol_color"        :face  'success))
        (File           . ,(nerd-icons-codicon  "nf-cod-symbol_file"         :face  'font-lock-string-face))
        (Reference      . ,(nerd-icons-codicon  "nf-cod-references"          :face  'font-lock-variable-name-face))
        (Folder         . ,(nerd-icons-codicon  "nf-cod-folder"              :face  'font-lock-variable-name-face))
        (EnumMember     . ,(nerd-icons-codicon  "nf-cod-symbol_enum_member"  :face  'font-lock-builtin-face))
        (Constant       . ,(nerd-icons-codicon  "nf-cod-symbol_constant"     :face  'font-lock-constant-face))
        (Struct         . ,(nerd-icons-codicon  "nf-cod-symbol_structure"    :face  'font-lock-variable-name-face))
        (Event          . ,(nerd-icons-codicon  "nf-cod-symbol_event"        :face  'font-lock-warning-face))
        (Operator       . ,(nerd-icons-codicon  "nf-cod-symbol_operator"     :face  'font-lock-comment-delimiter-face))
        (TypeParameter  . ,(nerd-icons-codicon  "nf-cod-list_unordered"      :face  'font-lock-type-face))
        (Template       . ,(nerd-icons-codicon  "nf-cod-notebook_template"      :face  'font-lock-escape-face))
        (ElispFunction  . ,(nerd-icons-codicon  "nf-cod-symbol_method"       :face  'font-lock-function-name-face))
        (ElispVariable  . ,(nerd-icons-codicon  "nf-cod-symbol_variable"     :face  'font-lock-type-face))
        (ElispFeature   . ,(nerd-icons-codicon  "nf-cod-globe"               :face  'font-lock-builtin-face))
        (ElispFace      . ,(nerd-icons-codicon  "nf-cod-symbol_color"        :face  'success))))

  :config
  (setq company-box-show-single-candidate t
        company-box-backends-colors nil
        company-box-tooltip-limit 50
        ;; Move company-box-icons--elisp to the end, because it has a catch-all
        ;; clause that ruins icons from other backends in elisp buffers.
        company-box-icons-functions
        (cons #'+company-box-icons--elisp-fn
              (delq 'company-box-icons--elisp
                    company-box-icons-functions)))
   
        
  (setq company-box-scrollbar nil)

  (defun +company-box-icons--elisp-fn (candidate)
    (when (derived-mode-p 'emacs-lisp-mode)
      (let ((sym (intern candidate)))
        (cond ((fboundp sym)  'ElispFunction)
              ((boundp sym)   'ElispVariable)
              ((featurep sym) 'ElispFeature)
              ((facep sym)    'ElispFace))))))

Yas Snippets

(use-package yasnippet
 :init (yas-global-mode))

Additional Snippets

(use-package yasnippet-snippets
  :after yasnippet)

Yasnippet capf

(use-package yasnippet-capf
  :commands yas-capf-minor-mode
  :ensure (:type git :host github :repo "justinbarclay/yasnippet-capf")
  :init
  (add-hook 'yas-minor-mode-hook #'yas-capf-minor-mode))

Languages

Major mode customizations

Code Formatting

(use-package apheleia
  :init
  (apheleia-global-mode)
  :config
  ;; Override ruby-ts-mode defaults
  (map-put! apheleia-mode-alist 'ruby-ts-mode '(rubocop)))

Tree Sitter support

(use-feature treesit
  :config
  (setq-default treesit-font-lock-level 4))

Treesit Auto

We want automatic fall back and to have manually manage `treesit-language-source-alist`.

(use-package treesit-auto
  :if (and (require 'treesit)
           (treesit-available-p))
  :defer 1
  :commands (make-treesit-auto-recipe)
  :custom
  (treesit-auto-install 'prompt)
  :init
  (setq my-jsdoc-tsauto-config
    (make-treesit-auto-recipe
     :lang 'jsdoc
     :ts-mode 'js-ts-mode
     :url "https://github.com/tree-sitter/tree-sitter-jsdoc"
     :revision "master"
     :source-dir "src"
     :requires 'javascript))
  (setq my-js-tsauto-config
   (make-treesit-auto-recipe
    :lang 'javascript
    :ts-mode 'js-ts-mode
    :remap '(js2-mode js-mode javascript-mode)
    :url "https://github.com/tree-sitter/tree-sitter-javascript"
    :revision "master"
    :requires 'jsdoc
    :source-dir "src"
    :ext "\\.js\\'"))
  (add-to-list 'treesit-auto-recipe-list my-js-tsauto-config)
  (add-to-list 'treesit-auto-recipe-list my-jsdoc-tsauto-config)
  (add-to-list 'treesit-auto-langs 'jsdoc)
  :config
  (global-treesit-auto-mode))

Combobulate

Better movement through treesitter

(use-package combobulate
  :ensure (:type git :host github :repo "mickeynp/combobulate")
  :hook (prog-mode . combobulate-mode)
  :custom (combobulate-js-ts-enable-auto-close-tag . nil))

General Intellisense

copilot

(use-package copilot
  :after jsonrpc
  :hook (prog-mode . copilot-mode)
  :ensure (copilot
           :files ("dist" "*.el")
           :type git
           :host github
           :repo "copilot-emacs/copilot.el")
  :bind (:map copilot-completion-map
              ("TAB" . 'copilot-accept-completion)
              ("C-TAB" . 'copilot-accept-completion-by-word)))

GPTel

(use-package gptel
  :config
  (setq
   gptel-default-mode 'org-mode
   gptel-model 'gemini-1.5-pro-latest
   gptel-backend (gptel-make-gemini "gemini"
                   :key (string-trim (aio-wait-for (1password--read "Gemini" "credential" "private")))
                   :stream t)))

LSP-Mode

lsp-mode overtakes flycheck’s system in whichever buffer it runs in. This causes issues when you want other linters to run at the same time. Following allong with the suggestion here, I’ve added a hook that adds back in the linters that I care about.
(use-package lsp-mode
  :commands lsp
  :hook ((rustic-mode
          rust-base-mode
          web-mode
          ruby-base-mode
          c-mode
          js-base-mode
          json-ts-mode
          typescript-ts-mode
          lua-mode
          jsx-ts-mode
          tsx-ts-mode
          less-css-mode
          css-ts-mode
          go-base-mode
          LaTeX-mode
          nix-mode
          org-mode)
         . lsp-deferred)
  (lsp-mode . lsp-enable-which-key-integration)
  :custom
  (lsp-idle-delay 0.1)
  (lsp-log-io nil)
  (lsp-completion-provider :none)
  (lsp-headerline-breadcrumb-enable nil)
  (lsp-solargraph-use-bundler 't)
  (lsp-keymap-prefix "C-l")
  (lsp-diagnostic-clean-after-change 't)
  (lsp-copilot-enabled 't)
  :config
  (defvar lsp-flycheck-mapping '(less-css-mode (less-stylelint less)
                                 css-base-mode (css-stylelint)
                                 js-base-mode (javascript-eslint)
                                 typescript-ts-base-mode (javascript-eslint)
                                 tsx-ts-mode (javascript-eslint)
                                 jsx-ts-mode (javascript-eslint))
                              "a selections of major modes and the associated checkers to run after lsp
runs it's diagnostics.")
  (defvar-local my/flycheck-local-cache nil)

  (defun my/flycheck-checker-get (fn checker property)
    (or (alist-get property (alist-get checker my/flycheck-local-cache))
        (funcall fn checker property)))

  (advice-add 'flycheck-checker-get :around 'my/flycheck-checker-get)

  (add-hook 'lsp-managed-mode-hook
            (lambda ()
              (when-let ((checkers (plist-get lsp-flycheck-mapping major-mode)))
                (setq my/flycheck-local-cache `((lsp . ((next-checkers . ,checkers))))))))
  (setopt flycheck-error-list-minimum-level nil)
  (setopt flycheck-relevant-error-other-file-show 't)
  (setopt flycheck-relevant-error-other-file-minimum-level 'warning)
  ;; As stolen from https://github.com/emacs-lsp/lsp-mode/issues/3279
  (defun lsp-diagnostics--flycheck-start (checker callback)
   "Start an LSP syntax check with CHECKER.

CALLBACK is the status callback passed by Flycheck."

   (remove-hook 'lsp-on-idle-hook #'lsp-diagnostics--flycheck-buffer t)

   (->> (lsp--get-buffer-diagnostics)
        (-mapcat
         (-lambda ((&Diagnostic :message :severity? :tags? :code? :source? :related-information?
                                :range (&Range :start (&Position :line      start-line
                                                                 :character start-character)
                                               :end   (&Position :line      end-line
                                                                 :character end-character))))
           (let ((group (gensym)))
             (cons (flycheck-error-new
                    :buffer (current-buffer)
                    :checker checker
                    :filename buffer-file-name
                    :message message
                    :level (lsp-diagnostics--flycheck-calculate-level severity? tags?)
                    :id code?
                    :group group
                    :line (lsp-translate-line (1+ start-line))
                    :column (1+ (lsp-translate-column start-character))
                    :end-line (lsp-translate-line (1+ end-line))
                    :end-column (1+ (lsp-translate-column end-character)))
                   (-mapcat
                    (-lambda ((&DiagnosticRelatedInformation
                               :message
                               :location
                               (&Location :range (&Range :start (&Position :line      start-line
                                                                           :character start-character)
                                                         :end   (&Position :line      end-line
                                                                           :character end-character))
                                          :uri)))
                      `(,(flycheck-error-new
                          :buffer (current-buffer)
                          :checker checker
                          :filename (-> uri lsp--uri-to-path lsp--fix-path-casing)
                          :message message
                          :level (lsp-diagnostics--flycheck-calculate-level (1+ severity?) tags?)
                          :id code?
                          :group group
                          :line (lsp-translate-line (1+ start-line))
                          :column (1+ (lsp-translate-column start-character))
                          :end-line (lsp-translate-line (1+ end-line))
                          :end-column (1+ (lsp-translate-column end-character)))))
                    related-information?)))))
        (funcall callback 'finished))))
lsp-ui
(use-package lsp-ui
  :commands lsp-ui-mode
  :hook (lsp-mode . lsp-ui-mode))

Eglot

(use-package eglot
  :bind (("C-c r" . eglot-rename)
         ("C-c o" . eglot-code-action-organize-imports)
         ("C-c h" . eldoc))
  :custom
  (eglot-send-changes-idle-time 0.1))

DevDocs

(use-package devdocs)

CSS/LESS/SASS

CSS

(use-feature css-mode
  :config
  (setq css-indent-offset 2))

Less

(use-feature less-css-mode
  :config
  (setq css-indent-offset 2))

Rainbow mode

(use-package rainbow-mode
  :hook ((css-mode . rainbow-mode)
         (less-mode . rainbow-mode)))

Sass mode

(use-package sass-mode
  :mode "\\.sass\\'")

C/C++

Add hooks and customizations

(use-feature c-ts-base-mode
  :config
  (progn ; C mode hook
    (add-hook 'c-mode-hook 'flycheck-mode)))

c++

(use-feature c++-ts-mode)

c-eldoc

(use-package c-eldoc)

Lispy-languages

Enable Paredit or Parinfer

  (defun enable-paredit ()
    (turn-off-smartparens-mode)
    (paredit-mode))

  (defun enable-parinfer ()
    (global-hungry-delete-mode 0)
    (turn-off-smartparens-mode)
    (paredit-mode)
    (parinfer-rust-mode))

(defun enable-lispy ()
    (turn-off-smartparens-mode)
    (lispy-mode))

paredit

(use-package paredit
  :commands (paredit-mode)
  :hook ((common-lisp-mode . (lambda () (enable-paredit)))
         (scheme-mode . (lambda () (enable-paredit)))
         (lisp-mode . (lambda () (enable-paredit)))))

lispy

We need lispy for some of the excellent bracket based navigation integrations with parinfer

(use-package lispy)

parinfer-rust-mode

(use-package parinfer-rust-mode
  :commands (parinfer-rust-mode)
  :hook (emacs-lisp-mode)
  :custom
  (parinfer-rust-disable-troublesome-modes 't)
  (parinfer-rust-check-before-enable 'defer)
  (parinfer-rust-auto-download nil)
  :config
  (add-hook 'vundo-pre-enter-hook (lambda () (parinfer-rust-mode 0) nil t))
  (add-hook 'vundo-post-exit-hook (lambda () (parinfer-rust-mode 1) nil t))
  (add-hook 'parinfer-rust-mode-hook 'parinfer-rust--auto-apply-fast-mode))

eldoc

(use-feature eldoc
  :config
  (global-eldoc-mode))

Elisp

elisp-mode

(use-feature elisp-mode
  :hook (emacs-lisp . enable-paredit))

Clojure

clj-refactor

;; clojure refactor library
;; https://github.com/clojure-emacs/clj-refactor.el
(use-package clj-refactor
  :after clojure-mode
  :config (progn (setq cljr-suppress-middleware-warnings t)
                 (add-hook 'clojure-mode-hook (lambda ()
                                                (clj-refactor-mode 1)
                                                (cljr-add-keybindings-with-prefix "C-c C-m")))))

Kibit

(use-package kibit-helper
  :defer t)

clojure-mode

(use-package clojure-mode
  :mode (("\\.clj\\'" . clojure-mode)
         ( "\\.cljs\\'" . clojurescript-mode))
  :init
  (progn
    (add-hook 'clojure-mode-hook (lambda () (enable-parinfer)))
    (add-hook 'clojure-mode-hook 'flycheck-mode)
    (add-hook 'clojure-mode-hook 'cider-mode)
    (add-hook 'clojure-mode-hook 'eldoc-mode)
    (add-hook 'clojure-mode-hook 'subword-mode))
  :config
  (progn
    (add-to-list 'auto-mode-alist '("\\.edn$" . clojure-mode))
    (add-to-list 'auto-mode-alist '("\\.boot$" . clojure-mode))
    (font-lock-add-keywords
     nil
     '(("(\\(facts?\\)"
        (2 font-lock-keyword-face))
       ("(\\(background?\\)"
        (2 font-lock-keyword-face))))
    (electric-pair-mode 1)
    (setq define-clojure-indent 2)))

Clojure mode also supports extra font locking(for syntax highlighting), but I have noticed that this causes performance issues in large and complicated clojure files (which I have been playing a lot with lately), so I have turned this feature off.

(require 'clojure-mode-extra-font-locking)

Cider

A REPL for Clojure and nrepl for ClojureScript

(use-package cider
  :hook ((clojure-mode . cider-mode)
         (clojurescript-mode . cider-mode))
  :commands (cider-jack-in cider-jack-in-clojurescript)
  :config
  (progn
    ;; REPL related stuff
    ;; REPL history file
    (setq cider-repl-history-file "~/.emacs.d/cider-history")
    ;; nice pretty printing

    (setq cider-repl-use-pretty-printing t)
    ;; nicer font lock in REPL

    (setq cider-repl-use-clojure-font-lock t)
    ;; result prefix for the REPL

    (setq cider-repl-result-prefix ";; => ")
    ;; never ending REPL history

    (setq cider-repl-wrap-history t)

    ;; looong history
    (setq cider-repl-history-size 3000)
    ;; eldoc for clojure

    (add-hook 'cider-mode-hook #'eldoc-mode)

    ;; error buffer not popping up
    (setq cider-show-error-buffer nil)

    ;; go right to the REPL buffer when it's finished connecting
    (setq cider-repl-pop-to-buffer-on-connect nil)

    ;; key bindings
    ;; these help me out with the way I usually develop web apps
    (defun cider-refresh ()
      (interactive)
      (cider-interactive-eval (format "(user/reset)")))))
Custom cider functions

This was suggested by @dpsutton on the slack channel and seems to be really interesting. It creates a little pop-up window by you cursor to show you the meaning of a thing

(defun cider--tooltip-show ()
  (interactive)
  (if-let ((info (cider-var-info (thing-at-point 'symbol))))
      (nrepl-dbind-response info (doc arglists-str name ns)
        (pos-tip-show (format "%s : %s\n%s\n%s" ns name arglists-str doc)
                      nil
                      nil
                      nil
                      -1))
    (message "info not found")))

Janet

(use-package janet-mode)

JavaScript Land

Javascript

(use-feature js-base-mode
 :custom
 (js-indent-level 2)
 (lsp-eslint-enable 't))
(use-feature js-ts-mode
  :mode ("\\.js\\'" "\\.cjs\\'" "\\.mjs\\'"))

Typescript

(use-feature typescript-ts-base-mode    
  :custom
  (typescript-indent-level 2)
  (lsp-eslint-enable 't)
  :config
  (flycheck-add-mode 'javascript-eslint 'typescript-ts-base-mode))
(use-feature typescript-ts-mode
  :mode ("\\.ts\\'" "\\.mts\\'" "\\.ts.snap\\'"))

TSX Mode

(use-feature tsx-ts-mode
  :mode "\\.tsx\\'")

Prettier

Autoformatting

(use-package prettier-js
  :hook ((typescript-ts-base-mode . prettier-js-mode)
         (js-base-mode . prettier-js-mode)
         (json-ts-mode . prettier-js-mode)
         (less-css-mode . prettier-js-mode)
         (web-mode . prettier-js-mode)))

denofmt

(use-package deno-fmt)

eslint-disable

(use-package eslint-disable-rule)

HTML

Web Mode

(use-package web-mode
  :mode (("\\.html?\\'" . web-mode))
  :config
  (setq web-mode-markup-indent-offset 2
        web-mode-css-indent-offset 2
        web-mode-code-indent-offset 2
        web-mode-block-padding 2
        web-mode-comment-style 2

        web-mode-enable-css-colorization t
        web-mode-enable-auto-pairing t
        web-mode-enable-comment-keywords t
        web-mode-enable-current-element-highlight t)
        (flycheck-add-mode 'javascript-eslint 'web-mode))

tagedit

(use-package tagedit
 :defer t)

sgml-mode

(use-feature sgml-mode
  :after tagedit
  :config
  (require 'tagedit)
  (tagedit-add-paredit-like-keybindings)
  (add-hook 'html-mode-hook (lambda () (tagedit-mode 1))))

Ruby

ruby-mode

(use-feature ruby-ts-mode
  :mode "\\.rb\\'"
  :mode "Rakefile\\'"
  :mode "Gemfile\\'"
  :mode "Vagrantfile\\'"
  :interpreter "ruby"
  :bind (:map ruby-ts-mode-map
              ("C-c r b" . 'treesit-beginning-of-defun)
              ("C-c r e" . 'treesit-end-of-defun))
  :hook (ruby-base-mode . subword-mode)
  :custom (ruby-indent-level 2)
          (ruby-indent-tabs-mode nil))

rbenv

(use-package rbenv
  :config
  (setq rbenv-installation-dir "/usr/local/bin/rbenv"))

inf-ruby

(use-package inf-ruby
  :defer t
  :bind
  (:map inf-ruby-minor-mode-map
        (("C-c C-z" . run-ruby)
         ("C-c C-b" . ruby-send-buffer)))
  :init
  (add-hook 'inf-ruby-mode-hook (lambda () (corfu-mode -1)))
  :config
    (progn
      (when (executable-find "pry")
        (add-to-list 'inf-ruby-implementations '("pry" . "pry"))
        (setq inf-ruby-default-implementation "pry"))))

formatter

(use-package rubocopfmt
  :hook ((ruby-base-mode . rubocopfmt-mode)
         (ruby-ts-mode . rubocopfmt-mode))
  :custom (rubocopfmt-on-save-use-lsp-format-buffer 't))

Test running library

(use-package rspec-mode
 :hook (ruby-base-mode . rspec-enable-appropriate-mode)
 :config
 (rspec-install-snippets))

Rust

Cargo

(use-package cargo)

Rustic

We need to remove rust-mode from auto-mode-alist because either Cargo or Rust-playground packages are causing rust-mode to shadow rustic-mode.

(use-package rustic
  :mode (("\\.rs\\'" . rustic-mode))
  :config
  (setq rustic-lsp-setup-p '())
  (setq rustic-indent-offset 2)
  (electric-pair-mode 1))

Rust repl

(use-package evcxr-mode
   :defer t
   :ensure nil
   
   :config (add-hook 'rustic-mode-hook 'evcxr-minor-mode)
   :quelpa ((evcxr
             :fetcher git
             :url "https://github.com/SerialDev/evcxr-mode.git"
             :upgrade t)))

Nix

Major Mode

(use-package nix-mode)

nixos-options

(use-package nixos-options)

nixpkgs-fm

(use-package nixpkgs-fmt
 :hook (nix-mode . nixpkgs-fmt-on-save-mode))

Go

Go Mode

(use-feature go-ts-mode
  :mode "\\.go\\'"
  :custom
  (go-ts-mode-indent-offset 2)
  :config
  (add-hook 'before-save-hook 'gofmt-before-save))

Lua

(use-package lua-mode)

JSON

(use-feature json-ts-mode
  :mode "\\.json\\'"
  :config
  (setq js-indent-level 2))

Docker

Dockerfile

(use-package dockerfile-mode
  :mode "\\Dockerfile\\'"
  :config
  (setq-local tab-width 2))

Docker

(use-package docker)

Markdown

(use-package markdown-mode
  :commands (markdown-mode gfm-mode)
  :mode (("README\\.md\\'" . gfm-mode)
         ("\\.md\\'" . markdown-mode)
         ("\\.markdown\\'" . markdown-mode))
  :init (setq markdown-command "multimarkdown"))

Flymd

Live preview of MarkDown

(use-package flymd
 :commands (flymd-flyit))

Powershell

(use-package powershell
  :mode "\\.ps\\'")

NuShell Config

(use-package nushell-ts-mode
  :mode "\\.nu\\'")

Terraform

Terraform mode

(use-package terraform-mode
:mode "\\.tf\\'" )

YAML

(use-package yaml-mode
  :defer t)

SSH

(use-package ssh-config-mode
  :defer t)

SQL

Linting

;; (use-package sqlint)

Latex

Much like Mu4e we treat this as a feature because otherwise there is too much ceremony in setting up an Auctex. Instead, we rely on Nix to install this on Darwin and Linux. On windows though? shrug 🤷.

(use-feature auctex
    :mode (("\\.tex\\'" . TeX-latex-mode)
           ("\\.tex\\.erb\\'" . TeX-latex-mode)
           ("\\.etx\\'" . TeX-latex-mode))
    :hook
    ((LaTeX-mode . turn-on-auto-fill)
     (LaTeX-mode . TeX-source-correlate-mode))
    :config
    (setq-default TeX-master nil)
    (add-hook 'TeX-after-compilation-finished-functions #'TeX-revert-document-buffer)
    :custom
    (LaTeX-item-indent 0)
    (TeX-auto-local ".auctex-auto")
    (TeX-style-local ".auctex-style")
    (TeX-auto-save t)
    (TeX-parse-self t)
    (TeX-save-query nil)
    ;; use pdflatex
    (TeX-PDF-mode t)
    (TeX-source-correlate-start-server nil)
    (TeX-source-correlate-method 'synctex)
    ;; use evince for dvi and pdf viewer
    ;; evince-dvi backend should be installed
    (TeX-view-program-selection
     '((output-dvi "DVI Viewer")
       (output-pdf "PDF Tools")
       (output-html "Firefox")))
    (TeX-view-program-list
     '(("DVI Viewer" "evince %o")
       ("PDF Tools" TeX-pdf-tools-sync-view))))

Then we want org support for math latex

(use-package cdlatex)

PDF Viewer

(use-package pdf-tools
 :hook (pdf-view-mode . (lambda () (display-line-numbers-mode -1)))
 :init
 (pdf-loader-install))

Text modes

(use-feature text-mode
  :custom
  (text-mode-ispell-word-completion nil))
(use-feature rst
  :mode (("\\.txt$" . rst-mode)
         ("\\.rst$" . rst-mode)
         ("\\.rest$" . rst-mode)))

Android

Kotlin

(use-package kotlin-ts-mode)

android-env

(use-package android-env)

Communication

Tramp

(use-feature tramp
  :defer t
  :hook (tramp-mode . (lambda () (projectile-mode 0)))
  :config (setq debug-ignored-errors (cons 'remote-file-error debug-ignored-errors))
  :custom
  (tramp-terminal-type "tramp")
  (tramp-use-ssh-controlmaster-options nil))

Alert

(use-package alert
      :commands (alert alert-define-style)
      :config
      (defun alert-burnt-toast-notify (info)
        (let ((args
               (list
                "-c" "New-BurntToastNotification"
                "-Text" (if-let ((title (plist-get info :title)))
                            (format "'%s', '%s'" title (plist-get info :message))
                          (format "'%s'" (plist-get info :message)))
                )))
          (apply #'start-process (append '("burnt-toast" nil "powershell.exe") args))))
      (alert-define-style 'burnt-toast :title "Notify Windows 10 using the PowerShell library BurntToast"
                          :notifier
                          #'alert-burnt-toast-notify)
      (setq alert-default-style 'burnt-toast))

Misc

codemetrics

(use-package cognitive-complexity 
  :ensure (:type git :host github :repo "emacs-vs/cognitive-complexity"))

Yequake

(use-package yequake
  :config
  (add-to-list 'yequake-frames '("consult-omni-demo"
                                 (buffer-fns . #'consult-omni-apps)
                                 (width . 0.8)
                                 (height . 0.1)
                                 (top . 0.5)
                                 (frame-parameters . ((name . "yequake-demo")
                                                      (minibuffer . only)
                                                      (autoraise . t)
                                                      (window-system . ns)))))) ;;change accordingly
                                                    

dot

(use-package graphviz-dot-mode)

1Password

(use-package 1password
  :ensure (1password :type git :host github :repo "justinbarclay/1password.el")
  :demand t
  :commands (1password-search-password 1password-search-id 1password-enable-auth-source)
  :hook (elpaca-after-init-hook . (lambda () (1password-enable-auth-source)))
  :custom
  (1password-results-formatter '1password-colour-formatter)
  (1password-executable (if (executable-find "op.exe")
                            "op.exe"
                          "op")))

Helpful

A better help buffer experience

(use-package helpful
  :defer t
  :bind  (("C-h f" . helpful-callable)
          ("C-h v" . helpful-variable)
          ("C-h k" . helpful-key)))

Which Key

(use-feature which-key
  :defer 't
  :init (which-key-mode))

Woman

(use-feature woman
  :config
  (progn (setq woman-manpath
              (split-string (shell-command-to-string "man --path") ":" t "\n"))
        (autoload 'woman "woman"
          "Decode and browse a UN*X man page." t)
        (autoload 'woman-find-file "woman"
          "Find, decode and browse a specific UN*X man-page file." t)))

Profiling

This function displays how long Emacs took to start. It’s a rough way of knowing when/if I need to optimize my init file.

(add-hook 'elpaca-after-init-hook
          (lambda ()
            (message "Emacs loaded in %s with %d garbage collections."
                     (format "%.2f seconds"
                             (float-time
                              (time-subtract (current-time) before-init-time)))
                     gcs-done)))

Esup

(use-package esup
  :commands (esup))

profiler

(use-feature profiler
  :bind
  (("s-l" . profiler-start)
   ("s-r" . profiler-report)))

http client

(use-package restclient)

Git-link

For when you want to link to the current file from github or gitlab

(use-package git-link)

git-sync

(use-package git-sync-mode
  :commands (git-sync-mode git-sync-global-mode)
  :ensure (:type git :host github :repo "justinbarclay/git-sync-mode"))

leetcode-emacs

(use-package leetcode-emacs
 :commands (leetcode-show)
 :ensure (leetcode :type git
          :host github
          :repo "ginqi7/leetcode-emacs"
          :files ("leetcode.el"))
 :config
 (setq leetcode-language "rust"))

Custom Functions

Allow for clojure style comment blocks

(defmacro comment (docstring &rest body)
  "Ignores body and yields nil"
  nil)

Resize font size

Increases the fonts size across all buffers

(defun font-name-replace-size (font-name new-size)
  (let ((parts (split-string font-name "-")))
    (setcar (nthcdr 7 parts) (format "%d" new-size))
    (mapconcat 'identity parts "-")))

(defun increment-default-font-height (delta)
  "Adjust the default font height by DELTA on every frame.
The pixel size of the frame is kept (approximately) the same.
DELTA should be a multiple of 10, in the units used by the
:height face attribute."
  (let* ((new-height (+ (face-attribute 'default :height) delta))
         (new-point-height (/ new-height 10)))
    (dolist (f (frame-list))
      (with-selected-frame f
        ;; Latest 'set-frame-font supports a "frames" arg, but
        ;; we cater to Emacs 23 by looping instead.
        (set-frame-font (font-name-replace-size (face-font 'default)
                                                new-point-height)
                        t)))
    (set-face-attribute 'default nil :height new-height)
    (message "default font size is now %d" new-point-height)))

(defun increase-default-font-height ()
  (interactive)
  (increment-default-font-height 10))

(defun decrease-default-font-height ()
  (interactive)
  (increment-default-font-height -10))

(global-set-key (kbd "C-M-=") 'increase-default-font-height)
(global-set-key (kbd "C-M--") 'decrease-default-font-height)

Open init

(defun open-config-file ()
  (interactive)
  (find-file "~/.emacs.d/README.org"))

(global-set-key (kbd "C-c i") 'open-config-file)

Sudo Save

For saving root protected files

(defun sudo-save ()
  (interactive)
  (if (not buffer-file-name)
      (write-file (concat "/sudo:root@localhost:" (ido-read-file-name "File:")))
    (write-file (concat "/sudo:root@localhost:" buffer-file-name))))

Rename buffer and file

;; source: http://steve.yegge.googlepages.com/my-dot-emacs-file
(defun rename-file-and-buffer (new-name)
  "Renames both current buffer and file it's visiting to NEW-NAME."
  (interactive "sNew name: ")
  (let ((name (buffer-name))
        (filename (buffer-file-name)))
    (if (not filename)
        (message "Buffer '%s' is not visiting a file!" name)
      (if (get-buffer new-name)
          (message "A buffer named '%s' already exists!" new-name)
        (progn
          (rename-file filename new-name 1)
          (rename-buffer new-name)
          (set-visited-file-name new-name)
          (set-buffer-modified-p nil))))))

Stats

(defun jb/mean (a)
  (/ (apply '+ a)
     (length a)))
(defun jb/square (a)
  (* a a))

(defun jb/stdev (a)
  (sqrt
   (/
    (apply '+ (mapcar 'square (mapcar (lambda (subtract)
                                        (- subtract (mean a)))
                                      a)))
    (- (length a) 1 ))))

Screenshots

(defun take-screenshot ()
  (interactive)
  (let ((frame-height (read-number "Enter frame height: " 40))
        (frame-width (read-number "Enter frame width: " 55))
        (filename (read-file-name "Where would you like to save the svg? ")))
    (set-frame-width (selected-frame) frame-width)
    (set-frame-height (selected-frame) frame-height)
    (with-temp-file filename
      (insert (x-export-frames nil 'svg)))
    (kill-new filename)
    (message filename)))

Turn back on file-name-handler-alist

(setq file-name-handler-alist doom--file-name-handler-alist)

Playground

Pomodoro timer

(require 'alert)
(require 'seq)
(require 'zone)

(defvar pomodoro--current-buffer nil "Buffer to return to after the break")
(defvar pomodoro--round 0 "The current iteration pomodoro")

;; Todo make this a defcustom
(defvar pomodoro-activity-list '("go for a walk" "stretch your back" "core exercises") "A list of activity you wish to be reminded to do during your breaks")
(defvar pomodoro--completed-activities '() "Activities completed during the current session")
(defvar pomodoro--timer nil "Current timer for pomodoro. It could be the break timer or the pomodoro timer itself.")
(defvar pomodoro--last-buffer nil "Return to this buffer after the pomodoro break is over.")

;; This is easy to do inline, but I like providing names to ideas like this rather than
;; relying on implicit knowledge
(defun pomodoro--minutes (minutes)
  "Converts the given minutes to seconds"
  (* minutes 60))

(defun pomodoro--suggest-activity (activity-list completed-activities)
  (let  ((suggested-list (seq-filter (lambda (item)
                                       (not
                                        (member item (or completed-activities
                                                         '()))))
                                     activity-list)))
    (when suggested-list
      (nth
       (random (length suggested-list))
       suggested-list))))

(defun pomodoro-start (&optional message)
  (interactive)
  (when (yes-or-no-p (or message
                         "Would you like to start a pomodoro session?"))
    (message "Round %s" pomodoro--round)
    (setq pomodoro--timer
          (run-at-time (pomodoro--minutes 25)
                       nil
                       (lambda ()
                         (let* ((suggested-activity-maybe (pomodoro--suggest-activity pomodoro-activity-list pomodoro--completed-activities))
                                ;; Reset the completed activities list if we've run out of activities to suggest
                                (suggested-activity (if suggested-activity-maybe
                                                       suggested-activity-maybe
                                                     (pomodoro--suggest-activity pomodoro-activity-list
                                                                                 (setq pomodoro--completed-activities nil)))))
                           (if pomodoro--completed-activities
                               (push suggested-activity pomodoro--completed-activities)
                             (setq pomodoro--completed-activities (list suggested-activity)))
                           (message "Time to take a break!")
                           (alert (format "Get up and %s" 'suggested-activity) :title "Pomodoro")
                           (pomodoro-break (setq pomodoro--round (1+ pomodoro--round)))))))))

(defun pomodoro-break (round)
  (setq pomodoro--last-buffer (current-buffer))
  (setq pomodoro--timer
        (run-at-time (pomodoro--minutes 5)
                     nil
                     (lambda ()
                       (pomodoro-start "Would you like to continue your pomodoro session?")
                       (message "Welcome back!"))))
  (zone))

(defun pomodoro-cancel-timer ()
  (interactive)
  (when pomodoro--timer
    (cancel-timer pomodoro--timer)
    (message "Timer canceled")))

Count line repititions

(defun count-repititions ()
  (interactive)
  ;;
  (let ((tracker (make-hash-table :test 'equal))
        (buffer (current-buffer)))
    (with-temp-buffer
      (insert-buffer buffer)
      (goto-char (point-min))
      (replace-regexp "^[0-9]+:[0-9][0-9]" "")
      (delete-blank-lines)
      (sort-lines nil (point-min) (point-max))
      (goto-char (point-min))
      (while (not (eobp))
        (delete-blank-lines)
        (let ((current-line (string-trim
                             (buffer-substring-no-properties ;; current-line
                              (line-beginning-position)
                              (line-end-position)))))
          (when (string-match "^All measurement" current-line)
            (puthash current-line
                     (+ 1 (gethash current-line tracker 0))
                     tracker)))
        (forward-line 1)))
    (message "%s" (length (hash-table-keys tracker)))
    (with-current-buffer (get-buffer-create "*repititions*")
      (erase-buffer)
      (maphash (lambda (k v)
                 (insert (format "%s - %s\n" v k)))
               tracker)
      (goto-char (point-min))
      (sort-numeric-fields 1 (point-min) (point-max)))))

Kebab Case String

(defun kebab-case (string)
  "Convert STRING to kebab-case. For example, HelloWorld! becomes hello-world! Note that this downcases the first character but does not add a - before it"
  (let ((case-fold-search nil))
    (-> (replace-regexp-in-string
         "\\([A-Z]\\)"
         "-\\1"
         string)
      (downcase)
      (string-trim-left "-"))))

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published