jeremygooch / dotemacs

My personal emacs configuration

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Jeremy’s Emacs Config

Deprecated

This repo has been deprecated and moved to https://github.com/jeremygooch/jeremacs.

Dependencies

Non critical deps

  • Latex (for some notes)

Initialization

early-init.el

In Emacs >= 27.1, the early-init.el file is run before the GUI is created. This can be used to take care of a few miscellaneous odds and ends.

;; -*- lexical-binding: t; -*-

;; -------------------------------------------------------------------------------- ;;
;; This early-init.el file was auto-tangled from an orgmode file.                   ;;
;; -------------------------------------------------------------------------------- ;;

;; Garbage Collections
(setq gc-cons-threshold 100000000) ;; ~100mb
(setq read-process-output-max 3000000) ;; ~3mb
(setq gc-cons-percentage 0.6)

;; Compile Warnings
(setq comp-async-report-warnings-errors nil) ;; native-comp warning
(setq byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local))

;; Whether to make packages available when Emacs starts
(setq package-enable-at-startup nil)

;; Disables bi-directional editing (i.e. writing in both Arabic and English)
(setq-default bidi-display-reordering 'left-to-right 
              bidi-paragraph-direction 'left-to-right)
(setq bidi-inhibit-bpa t)  ; emacs 27 only - disables bidirectional parenthesis

;; Misc UI optimizations
(setq fast-but-imprecise-scrolling t)
(setq inhibit-compacting-font-caches t)
;; Slow down the UI updates a bit
(setq idle-update-delay 1.0)

Personal Information

Header of tangled output

;; -*- lexical-binding: t; -*-
;;; 
;;; Jeremy's Emacs Configuration
;;;

;; Copyright (C) Jeremy Gooch
;; Author: Jeremy Gooch <jeremy.gooch@gmail.com>
;; URL: https://github.com/jeremygooch/dotemacs
;; This file is not part of GNU Emacs.
;; This file is free software.

;; ------- The following code was auto-tangled from an Orgmode file. ------- ;;

For the sake of completeness, configure name and email address

(setq user-full-name "Jeremy Gooch"
      user-mail-address "jeremy.gooch@gmail.com")

Setup a prefix for custom commands.

(progn (define-prefix-command 'jrm-key-map))
(global-set-key (kbd "C-x C-j") jrm-key-map)

Packages

Set Sources

(require 'package)

(setq package-archives '(
                         ("melpa-stable" . "http://stable.melpa.org/packages/")
                         ("elpa" . "https://elpa.gnu.org/packages/")
                         ("gnu" . "http://elpa.gnu.org/packages/")
                         ("melpa" . "https://melpa.org/packages/")))
(package-initialize)

use-package Setup

(eval-when-compile
  (require 'use-package))

(require 'use-package-ensure)
(setq use-package-always-ensure t)
(setq use-package-verbose nil)

;; Allow use-package to install missing system packages
(use-package use-package-ensure-system-package :ensure t)

Better Garbage Collection Strategy

(use-package gcmh
  :diminish gcmh-mode
  :config
  (setq gcmh-idle-delay 5
        gcmh-high-cons-threshold (* 16 1024 1024))  ; 16mb
  (gcmh-mode 1))

(add-hook 'emacs-startup-hook
          (lambda ()
            (setq gc-cons-percentage 0.1))) ;; Default value for `gc-cons-percentage'

Global Variables

System specific variables

Make sure to copy to ~/.emacs.d

(load (expand-file-name "jrm-variables.el" user-emacs-directory))

Custom Files

(if (file-exists-p "~/.emacs.d/jrm-util.el") (load "~/.emacs.d/jrm-util.el"))
(if (file-exists-p "~/.emacs.d/tf.el") (load "~/.emacs.d/tf.el"))

Path

(setq exec-path (append exec-path '("/usr/local/bin")))
(use-package exec-path-from-shell
  :init
  (exec-path-from-shell-initialize))
;; (setq exec-path (cons (expand-file-name (concat *node-dir* "sass")) exec-path))
(setq exec-path (append exec-path '("/usr/local/git/bin")))
(setq exec-path (append exec-path (list (concat *node-dir* "bin/"))))
;; (setq exec-path (append exec-path (list *deno-dir*)))
;; (setq exec-path (cons (expand-file-name (concat *node-dir* "sass")) exec-path))
;; (setenv "PATH" (concat (getenv "PATH") ":/usr/local/git/bin"))
(setenv "PATH" (concat (getenv "PATH") (concat ":" *node-dir* "bin/")))

Opinionated Configurations

Setup a prefix for my custom commands.

(progn (define-prefix-command 'jrm-key-map))
(global-set-key (kbd "C-x C-j") jrm-key-map)

General Configuration

Basic Emacs Settings Preferences

I prefer emacs to just ask y/n not yes/no

(fset 'yes-or-no-p 'y-or-n-p)

When killing a buffer always pick the current buffer by default

(defun kill-current-buffer ()
  "Kills the current buffer."
  (interactive)
  (kill-buffer (current-buffer)))
(global-set-key (kbd "C-x k") 'kill-current-buffer)

Prevent async shell command buffers from popping-up:

(add-to-list 'display-buffer-alist
  '("\\*Async Shell Command\\*.*" display-buffer-no-window))

Fix emacs’ regex

(setq-default pcre-mode t)

Use aspell for Mac (aspell can be installed with brew)

(setq ispell-program-name "/usr/local/bin/aspell")

Silence alarms

(setq ring-bell-function 'ignore)
(save-place-mode 1)

Dired

Some basic Dired setup

(global-auto-revert-mode 1)
(setq global-auto-revert-non-file-buffers t)
(setq auto-revert-verbose nil)
(setq dired-listing-switches "-alh")

It’s nice to be able to tab through directories in dired, and I’m a sucker for eye candy with icons

(defun jrm/dired-subtree-toggle-and-refresh ()
  "Calls dired toggle and refreshes the buffer."
  (interactive)
  (dired-subtree-toggle)
  (revert-buffer))

(use-package dired-subtree
  :after dired
  :config
  (bind-key "<tab>" #'jrm/dired-subtree-toggle-and-refresh dired-mode-map)
  (bind-key "<backtab>" #'dired-subtree-cycle dired-mode-map))

(use-package all-the-icons-dired)
(add-hook 'dired-mode-hook 'all-the-icons-dired-mode)

Allow uncompressing zip files

(eval-after-load "dired-aux"
   '(add-to-list 'dired-compress-file-suffixes 
		   '("\\.zip\\'" ".zip" "unzip")))

Tramp

(use-package tramp :config (setq tramp-default-method "scp"))

RipGrep

Use ripgrep by default

(use-package rg
  :config
  (rg-define-search work
    :flags ("--hidden -g '!e2e/'")))

IBuffer

	(global-set-key (kbd "C-x C-b") 'ibuffer)
	(setq ibuffer-saved-filter-groups
	(quote (("default"
		 ("dired" (mode . dired-mode))
		 ("org" (mode . org-mode))
		 ("shell" (mode . shell-mode))
		 ("git" (name . "^magit\*"))
		 ("Slack" (or (mode . slack-mode)
						(name . "^\\*Slack.*$")))
		 ("email" (name . "^\\*mu4e-.*\\*$"))
		 ("ecmascript" (or (mode . javascript-mode)
					 (name . "^.*.js$")
					 (name . "^.*.ts")
					 (name . "^.*.json$")))
		 ("markup" (or (mode . web-mode)
						 (name . "^.*.tpl")
						 (name . "^.*.mst")
						 (name . "^.*.html")))
		 ("images" (name . "^.*png$"))
		 ("process" (or (mode . grep-mode)
				(name . "^\\*tramp*$")))
		 ("emacs" (or (name . "^\\*scratch\\*$")
						(name . "^\\*Messages\\*$")
						(name . "^\\*eww\\*$")
						(name . "^\\*GNU Emacs\\*$")))))))
	(add-hook 'ibuffer-mode-hook (lambda () (ibuffer-switch-to-saved-filter-groups "default")))

GPG Pinentry

Instead of using the display’s popup, prompt for gpg creds in the minibuffer

(setq epa-pinentry-mode 'loopback)

Introspection

Ivy/Counsel/Swiper

Generic auto-complete with Ivy which

(use-package ivy :demand
  :diminish ivy-mode
  :config
  (setq ivy-use-virtual-buffers t
	   ivy-count-format "%d/%d ")
  (global-set-key (kbd "C-x b") 'ivy-switch-buffer))
(ivy-mode 1)
(setq ivy-use-selectable-prompt t)

(use-package ivy-prescient
  :config (ivy-prescient-mode))

Ivy enhanced search (swiper) and common Emacs meta commands (counsel)

(use-package counsel
  :config
  (global-set-key (kbd "M-x") 'counsel-M-x)
  (global-set-key (kbd "C-M-SPC") 'counsel-git))

(use-package swiper
  :config
  (global-set-key (kbd "C-s") 'swiper-isearch))

Which key

Some quick help for when I get stuck in the middle of a command

(use-package which-key :config (which-key-mode))

File Editing/Navigation

General Settings

Keep temporary and backup buffers out of current directory like a civilized human being.

(custom-set-variables
 '(auto-save-file-name-transforms '((".*" "~/.emacs.d/autosaves/\\1" t)))
 '(backup-directory-alist '((".*" . "~/.emacs.d/backups/")))
 '(delete-old-versions t))

(make-directory "~/.emacs.d/autosaves/" t)
(setq create-lockfiles nil)

Replace region with next keystroke.

(delete-selection-mode 1)

Disable bidirectional editing for performance issues when opening large files.

(setq bidi-paragraph-direction 'left-to-right)

Yasnippet

(use-package yasnippet
  :init (setq yas-snippet-dirs
              '("~/src/dotemacs/snippets"))
  :config (yas-global-mode))

In-file Navigation

Easier paragraph jumping

(global-set-key (kbd "M-p") 'backward-paragraph)
(global-set-key (kbd "M-n") 'forward-paragraph)

Avy is great for speed-of-thought navigation

(use-package avy)
(global-set-key (kbd "M-s") 'avy-goto-char-timer)
(global-set-key (kbd "C-c SPC") 'avy-goto-line)

Turn on linum mode for almost everything.

(global-set-key (kbd "C-c l l") 'display-line-numbers-mode)

Adjust the local mark ring pop key sequence, so after pressing `C-u C-SPC`, you can just press `C-SPC` to keep jumping.

(setq set-mark-command-repeat-pop t)

Programming

Setup basic editorconfig plugin for closer integration with other tools

(use-package editorconfig
  :ensure t
  :config
  (editorconfig-mode 1))

LSP

(use-package lsp-mode
  :hook (typescript-mode . lsp)
  :hook (javascript-mode . lsp)
  :hook (js2-mode . lsp)
  :hook (html-mode . lsp)
  :hook (scss-mode . lsp)
  :hook (sass-mode . lsp)
  :hook (css-mode . lsp)
  :hook (web-mode . lsp)
  :hook (clojure-mode . lsp)
  :commands lsp
  :bind (("M-." . lsp-find-definition))
  :bind (("M-n" . forward-paragraph))
  :bind (("M-p" . backward-paragraph))
  :config
  (dolist (m '(clojure-mode
		 clojurec-mode
		 clojurescript-mode
		 clojurex-mode))
    (add-to-list 'lsp-language-id-configuration `(,m . "clojure")))
  :config
  (with-eval-after-load 'lsp-mode
    (add-to-list 'lsp-file-watch-ignored-directories "[/\\\\]\\test\\'")))


;; optionally
(use-package lsp-ui :commands lsp-ui-mode)
(use-package helm-lsp :commands helm-lsp-workspace-symbol)
(use-package lsp-treemacs :commands lsp-treemacs-errors-list)
;; optionally if you want to use debugger
(use-package dap-mode)
;; (global-set-key (kbd "M-p") 'backward-paragraph)
(define-key lsp-signature-mode-map (kbd "M-p") 'backward-paragraph)
(define-key lsp-signature-mode-map (kbd "M-n") 'forward-paragraph)
;; (global-set-key (kbd "M-n") 'forward-paragraph)
LSP Variables
(setq lsp-eslint-unzipped-path (concat *node-dir* "bin"))

Lsp Mode Performance adjustments (see https://emacs-lsp.github.io/lsp-mode/page/performance/).

;; (setq gc-cons-threshold 100000000)
;; (setq read-process-output-max 3000000) ;; ~3mb
(setq lsp-idle-delay 1)
(setq lsp-html-server-command (quote ((concat *node-dir* "bin/html-languageserver") "--stdio")))

(setq lsp-clients-angular-language-server-command (quote
                                                   ("node"
                                                    "/home/jrm/.nvm/versions/node/v16.13.2/lib/node_modules/@angular/language-server"
                                                    "--ngProbeLocations"
                                                    "/home/jrm/.nvm/versions/node/v16.13.2/lib/node_modules"
                                                    "--tsProbeLocations"
                                                    "/home/jrm/.nvm/versions/node/v16.13.2/lib/node_modules"
                                                    "--stdio")))
LSP Utility functions
(defun jrm/lsp-clear-blacklist () "Clears the blacklist folders for LSP Mode"
       (interactive)
       (setf (lsp-session-folders-blacklist (lsp-session)) nil)
       (lsp--persist-session (lsp-session)))

LSP + DAP

DAP Chrome

Dap is used for debugging in browser(s). More information at: https://emacs-lsp.github.io/lsp-mode/tutorials/reactjs-tutorial/

;; (require 'dap-chrome)
DAP NodeJS
(require 'dap-node)
(defun jrm/dap-node-stop-all ()
  "Kill all background node processes running in inspect"
  (interactive)
  (dap-delete-all-sessions)
  (async-shell-command "kill `ps -A | grep 'inspect-brk' | awk '{print $1}'`"))

Lisps

Paredit for maintaining sanity while working with lisp

(defun paredit-enable-modes () (add-hook 'emacs-lisp-mode-hook 'paredit-mode))

(use-package paredit :config (paredit-enable-modes))

Some general settings for lisp dialects (elisp, clojure, etc).

(autoload 'enable-paredit-mode "paredit" "Turn on pseudo-structural editing of Lisp code." t)
(add-hook 'emacs-lisp-mode-hook       #'enable-paredit-mode)
(add-hook 'eval-expression-minibuffer-setup-hook #'enable-paredit-mode)
(add-hook 'ielm-mode-hook             #'enable-paredit-mode)
(add-hook 'lisp-mode-hook             #'enable-paredit-mode)
(add-hook 'lisp-interaction-mode-hook #'enable-paredit-mode)
(add-hook 'scheme-mode-hook           #'enable-paredit-mode)
(add-hook 'clojure-mode-hook          #'enable-paredit-mode)
elisp

Make evaluating elisp buffers even quicker

(global-set-key (kbd "C-c C-e")  'eval-buffer)
Clojure Development

Clojure with Cider for interactive Clojure development

(use-package clojure-mode
  :defer
  :config
  (add-to-list 'auto-mode-alist '("\\.edn$" . clojure-mode))
  (add-to-list 'auto-mode-alist '("\\.boot$" . clojure-mode))
  (add-to-list 'auto-mode-alist '("\\.cljs.*$" . clojure-mode))
  (add-to-list 'auto-mode-alist '("lein-env" . enh-ruby-mode)))

(use-package eldoc :diminish eldoc-mode)

(use-package cider
  :defer
  :config
  (add-hook 'cider-repl-mode-hook #'eldoc-mode)
  (setq cider-repl-pop-to-buffer-on-connect t) ;; go to the repl when done connecting
  (setq cider-show-error-buffer t)
  (setq cider-auto-select-error-buffer t)) ;; jump to error message
Clojure/Quil Workflow Customization

A popup HSV color picker is helpful for quick prototyping/sketching

(defun convert-range-360 (val)
  "Converts a value from a 0-1 range to 0-360 range. Used for calculating hue."
  (* (/ (- val 0) (- 1 0)) (+ (- 360 0) 0)))

(defun jrm/insert-color-hsb ()
  "Select a color and insert its hue/saturation/brightness[lumenosity] format."
  (interactive "*")
  (let ((buf (current-buffer)))
    (custom-set-variables '(list-colors-sort (quote hsv)))
    (list-colors-display
     nil nil `(lambda (name)
		  (interactive)
		  (quit-window)
		  (with-current-buffer ,buf
		    (setq hsb (apply 'color-rgb-to-hsl (color-name-to-rgb name)))
		    (setq hue (convert-range-360 (nth 0 hsb)))
		    (setq sat (* 100 (nth 1 hsb)))
		    (insert (format "%s" hue 100) " " (format "%s" sat) " " (format "%s" 100.0)))))))
(global-set-key (kbd "C-x C-j H")  'jrm/insert-color-hsb)

ECMAScript

Tern is a require package and can be installed with sudo npm install -g tern

General Settings

Some basic code folding

(use-package yafolding
  :hook ((js-mode . yafolding-mode)
         (js2-mode . yafolding-mode)
         (typescript-mode . yafolding-mode)
         (fundamental-mode . yafolding-mode)))

Use js2 mode rather than the built in javascript mode.

(use-package js2-mode
  :defer
  :init
  (add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))
  (add-to-list 'auto-mode-alist '("\\.mjs\\'" . js2-mode)))

(bind-keys*
 ("M-." . lsp-find-definition))

Setup ECMA unicode glyphs

(defun jrm/ecma-prettify-symbols ()
  "Adds common ECMA symobls to prettify-symbols-alist."
  (push '(">=" . ?≥) prettify-symbols-alist)
  (push '("=>" . ?⇒) prettify-symbols-alist)
  (push '("<=" . ?≤) prettify-symbols-alist)
  (push '("===" . ?≡) prettify-symbols-alist)
  (push '("!=" . ?≠) prettify-symbols-alist)
  (push '("!==" . ?≢) prettify-symbols-alist)
  (push '("&&" . ?∧) prettify-symbols-alist)
  (prettify-symbols-mode))

(add-hook 'js2-mode-hook 'jrm/ecma-prettify-symbols)
(add-hook 'js-mode-hook 'jrm/ecma-prettify-symbols)

Web Beautify for unminifying assets

(use-package web-beautify)
Angular/React/TS Development
(custom-set-variables
 '(flycheck-javascript-eslint-executable (concat *eslint-dir* "eslint.js")))
(use-package flycheck :diminish flycheck-mode)

Enable typescript frameworks for just typescript and prototype

(setq typescript-enabled-frameworks '(typescript prototype))

Rjsx for JSX

(use-package rjsx-mode
  :config (add-to-list 'auto-mode-alist '("src/elfeed-web-react/.*\\.js\\'" . rjsx-mode)))

Add prettier support. Assumes prettier is installed globally.

(defun prettier-before-save ()
  "Add this to .emacs to run refmt on the current buffer when saving:
 (add-hook 'before-save-hook 'prettier-before-save)."
  (interactive)
  (when (member major-mode '(js-mode js2-mode)) (prettier)))
(add-hook 'before-save-hook 'prettier-before-save)

Add ECMA unicode glyphs that I like

(add-hook 'typescript-mode-hook 'jrm/ecma-prettify-symbols)

Quick hack to show/hide import statements for JS/TS

(defcustom jrm/imports-placeholder-content "[imports...]"
  "Text to show in place of a folded block."
  :tag "Ellipsis"
  :type 'string
  :group 'jrmhideimports)

(defvar import-mark-fringe nil)

(defun jrm/add-import-mark-fringe ()
  (interactive)
  (let ((s (make-string 1 ?x)))
	(when import-mark-fringe (delete-overlay import-mark-fringe))
	(setq import-mark-fringe (make-overlay (point) (1+ (point))))
	(put-text-property 0 1 'display '(left-fringe right-triangle) s)
	(overlay-put import-mark-fringe 'before-string s)))


(defun jrm/remove-import-mark-fringe ()
  (interactive)
  (when import-mark-fringe (delete-overlay import-mark-fringe)))


(defun jrm/imports-placeholder ()
  "Return propertized ellipsis content."
  (concat " "
	  (propertize jrm/imports-placeholder-content 'face 'font-lock-comment-face)
	  " "))

(defun jrm/jsts-hide-imports ()
  "Hide standard imports based on regex for standard JS/TS imports of multiple modules"
  (let ((final-location (point)))
    (funcall (lambda () "Use regex to hide the imports"
	       (end-of-buffer)
	       (search-backward-regexp "from[[:space:]\.].*;")
	       (next-line)
	       (set-mark-command nil)
	       (jrm/add-import-mark-fringe)
	       (beginning-of-buffer)

	       (let ((new-overlay (make-overlay (region-beginning) (region-end))))
		 (overlay-put new-overlay 'invisible t)
		 (overlay-put new-overlay 'intangible t)
		 (overlay-put new-overlay 'evaporate t)
		 (overlay-put new-overlay 'before-string (jrm/imports-placeholder))
		 (overlay-put new-overlay 'category "hide-js-imports"))

	       ;; (next-line)
	       ;; (beginning-of-buffer)
	       (goto-char final-location)

	       (pop-mark)
	       ;; (next-line)

	       (message "Imports hidden")))))

(defun jrm/has-import-overlay ()
  "Finds any matching overlays"
  (mapcar (lambda (overlay)
	    (and (member "hide-js-imports" (overlay-properties overlay)) overlay))
	  (overlays-in (point-min) (point-max))))

(defun jrm/jsts-show-imports ()
  "Show module imports"
  (mapcar 'delete-overlay (delq nil (jrm/has-import-overlay)))
  (jrm/remove-import-mark-fringe))

(defun jrm/jsts-toggle-imports ()
  "Show/Hide standard module import code"
  (interactive)
  (if (delq nil (jrm/has-import-overlay))
      (jrm/jsts-show-imports)
    (jrm/jsts-hide-imports)))
Indentation

Defining custom indentation based on project paths and setting them to functions that I can call as needed.

(defun jrm/setup-indent (n)
  (setq indent-tabs-mode nil)
  (setq-local c-basic-offset n)
  (setq-local javascript-indent-level n)
  (setq-local js-indent-level n)
  (setq-local typescript-indent-level n)
  (setq-local web-mode-markup-indent-offset n)
  (setq-local web-mode-css-indent-offset n)
  (setq-local web-mode-code-indent-offset n)
  (setq-local sass-indent-offset n)
  (setq-local css-indent-offset n))

(defun jrm/two-space-code-style ()
  "indent 2 spaces width"
  (interactive)
  (message "Using 2 spaces coding style")
  (jrm/setup-indent 2))

(defun jrm/four-space-code-style ()
  "indent 4 spaces width"
  (interactive)
  (message "Using 4 spaces coding style")
  (jrm/setup-indent 4))
(add-hook 'typescript-mode-hook 'jrm/four-space-code-style)
(add-hook 'lua-mode-hook 'jrm/four-space-code-style)
(add-hook 'web-mode-hook 'jrm/four-space-code-style)
(add-hook 'js-mode-hook 'jrm/four-space-code-style)
(add-hook 'js2-mode-hook 'jrm/four-space-code-style)
(add-hook 'sass-mode-hook 'jrm/four-space-code-style)
(add-hook 'scss-mode-hook 'jrm/four-space-code-style)
(add-hook 'typescript-mode-hook 'jrm/two-space-code-style)
(add-hook 'lua-mode-hook 'jrm/two-space-code-style)
(add-hook 'web-mode-hook 'jrm/two-space-code-style)
(add-hook 'json-mode-hook 'jrm/two-space-code-style)
(add-hook 'js2-mode-hook 'jrm/two-space-code-style)
;; (add-hook 'typescript-mode-hook 'jrm/develop-environment)
;; (add-hook 'lua-mode-hook 'jrm/develop-environment)
;; (add-hook 'web-mode-hook 'jrm/develop-environment)
;; (add-hook 'json-mode-hook 'jrm/neon-code-style)
Lint On Save

HTML/CSS

(use-package sass-mode
  :config
  (add-to-list 'auto-mode-alist '("\\.scss\\'" . scss-mode)))

(use-package web-mode
  :config
  (add-to-list 'auto-mode-alist '("\\.phtml\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.html\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.tpl\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.mst\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.tpl\\.php\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.[agj]sp\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.as[cp]x\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.erb\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.mustache\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.djhtml\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.hbs\\'" . web-mode))
  :custom (web-mode-enable-auto-indentation nil))

Quick utility for converting rems to px using base 16

(defun jrm/rem-to-px () "" (interactive)
       (let (n) (setq n (read-number "Enter REM value: "))
	    (message "%spx" (* 16 n))))

PHP Development

(use-package php-mode
  :defer
  :config
  (autoload 'php-mode "php-mode-improved" "Major mode for editing php code." t)
  (add-to-list 'auto-mode-alist '("\\.php$" . php-mode))
  (add-to-list 'auto-mode-alist '("\\.inc$" . php-mode)))

Additional Languages

Various modes helpful for development

(use-package yaml-mode
  :defer
  :config (add-to-list 'auto-mode-alist '("\\.yml\\'" . yaml-mode)))
(use-package restclient :defer)
(use-package groovy-mode :defer)
(use-package go-mode :defer)
(use-package emmet-mode
  :defer
  :config
  (add-hook 'sgml-mode-hook 'emmet-mode)
  (add-hook 'css-mode-hook 'emmet-mode)
  (add-hook 'web-mode-hook 'emmet-mode)
  (add-hook 'sass-mode-hook 'emmet-mode))

Version Control

Magit for version control

(use-package magit
  :config
  (global-set-key (kbd "C-x g") 'magit-status)
  (add-hook 'magit-status-sections-hook 'magit-insert-stashes))

;; Getting an alist-void error when running magit commands that refresh the buffer. Narrowed down to this variable so turning off for now
(setq magit-section-cache-visibility nil)

Company

(use-package company
  :after lsp-mode
  :hook (prog-mode . company-mode)
  :config
  (setq company-minimum-prefix-length 2)
  (setq company-idle-delay 0.2))

  (global-company-mode)
  (global-set-key (kbd "TAB") #'company-indent-or-complete-common)

;;(setq tab-always-indent 'complete)
(setq company-tooltip-align-annotations t)

(use-package company-box
  :hook (company-mode . company-box-mode))

Theme/UI

General Settings

(defun jrm/modus-operandi_extra-adjustments (theme)
  "Updates additional colors and such based on the current modus theme"
  (let ((isOperandi (string-equal theme "operandi")))
    (if isOperandi
        (custom-set-faces
         '(org-block ((t (:inherit shadow :extend t :background "gray83"))))
         '(org-block-begin-line ((t (:extend t :background "gray95" :foreground "gray59" :height 0.9))))
         '(org-block-end-line ((t (:extend t :background "gray95" :foreground "gray59" :height 0.9)))))
      (custom-set-faces
       '(org-block ((t (:inherit shadow :extend t :background "gray20"))))
       '(org-block-begin-line ((t (:extend t :background "gray11" :foreground "dim gray" :height 0.9))))
       '(org-block-end-line ((t (:extend t :background "gray11" :foreground "dim gray" :height 0.9))))))
    (if isOperandi
        (setq dashboard-startup-banner (concat *dotemacs-dir* "assets/Lambda_light.png"))
      (setq dashboard-startup-banner (concat *dotemacs-dir* "assets/Lambda_dark.png")))
;
; (if isOperandi
;; 	(set-face-background hl-line-face "LightSteelBlue1")
;;       (set-face-background hl-line-face "#040e17"))

    ))

(defun jrm/modus-themes-toggle () ""
       (interactive)
       (pcase (modus-themes--current-theme)
         ('modus-operandi (jrm/modus-operandi_extra-adjustments "operandi"))
         ('modus-vivendi (jrm/modus-operandi_extra-adjustments "vivendi"))
         (_ (message "No modus theme enabled"))))


(use-package modus-themes
  :ensure
  :init
  ;; Add all your customizations prior to loading the themes
  (setq modus-themes-italic-constructs nil
        modus-themes-bold-constructs t
        modus-themes-mode-line '(borderless)
        modus-themes-paren-match '(bold intense underline)
        modus-themes-region '(bg-only))

  ;; Load the theme files before enabling a theme
  ;; (modus-themes-load-themes)

  ;; Enable personal customizations after loading a modus theme
  (add-hook 'modus-themes-after-load-theme-hook 'jrm/modus-themes-toggle)

  :config
  ;; Load the theme of your choice:
  (load-theme 'modus-operandi :no-confirm) ;; (load-theme 'modus-vivendi)
  :bind ("<f5>" . modus-themes-toggle))

Remove default scrollbars

(scroll-bar-mode -1)

Hide the default toolbars

(menu-bar-mode -1)
(tool-bar-mode -1)

I prefer to see trailing whitespace but not for every mode (e.g. org, elfeed, etc)

(use-package whitespace
  :config
  (setq-default show-trailing-whitespace t)
  (defun no-trailing-whitespace ()
    (setq show-trailing-whitespace nil))
  (add-hook 'minibuffer-setup-hook              'no-trailing-whitespace)
  (add-hook 'dashboard-mode-hook                'no-trailing-whitespace)
  (add-hook 'eww-mode-hook                      'no-trailing-whitespace)
  (add-hook 'vterm-mode-hook                    'no-trailing-whitespace)
  (add-hook 'shell-mode-hook                    'no-trailing-whitespace)
  (add-hook 'mu4e:view-mode-hook                'no-trailing-whitespace)
  (add-hook 'eshell-mode-hook                   'no-trailing-whitespace)
  (add-hook 'help-mode-hook                     'no-trailing-whitespace)
  (add-hook 'term-mode-hook                     'no-trailing-whitespace)
  (add-hook 'slack-message-buffer-mode-hook     'no-trailing-whitespace)
  (add-hook 'mu4e:view-mode-hook                'no-trailing-whitespace)
  (add-hook 'calendar-mode-hook                 'no-trailing-whitespace))

Use visual line mode for text wrapping

(global-visual-line-mode t)

Custom Colors

Shells

;; function to switch background color
(defun buffer-background-switch ()
  (interactive)
  (setq buffer-face-mode-face `(:background "#0a1310" :foreground "#218352"))
  (custom-set-faces '(comint-highlight-prompt ((t (:inherit minibuffer-prompt :foreground "#2cc46c")))))
  (buffer-face-mode 1))

(add-hook 'shell-mode-hook 'buffer-background-switch)
(add-hook 'eshell-mode-hook 'buffer-background-switch)

Org Mode

Set Org mode source block background color to dark gray so it stands out from the typical background

(custom-set-faces '(org-block ((t (:inherit shadow :background "gray83")))))

Org Tables

I commonly use org for db management so adding a quick way to shrink tables

(defun jrm/set-org-table-column-widths ()
  "This adds a row after the current Org Table row with a width cookie for each column"
  (interactive)
  (let ((new-width (read-string "Set width to: ")))
    (beginning-of-line)
    (set-mark-command nil)
    (end-of-line)
    (kill-ring-save (region-beginning) (region-end))
    (org-return)
    (org-yank)
    (beginning-of-line)
    (set-mark-command nil)
    (end-of-line)
    (save-restriction
      (narrow-to-region (region-beginning) (region-end))
      (goto-char (point-min))
      (while (search-forward-regexp "|[[:space:]][-_.A-Za-z0-9]+[[:space:]]" nil t)
        (replace-match (concat "| <" new-width "> "))))
    (org-table-shrink)
    (beginning-of-line)))

Dashboard

I like a nice big splash screen.

(use-package dashboard
  :config
  (dashboard-setup-startup-hook)
  (setq dashboard-startup-banner (concat *dotemacs-dir* "assets/Lambda_light.png"))
  (setq dashboard-items '((recents  . 10)))
  (setq dashboard-banner-logo-title ""))

Highlight line

Helpful for finding the cursor when jumping around

(global-hl-line-mode +1)
(set-face-background hl-line-face "LightSteelBlue1")

Ivy Posframe (Not used)

     (use-package ivy-posframe
	:config
	(setq ivy-posframe-display-functions-alist
	      '((swiper          . ivy-posframe-display-at-frame-bottom-left)
		(complete-symbol . ivy-posframe-display-at-point)
		(counsel-M-x     . ivy-posframe-display-at-frame-center)
		(t               . ivy-posframe-display)))
	(ivy-posframe-mode 0)
	(custom-set-faces '(ivy-posframe ((t (:inherit default :background "black"))))))

Modeline

Use the spaceline from spacemacs

(use-package spaceline
  :config
  (require 'spaceline-config)
  (setq powerline-default-separator (quote wave))
  (spaceline-spacemacs-theme)
  (setq powerline-height 20)
  (set-face-attribute 'mode-line nil :box nil)
  (set-face-attribute 'mode-line-inactive nil :box nil))

Show spaceline icons

  (use-package spaceline-all-the-icons
    :after spaceline
    :config (spaceline-all-the-icons-theme))
(custom-set-variables
 '(spaceline-all-the-icons-separator-type (quote arrow)))

Minibuffer

Display the current time and battery indicator

(setq display-time-24hr-format t)
(setq display-time-format "%H:%M - %d.%b.%y")
(display-time-mode 1)
(display-battery-mode 1)

Frames

(add-to-list 'default-frame-alist '(ns-transparent-titlebar . t))
(add-to-list 'default-frame-alist '(ns-appearance . dark))

;; Autohide the top panel if necessary
(setq ns-auto-hide-menu-bar t)
(toggle-frame-maximized)

(set-face-attribute 'default nil :height 120)
(global-set-key (kbd "<f9>") 'other-frame)

Symbols

Show symbols by default

(global-prettify-symbols-mode 1)

Manage Window

Go fullscreen and set the default font size.

(set-frame-parameter nil 'fullscreen 'fullboth)
(set-face-attribute 'default nil :height 140)
(set-face-attribute 'default nil :font "Inconsolata-14")
(set-face-attribute 'default nil :font "Inconsolata-18")

Org Mode

Load some basic minor modes by default

(add-hook 'org-mode-hook 'no-trailing-whitespace)
(add-hook 'org-mode-hook 'flyspell-mode)
(setq org-hide-emphasis-markers t)
;; Copy the visible text (without formatting marks) by default
;; (define-key org-mode-map (kbd "M-w") 'org-copy-visible)
;; (define-key org-mode-map (kbd "M-W") 'kill-ring-save)

General Styling

Fonts and variable pitch mode for alignment issues with non-monospaced fonts. Variable pitch will use roboto, but override elements that need fixed-pitch (i.e. org-table) with a font that is monospaced (i.e. a font that is already fixed-pitch)

(add-hook 'org-mode-hook (lambda () (variable-pitch-mode t)))
(add-hook 'org-mode-hook (lambda () (set-face-attribute 'org-table nil :inherit 'fixed-pitch)))
(add-hook 'org-mode-hook (lambda () (set-face-attribute 'org-block nil :inherit 'fixed-pitch :height 0.8)))
(custom-set-faces
 '(variable-pitch ((t (:family "Roboto"))))) ;; make sure this font is installed
(add-hook 'org-mode-hook (lambda () (set-face-attribute 'org-date nil :inherit 'fixed-pitch)))

Show the asterisks as bullets and set up indentation

(use-package org-bullets :config (add-hook 'org-mode-hook (lambda () (org-bullets-mode))))
(add-hook 'org-mode-hook 'org-indent-mode)

Show lists with a bullet rather than the - character.

(font-lock-add-keywords 'org-mode
                        '(("^ *\\([-]\\) "
                           (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) ""))))))

Navigation

Setup an easy way to jump to an org headline using org-goto C-c C-j

 (setq org-goto-interface 'outline-path-completion
	org-goto-max-level 10)

 (setq org-outline-path-complete-in-steps nil)
(global-set-key (kbd "C-o") 'other-window)
(define-key dired-mode-map (kbd "C-o") 'other-window)
(define-key rg-mode-map (kbd "C-o") 'other-window)
(define-key grep-mode-map (kbd "C-o") 'other-window)
;; (define-key bookmark-bmenu-mode-map (kbd "C-o") 'other-window)

Source Blocks

When evaluating a source code block in org mode do not prompt for input, just run it.

(setq org-confirm-babel-evaluate nil)

Stylistic preferences for using the pre-v9 version of org mode (E.g. easy templates and how to split the source window when editing, make the source blocks full width.)

  (require 'org-tempo)
  (setq org-src-window-setup 'other-window)

  (custom-set-faces
   '(org-block ((t (:inherit shadow :extend t :background "gray83"))))
   '(org-block-begin-line ((t (:extend t :background "gray95" :foreground "gray59" 
:height 0.9))))
   '(org-block-end-line ((t (:extend t :background "gray95" :foreground "gray59" :height 0.9)))))

Set the node environment

(setq org-babel-js-cmd (concat *node-dir* "bin/node"))

Source Block Shortcuts

(add-to-list
 'org-structure-template-alist
 '("r" . "src restclient"))
(add-to-list
 'org-structure-template-alist
 '("js" . "src js"))
(add-to-list
 'org-structure-template-alist
 '("ts" . "src typescript"))
(add-to-list
 'org-structure-template-alist
 '("el" . "src emacs-lisp"))
(add-to-list
 'org-structure-template-alist
 '("b" . "src bash"))
(add-to-list
 'org-structure-template-alist
 '("elt" . "src emacs-lisp :tangle ~/.emacs"))

(add-to-list 'org-tempo-keywords-alist '("n" . "name"))

Additional Modes

Add some export modes for getting content out of org. Adding diminish to ob-clojure throws a Wrong type argument: stringp, :defer error.

(use-package ox-twbs :defer)
(use-package ob-rust :defer)
(use-package ob-restclient)
(require 'ob-clojure)
(use-package ob-typescript :diminish typescript-mode)

Allow asynchronous execution of org-babel src blocks so you can keep using emacs during long running scripts

(use-package ob-async)

Load some languages by default

(add-to-list 'org-src-lang-modes '("js" . "javascript")
	       '("php" . "php"))
(org-babel-do-load-languages
 'org-babel-load-languages
 '((python . t)
   (js . t)
   (lisp . t)
   (clojure . t)
   (typescript . t)
   (rust . t)
   (sql . t)
   (shell . t)
   (java . t)))

I like org source blocks for typescript to use different compiler settings than what ships with ob-typescript. Not sure if there’s a better way to do this, but just overwriting the function from the source with the code below using the configuration I prefer.

 (defun org-babel-execute:typescript (body params)
   "Execute a block of Typescript code with org-babel. This function is called by `org-babel-execute-src-block'"
   (let* ((tmp-src-file (org-babel-temp-file "ts-src-" ".ts"))
	   (tmp-out-file (org-babel-temp-file "ts-src-" ".js"))
	   (cmdline (cdr (assoc :cmdline params)))
	   (cmdline (if cmdline (concat " " cmdline) ""))
	   (jsexec (if (assoc :wrap params) ""
		     (concat " ; node " (org-babel-process-file-name tmp-out-file)))))
     (with-temp-file tmp-src-file (insert body))
     (let ((results (org-babel-eval (format "tsc %s --lib 'ES7,DOM' -out %s %s %s"
					     cmdline
					     (org-babel-process-file-name tmp-out-file)
					     (org-babel-process-file-name tmp-src-file)
					     jsexec) ""))
	    (jstrans (with-temp-buffer
		       (insert-file-contents tmp-out-file)
		       (buffer-substring-no-properties (point-min) (point-max)))))
	(if (eq jsexec "") jstrans results))))

For org-babel’s clojure backend use cider rather than the default slime

(setq org-babel-clojure-backend 'cider)

Org Capture

Global Org Capture

Simple command to open emacs (assumes it’s already running) and launch org capture in a new frame. This can be bound to a global key sequence.

emacsclient -ne "(make-capture-frame)"
 (server-start)

 (defadvice org-capture-finalize 
     (after delete-capture-frame activate)
   "Advise capture-finalize to close the frame"
   (if (equal "capture" (frame-parameter nil 'name))
	(delete-frame)))

 (defadvice org-capture-destroy
     (after delete-capture-frame activate)
   "Advise capture-destroy to close the frame"
   (if (equal "capture" (frame-parameter nil 'name))
	(delete-frame)))

 (use-package noflet
   :ensure t )
 (defun make-capture-frame ()
   "Create a new frame and run org-capture."
   (interactive)
   (make-frame '((name . "capture")))
   (select-frame-by-name "capture")
   (delete-other-windows)
   (noflet ((switch-to-buffer-other-window (buf) (switch-to-buffer buf)))
     (org-capture)))

Helpful for bridging org and jira.

(use-package ox-jira)

Remote Syncing

This attempts to sync an org file on save if it detects the file is in the *org-dir* directory.

(defun jrm/git-auto-sync ()
  "Automatically stages, commits, pulls, and pushes the current branch's upstream settings. Commit message is current timestamp. Depends on Magit."
  (interactive)
  (if (string-match-p (regexp-quote *org-dir*) (file-name-directory buffer-file-name))
      (progn
        (magit-stage-modified)
        (magit-run-git-with-editor "commit" "-m" (format-time-string "%a %d %b %Y %H:%M:%S %Z"))
        (magit-run-git-async "pull")
        (magit-run-git-async "push"))))

(add-hook 'org-mode-hook (lambda () (add-hook 'after-save-hook 'jrm/git-auto-sync nil t)))

LaTex

Use xelatex for more latex options like fontspec

(setq org-latex-compiler "xelatex")

Show any latex previews by default

(custom-set-variables '(org-startup-with-latex-preview t))

TODOs/Agenda

Setup standard todo keywords

 (setq org-use-fast-todo-selection t)
 (setq org-todo-keywords
	'((sequence "TODO(t!)" "|" "DONE(d!)")
     (sequence "WORKFLOW TODO(w@/!)" "SOON(s@/!)" "|" "SOMEDAY(S@/!)")))
 ;; Custom colors for the keywords
 (setq org-todo-keyword-faces
	'(("TODO" :foreground "red" :weight bold)
     ("DONE" :foreground "forest green" :weight bold)
     ("WORKFLOW TODO" :foreground "#61afef" :weight bold)
     ("SOON" :foreground "#da8548" :weight bold)
     ("SOMEDAY" :foreground "#9963ad" :weight bold)))

File locations for org agenda

(global-set-key (kbd "C-c a") 'org-agenda)


(setq org-agenda-custom-commands
      '(("p" "Personal Week and Task List"
         ((agenda "")
          (alltodo)
          (search "* DONE"))
         ((org-agenda-files '("~/org/personal"))))
        ("j" "Jira Kanban Board"
         ((search ":status:   Open")
          (search ":status:   To Do")
          (search ":status:   Selected for Development")
          (search ":status:   In Development")
          (search ":status:   In Review")
          ;; (search ":status:   Ready for Test")
          ;; (search ":status:   In Test")
          (search ":status:   Ready for Demo"))
         ((org-agenda-files '("~/.org-jira"))))
      
        ("A" "Personal and Work Week and Task List"
         ((agenda "")
          (alltodo)
          (search "* DONE")))))

Org Jira

Org Export

(custom-set-variables
 '(org-export-backends '(ascii html icalendar latex md odt)))

Markdown

(use-package grip-mode
  :ensure-system-package (grip . "pip install grip"))

Org Roam

(use-package org-roam
  :ensure t
  :custom
  (org-roam-directory "~/org-roam")
  (org-roam-complete-everywhere t)
  (org-roam-capture-templates
   '(("d" "default" plain
      "%?"
      :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n")
      :unnarrowed t)
     ("p" "personal" plain
      "%?"
      :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+filetags: :personal:\n")
      :unnarrowed 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)
         ("C-c n I" . org-roam-node-insert-immediate)
         :map org-mode-map
         ("C-M-i" . completion-at-point))
  :config
  (org-roam-setup))


(defun org-roam-node-insert-immediate (arg &rest args)
  (interactive "P")
  (let ((args (push arg args))
        (org-roam-capture-templates (list (append (car org-roam-capture-templates)
                                                  '(:immediate-finish t)))))
    (apply #'org-roam-node-insert args)))

Org Roam UI

(use-package org-roam-ui)

Org Roam Syncing

(defun jrm/git-auto-sync-org-roam ()
  "Automatically stages, commits, pulls, and pushes the current branch's upstream settings. Commit message is current timestamp. Depends on Magit."
  (interactive)
  (if (string-match-p (regexp-quote *org-roam-dir*) (file-name-directory buffer-file-name))
      (progn
        (magit-stage-untracked)
        (magit-stage-modified)
        (magit-run-git-with-editor "commit" "-m" (format-time-string "%a %d %b %Y %H:%M:%S %Z"))
        (magit-run-git-async "pull")
        (magit-run-git-async "push"))))

(add-hook 'org-mode-hook (lambda () (add-hook 'after-save-hook 'jrm/git-auto-sync-org-roam nil t)))

Shells

By default just use bash for all shells

(defvar my-term-shell "/bin/bash")
(defadvice ansi-term (before force-bash)
  (interactive (list my-term-shell)))
(ad-activate 'ansi-term)

Make shells interactive (i.e. M-!, or source blocks in org)

(setq shell-command-switch "-c")

Vterm

Make sure to unbind f9 for swtiching between frames

(use-package vterm)
;; for some reason, using a setq or the :config or :custom keyword in
;; use-package does not set the variable correctly for
;; vterm. Resorting to custom-set-variables.
(custom-set-variables
 '(vterm-keymap-exceptions (push "<f9>" vterm-keymap-exceptions))
 '(vterm-keymap-exceptions (push "C-o" vterm-keymap-exceptions)))
(define-key vterm-mode-map (kbd "C-q") #'vterm-send-next-key)

Consuming Content

Elfeed

Many thanks to pragmatic emacs’ post for guidance on this setup.

(use-package elfeed-org
  :config (elfeed-org) (setq rmh-elfeed-org-files (list (concat *org-dir* "personal/elfeed.org"))))

(defun jrm/elfeed-show-all ()
  (interactive)
  (bookmark-maybe-load-default-file)
  (bookmark-jump "elfeed-all"))
(defun jrm/elfeed-show-development ()
  (interactive)
  (bookmark-maybe-load-default-file)
  (bookmark-jump "elfeed-development"))
(defun jrm/elfeed-show-news ()
  (interactive)
  (bookmark-maybe-load-default-file)
  (bookmark-jump "elfeed-news"))
(defun jrm/elfeed-show-emacs ()
  (interactive)
  (bookmark-maybe-load-default-file)
  (bookmark-jump "elfeed-emacs"))
(defun jrm/elfeed-show-general ()
  (interactive)
  (bookmark-maybe-load-default-file)
  (bookmark-jump "elfeed-general"))

(defun jrm/elfeed-load-db-and-open ()
  "Wrapper to load the elfeed db from disk before opening"
  (interactive)
  (elfeed-db-load)
  (elfeed)
  (elfeed-search-update--force))

(defun jrm/elfeed-save-db-and-bury ()
  "Wrapper to save the elfeed db to disk before burying buffer"
  (interactive)
  (elfeed-db-save)
  (quit-window))

(use-package elfeed
  :defer
  :bind (:map elfeed-search-mode-map
              ("A" . jrm/elfeed-show-all)
              ("E" . jrm/elfeed-show-emacs)
              ("D" . jrm/elfeed-show-development)
              ("R" . jrm/elfeed-show-general)
              ("N" . jrm/elfeed-show-news)
              ("q" . jrm/elfeed-save-db-and-bury)))

(global-set-key (kbd "C-x e") 'jrm/elfeed-load-db-and-open)

Sometimes it’s helpful to hide images for certain posts.

(defun jrm/elfeed-show-hide-images ()
  (interactive)
  (let ((shr-inhibit-images t))
    (elfeed-show-refresh)))
(global-set-key (kbd "C-x C-j e") 'jrm/elfeed-show-hide-images)

Helpful Utility Functions and settings

Most of the functions in this section are bound to C-x C-j prefix key.

Copy Entire Buffer easily

(defun jrm/copy-all ()
  "Copy the current buffer without loosing your place"
  (interactive)
  (let ((original-position (point)))
    (mark-whole-buffer)
    (kill-ring-save 0 0 t)
    (goto-char original-position)
    (message "Buffer contents yanked.")))
(global-set-key (kbd "C-x C-j C-c") 'jrm/copy-all)

Quickly Change Font Sizes

I find myself need specific font sizes for different scenarios, i.e. projecting, screen-sharing on conference calls, etc. So, binding these to a quick way to toggle through them.

Note: there might be a better way to handle this but things like M-+/M– won’t zoom things like line numbers, etc.

(defvar jrm/screens-alist '((?0 "xsmall" (lambda () (set-face-attribute 'default nil :height 70) 'default))
                            (?1 "small" (lambda () (set-face-attribute 'default nil :height 110) 'default))
                            (?2 "medium" (lambda () (set-face-attribute 'default nil :height 120) 'proj))
                            (?3 "large" (lambda () (set-face-attribute 'default nil :height 140) 'proj))
                            (?4 "xtra-large" (lambda () (set-face-attribute 'default nil :height 160) 'projLg))
                            (?5 "xxtra-large" (lambda () (set-face-attribute 'default nil :height 190) 'projLg))
                            (?6 "xxxtra-large" (lambda () (set-face-attribute 'default nil :height 210) 'projLg)))
  "List that associates number letters to descriptions and actions.")
(defun jrm/adjust-font-size ()
  "Lets the user choose the the font size and takes the corresponding action.
Returns whatever the action returns."
  (interactive)
  (let ((choice (read-char-choice
                 (mapconcat (lambda (item) (format "%c: %s" (car item) (cadr item)))
                            jrm/screens-alist "; ")
                 (mapcar #'car jrm/screens-alist))))
    (funcall (nth 2 (assoc choice jrm/screens-alist)))))
(global-set-key (kbd "C-x C-j p")  'jrm/adjust-font-size)

Copy current file path

Lifted from (http://ergoemacs.org/emacs/emacs_copy_file_path.html)

(defun jrm/copy-file-path (&optional *dir-path-only-p)
  "Copy the current buffer's file path or dired path to `kill-ring'.
Result is full path."
  (interactive "P")
  (let ((-fpath
	   (if (equal major-mode 'dired-mode)
	       (expand-file-name default-directory)
	     (if (buffer-file-name)
		 (buffer-file-name)
	       (user-error "Current buffer is not associated with a file.")))))
    (kill-new
     (if *dir-path-only-p
	   (progn
	     (message "Directory path copied: 「%s" (file-name-directory -fpath))
	     (file-name-directory -fpath))
	 (progn (message "File path copied: 「%s" -fpath) -fpath )))))

Async Shell Command

(defun jrm/async-callback-run-callback (process signal cb)
  (interactive)
  (when (memq (process-status process) '(exit signal))
    (cb)
    (shell-command-sentinel process signal)))

(defun jrm/async-callback (cmd cb)
  (let* ((output-buffer (generate-new-buffer "*Custom Shell Command*"))
         (proc (progn
                 (async-shell-command cmd output-buffer)
                 (get-buffer-process output-buffer))))
    (if (process-live-p proc)
        (set-process-sentinel proc cb #'jrm/async-callback-run-callback)
      (message "No process running."))))
(defun jrm/wifi-restart-ubuntu ()
  "Restart wifi on ubuntu & derivities using network manager."
  (interactive)
  (shell-command (concat "echo " (shell-quote-argument (read-passwd "Enter Password: "))
			 " | sudo -S service network-manager restart")))

DWIM Narrow

The following narrow was lifted from Protesilaos Stavrou blog/video: https://protesilaos.com/codelog/2021-07-24-emacs-misc-custom-commands/

(defun prot-common-window-bounds ()
  "Determine start and end points in the window."
  (list (window-start) (window-end)))
;;;###autoload
(defun prot-simple-narrow-visible-window ()
  "Narrow buffer to wisible window area.
Also check `prot-simple-narrow-dwim'."
  (interactive)
  (let* ((bounds (prot-common-window-bounds))
         (window-area (- (cadr bounds) (car bounds)))
         (buffer-area (- (point-max) (point-min))))
    (if (/= buffer-area window-area)
        (narrow-to-region (car bounds) (cadr bounds))
      (user-error "Buffer fits in the window; won't narrow"))))
;;;###autoload
(defun prot-simple-narrow-dwim ()
  "Do-what-I-mean narrowing.
If region is active, narrow the buffer to the region's
boundaries.
If no region is active, narrow to the visible portion of the
window.
If narrowing is in effect, widen the view."
  (interactive)
  (unless mark-ring                  ; needed when entering a new buffer
    (push-mark (point) t nil))
  (cond
   ((and (use-region-p)
         (null (buffer-narrowed-p)))
    (let ((beg (region-beginning))
          (end (region-end)))
      (narrow-to-region beg end)))
   ((null (buffer-narrowed-p))
    (prot-simple-narrow-visible-window))
   (t
    (widen)
    (recenter))))
(global-set-key (kbd "C-x n n") 'prot-simple-narrow-dwim)

Disable the narrow-to-region message

(put 'narrow-to-region 'disabled nil)

DWIM Copy & Comment

(defun jrm/kill-and-comment-dwim (arg)
  "Takes the active region duplicates it and comments
out the top copy. If no region is selected, the region selected with
mark-paragraph is used.

With a universal prefix argument, do not paste the content but saves
it to the kill ring."
  (interactive "P")
  (if (not mark-active) (mark-paragraph))
  (let ((start (region-beginning))
        (end (region-end)))
    (kill-ring-save start end)
    (comment-region start end)
    (if (not (bound-and-true-p arg))
      (progn
        (goto-char end)
        (end-of-line)
        (insert "\n")
        (yank)

        ;; Delete evidence
        (pop kill-ring)
        (when kill-ring-yank-pointer
          (setq kill-ring-yank-pointer kill-ring))))))

(global-set-key (kbd "M-RET") 'jrm/kill-and-comment-dwim)

Grep customizations

For various reasons ripgrep does not work with all the projects I need so customizing grep to my liking

(setq grep-find-ignored-directories (quote ("SCCS" "RCS" "CVS" "MCVS" ".src" ".svn" ".git" ".hg" ".bzr" "_MTN" "_darcs" "{arch}" "node_modules" "vendor" "dist" "coverage")))

Sudo current buffer

Thank you mastering emacs!

(defun sudo ()
  "Use TRAMP to `sudo' the current buffer."
  (interactive)
  (when buffer-file-name
    (find-alternate-file
     (concat "/sudo:root@localhost:"
             buffer-file-name))))

Final Pieces

Remap Key sequences

(global-set-key (kbd "s-u") '(lambda () (interactive) (revert-buffer t (not (buffer-modified-p)) t)))

Last Line

(provide '.emacs)

TODOs

Automatically put jrm-variables in ~/.emacs.d

About

My personal emacs configuration

License:GNU General Public License v3.0


Languages

Language:YASnippet 81.3%Language:Emacs Lisp 18.7%