Opinionated emacs config centered around general web development (primarily for angular at this time), minimal lisp development, and documentation. This is a literate config so simply tangle this file and any additional org files in this repo you might like to use. The additional org files include functionality like my personal keybindings and common snippets for template completion, etc.
This config requires Emacs >= 29 compiled with treesitter. It is possible to run this config on older Emacs versions, but you may need to turn off certain blocks (i.e. treesitter) to get it to work correctly. For instance, I’m running this config on an Android phone via termux for Emacs 28.3 and it works great but did require turning off certain features via trial-and-error.
Note, this config will attempt to install language grammers for treesitter as you need them, so no need to install those manually.
To use this project:
- clone this repo
- install dependencies
- update readme in Basic Setup for your personal binary paths and such
- Tangle the readme. To tangle the file, open it in emacs, then run
C-c C-v t
. - Tangle any other org files in this repo that you would like to use
To omit a block from being tangled, just set the src header to include :tangle no
. More info on header arguments.
- The modeline does not update to doom-modeline on initial install. Workaround: close and reopen emacs and the modeline should show up and work properly.
- ripgrep (rg) for better grep
- node for JS cli, recommend (but not require) nvm for managing versions
- Typescript server. Make sure it is on your PATH
- Angular Language Server
- eslint:
npm i -g eslint
- html support:
npm install -g vscode-langservers-extracted
- Roboto and Inconsolata fonts
- Pandoc for converting between org mode and other formats like markdown
- Aspell for spell-checking, can be installed with brew
Update the following variables to match your setup.
(setq jrm/custom-vars
'((path-to-here . "/Users/jeremygooch/src/jeremacs/")
(node . "/Users/jeremygooch/.nvm/versions/node/v19.9.0/")
(html-language-server . "/Users/jeremygooch/.nvm/versions/node/v19.9.0/bin/html-languageserver")
(angular-language-server . "/Users/jeremygooch/.nvm/versions/node/v19.9.0/lib/node_modules/@angular/language-server")
(global-node-modules . "/Users/jeremygooch/.nvm/versions/node/v19.9.0/lib/node_modules")
(name . "Jeremy Gooch")
(email . "jeremy.gooch@gmail.com")))
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)
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
<<basic-setup>>
(setq user-full-name (cdr (assoc 'name jrm/custom-vars))
user-mail-address (cdr (assoc 'name jrm/custom-vars)))
(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)
(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)
(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'
Ensure environment variables inside Emacs look the same as in the standard shell.
(setq exec-path (append exec-path '("/usr/local/bin")))
(use-package exec-path-from-shell
:init
(exec-path-from-shell-initialize))
Set custom exec path for git and node
(setq exec-path (append exec-path '("/usr/local/git/bin")))
(setq exec-path (append exec-path (list (concat (cdr (assoc 'node jrm/custom-vars)) "bin/"))))
Ensure node is on path
(setenv "PATH" (concat (getenv "PATH") (concat ":" (cdr (assoc 'node jrm/custom-vars)) "bin/")))
I prefer emacs to just ask y/n not yes/no
(fset 'yes-or-no-p 'y-or-n-p)
Silence alarms
(setq ring-bell-function 'ignore)
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")
Remove default scrollbars and toolbars.
(scroll-bar-mode -1)
(menu-bar-mode -1)
(tool-bar-mode -1)
Remember where I left off after killing a file
(save-place-mode 1)
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)
When a file changes on disk, automatically reload its buffer silently
(global-auto-revert-mode 1)
(setq global-auto-revert-non-file-buffers t)
(setq auto-revert-verbose nil)
(global-prettify-symbols-mode 1)
Default dired flags (uses ls
style syntax)
(setq dired-listing-switches "-alh")
See child folders without having to open child in a new buffer. Always refresh the buffer on showing a subfolder.
(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))
Look and feel
(use-package all-the-icons-dired
:config (unless (member "all-the-icons" (font-family-list)) (all-the-icons-install-fonts t)))
(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")))
Add a little organization to the default ibuffer view
(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")))
Use ripgrep by default
(use-package rg)
Instead of using the display’s popup, prompt for gpg creds in the minibuffer
(setq epa-pinentry-mode 'loopback)
Generic auto-complete with Ivy (+ counsel swipper).
(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))
Some quick help for when getting stuck in the middle of a command
(use-package which-key :config (which-key-mode))
(use-package yasnippet
:init (setq yas-snippet-dirs '("~/.emacs.d/snippets"))
:config (yas-global-mode))
This config uses the Nord theme port for emacs.
(use-package nord-theme
:config
(load-theme 'nord :no-confirm))
(use-package dashboard
:config
(dashboard-setup-startup-hook)
(setq dashboard-startup-banner (concat (cdr (assoc 'path-to-here jrm/custom-vars)) "/assets/Lambda_transparent.png"))
(setq dashboard-items '((recents . 10)))
(setq dashboard-banner-logo-title ""))
(global-hl-line-mode +1)
Uses doom-modeline for performance reasons. Spaceline is also nice, but the icons cause performance issues when opening emacs (see: domtronn/spaceline-all-the-icons.el#55).
Fortunately, doom-modeline uses nerd icons which don’t suffer from the performance hit and the modeline still looks nice.
(use-package doom-modeline
:hook (after-init . doom-modeline-mode)
:config (unless (member "Symbols Nerd Font Mono" (font-family-list)) (nerd-icons-install-fonts t))
:custom
(doom-modeline-height 25)
(doom-modeline-bar-width 1)
(doom-modeline-icon t)
(doom-modeline-major-mode-icon t)
(doom-modeline-major-mode-color-icon t)
(doom-modeline-buffer-file-name-style 'truncate-upto-project)
(doom-modeline-buffer-state-icon t)
(doom-modeline-buffer-modification-icon t)
(doom-modeline-minor-modes nil)
(doom-modeline-enable-word-count nil)
(doom-modeline-buffer-encoding t)
(doom-modeline-indent-info nil)
(doom-modeline-checker-simple-format t)
(doom-modeline-vcs-max-length 12)
(doom-modeline-env-version t)
(doom-modeline-irc-stylize 'identity)
(doom-modeline-github-timer nil)
(doom-modeline-gnus-timer nil))
(add-hook 'after-init-hook #'doom-modeline-mode)
For the minibuffer show 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)
(set-face-attribute 'default nil :height 140)
(set-face-attribute 'default nil :font "Inconsolata-14")
(set-face-attribute 'default nil :font "Inconsolata-18")
Load some basic minor modes by default
(add-hook 'org-mode-hook 'no-trailing-whitespace)
(add-hook 'org-mode-hook 'flyspell-mode)
(add-hook 'org-mode-hook 'variable-pitch-mode)
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)
Hide formatting characters
(setq org-hide-emphasis-markers t)
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) "•"))))))
(custom-set-faces
'(org-block ((t (:inherit shadow :extend t :background "#202531"))))
'(org-block-begin-line ((t (:extend t :background "#191d27" :foreground "#617776"
:height 0.9))))
'(org-block-end-line ((t (:extend t :background "#191d27" :foreground "#617776" :height 0.9)))))
This is helpful so images can be easily resized inline with org attributes
(setq org-image-actual-width nil)
(require 'ox-md nil t)
Helpful utility using pandoc as the backend for converting markdown back to org mode. Lifted from here.
(defun markdown-convert-buffer-to-org ()
"Convert the current buffer's content from markdown to orgmode format and save it with the current buffer's file name but with .org extension."
(interactive)
(shell-command-on-region (point-min) (point-max)
(format "pandoc -f markdown -t org -o %s"
(concat (file-name-sans-extension (buffer-file-name)) ".org"))))
Org tables formatting depends on fixed pitch fonts, so the follow setting overrides variable pitch just for tables.
(set-face-attribute 'org-table nil :inherit 'fixed-pitch)
Some nice eye candy for code buffers
(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))
(global-prettify-symbols-mode 1)
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)
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 with M-x jrm/adjust-font-size
.
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)))))
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))
(set-frame-parameter nil 'fullscreen 'fullboth)
(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-visual-line-mode t)
Avy is great for speed-of-thought navigation. Only install it when needed.
(use-package avy)
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)
Simple hack (found on redit) to ensure you can expand a heading even if the cursor is on the ellipses after a collapsed heading:
(defun my-org-prepare-expand-heading ()
"Move point to before ellipsis, if after ellipsis."
(when (and (not (org-at-heading-p))
(save-excursion
(org-end-of-line)
(org-at-heading-p)))
(org-end-of-line)))
(add-hook 'org-tab-first-hook #'my-org-prepare-expand-heading)
Keep temporary and backup buffers out of current directory like a civilized editor.
(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)
(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))
(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))
(use-package php-mode
: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)))
(use-package restclient)
(use-package ob-restclient)
(use-package typescript-mode
:hook (typescript-mode . jrm/ecma-prettify-symbols)
:hook (typescript-ts-mode . jrm/ecma-prettify-symbols))
(use-package ob-typescript :diminish typescript-mode)
This config uses lsp-mode instead of eglot for better angular template support out of the box.
(use-package lsp-mode
;; :hook ((typescript-mode . lsp-mode)
;; (typescript-ts-mode . lsp-mode)
;; (javascript-mode . lsp-mode)
;; (js-mode . lsp-mode)
;; (js2-mode . lsp-mode)
;; (html-mode . lsp)
;; (scss-mode . lsp)
;; (sass-mode . lsp)
;; (css-mode . lsp)
;; (web-mode . lsp)
;; (terraform-mode . lsp)
;; (clojure-mode . lsp)
;; (lsp-mode . lsp-enable-which-key-integration))
:hook ((prog-mode . lsp-deferred)
(lsp-mode . lsp-enable-which-key-integration))
:custom
(read-processs-output-max (* 1024 1024))
:commands lsp
:bind (("M-." . lsp-find-definition))
:config (setq lsp-idle-delay 1)
:init
(setq lsp-keymap-prefix "C-c")
(setq lsp-diagnostics-provider :flycheck))
(use-package lsp-ui
:commands lsp-ui-mode
:hook (lsp-mode . lsp-ui-mode))
(use-package helm-lsp :commands helm-lsp-workspace-symbol)
(use-package lsp-treemacs :commands lsp-treemacs-errors-list)
(use-package dap-mode)
(add-to-list 'auto-mode-alist '("\\.js[mx]?\\'" . js2-mode))
;; Clean up the javascript-mode entry from the alist to avoid issues with overriding it
(delete '("\\.js[mx]?\\'" . javascript-mode) auto-mode-alist)
(setq lsp-html-server-command `(,(cdr (assoc 'html-language-server jrm/custom-vars)) "--stdio"))
(setq lsp-clients-angular-language-server-command
`("node"
,(cdr (assoc 'angular-language-server jrm/custom-vars))
"--ngProbeLocations"
,(cdr (assoc 'global-node-modules jrm/custom-vars))
"--tsProbeLocations"
,(cdr (assoc 'global-node-modules jrm/custom-vars))
"--stdio"))
(use-package company
:defer t
: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 company-tooltip-align-annotations t)
(use-package company-box
:hook (company-mode . company-box-mode))
(use-package flycheck
:diminish flycheck-mode
:init
(setq flycheck-javascript-eslint-executable "eslint_d")
:hook (lsp-mode . flycheck-mode)
:custom (flycheck-display-error-delay .3))
Auto install treesitter sources if they’re not present
(use-package treesit-auto
:custom
(treesit-auto-install 'prompt)
:config
(setq treesit-auto-langs '(javascript typescript tsx css html))
(treesit-auto-add-to-auto-mode-alist '(javascript typescript tsx css html))
(global-treesit-auto-mode))
Paredit is pretty much mandatory for me these days when writing in a lisp dialect.
(use-package paredit
:hook ((emacs-lisp-mode . paredit-mode)
(lisp-mode . paredit-mode)
(scheme-mode . paredit-mode)
(clojure-mode . paredit-mode)))
By default, code folding is bound to C-<return>
.
(use-package yafolding
:hook ((js-mode . yafolding-mode)
(js2-mode . yafolding-mode)
(css-mode . yafolding-mode)
(scss-mode . yafolding-mode)
(typescript-mode . yafolding-mode)
(typescript-ts-mode . yafolding-mode)
(fundamental-mode . yafolding-mode)))
(use-package yaml-mode :config (add-to-list 'auto-mode-alist '("\\.yml\\'" . yaml-mode)))
Having a tool like this at your fingertips (without having to switch to another ui/tool/website/whatever) is worth having this installed by default. It’s not bound to any key by default as I don’t use it that often though.
(use-package web-beautify)
Most teams/tools use an editor config in project roots. So to avoid friction with setting indentation size, tabs/spaces, etc based on major-mode or otherwise trying to glean it from the project just look at the editor config.
(use-package editorconfig :config (editorconfig-mode 1))
(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)
Open magit in full screen every time
(setq magit-display-buffer-function
#'magit-display-buffer-fullframe-status-v1)
(setq magit-bury-buffer-function
#'magit-restore-window-configuration)
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))))
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 )))))
Ansi term is a great built in terminal. By default force it to use bash.
(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")
When evaluating a source code block in org mode do not prompt for input, just run it.
(setq org-confirm-babel-evaluate nil)
For org source blocks, I prefer the pre-v9 syntax to expanding source blocks that feels similar to yasnippent. Also, split the window when editing a source block.
(require 'org-tempo)
(setq org-src-window-setup 'other-window)
(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
'("jv" . "src java"))
(add-to-list 'org-tempo-keywords-alist '("n" . "name"))
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)
(use-package ob-rust)
(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)
(groovy . 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))))
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))
I like to keep the magit buffer open when closing all project files
(setq project-kill-buffer-conditions '(buffer-file-name
(and
(major-mode . fundamental-mode)
"\\`[^ ]")
(and
(derived-mode . special-mode)
(not
(major-mode . help-mode))
(not
(major-mode . magit-status-mode))
(not
(derived-mode . gnus-mode)))
(derived-mode . compilation-mode)
(derived-mode . dired-mode)
(derived-mode . diff-mode)
(derived-mode . comint-mode)
(derived-mode . eshell-mode)
(derived-mode . change-log-mode)))
Load additional configs based on file name patterns
(let ((emacs-dir (directory-file-name (file-name-parent-directory user-init-file))))
(dolist (file (directory-files emacs-dir))
(when (string-match "^init\\.[A-Za-z0-9_-]+\\.el$" file)
(load (expand-file-name file emacs-dir)))))
(provide 'emacs)