This is an literate programming init.el
. Read Harry’s brief tutorial on how
this works and how to do it yourself.
After reading the previously hyperlinked tutorial there are a few files and functions to know about.
Define a function to load elisp files if present, then call it immediately for the preamble elisp file and towards the end of this document for the postamble elisp file.
(defun winny/load-file-when-present (file)
"Load FILE when it's present.
This suppresses an error caused by missing file. It only prints
out an error when the file throws an error."
(condition-case err
(load file nil t t)
(file-missing nil) ; Ignore missing optional file
(error (message "Failed to load %s: %s" file (error-message-string err))
nil)))
(winny/load-file-when-present "~/.emacs.d/host-pre-init.el")
See Load =host-post-init.el=.
Try winny/reload-configuration
. (It is defined in =init.el=.)
(defun winny/visit-configuration (&optional other-window)
"Visit configuration.org"
(interactive "P")
(funcall
(if other-window
#'find-file-other-window
#'find-file)
(concat user-emacs-directory "configuration.org")))
From https://wilkesley.org/~ian/xah/emacs/elisp_datetime.html
(defun winny/iso-8601 ()
(concat
(format-time-string "%Y-%m-%dT%T")
((lambda (x) (concat (substring x 0 3) ":" (substring x 3 5)))
(format-time-string "%z"))))
(defun winny/insert-iso-8601 ()
(interactive)
(insert (winny/iso-8601)))
Hardcode some settings that I wish to never change in any Emacs setup.
(setq mode-require-final-newline t
auto-revert-verbose nil
async-shell-command-buffer 'new-buffer
Man-notify-method 'friendly
help-window-select 'other
show-paren-delay 0.0
custom-file "/dev/null" ; XXX Windows
custom-safe-themes t
warning-suppress-types '((comp))
display-time-24hr-format t)
(tool-bar-mode -1)
(tooltip-mode -1)
(when (fboundp 'scroll-bar-mode) (scroll-bar-mode -1))
(menu-bar-mode -1)
(column-number-mode 1)
;; Blink cursor for about ten seconds with a calm 3/4 second interval.
(blink-cursor-mode 1)
(setq blink-cursor-interval 0.75
blink-cursor-blinks (ceiling (/ 10.0 blink-cursor-interval)))
(show-paren-mode 1)
;; Release visual compression and tension.
(setq-default line-spacing .15)
This way packages can be installed from submodules or tracked directly in my git repository.
(add-to-list 'load-path "~/.emacs.d/site-lisp/")
(require 'package)
(add-to-list 'package-archives
'("melpa" . "http://melpa.org/packages/") t)
(when (< emacs-major-version 24)
;; For important compatibility libraries like cl-lib
(add-to-list 'package-archives '("gnu" . "http://elpa.gnu.org/packages/")))
(package-initialize)
(defconst winny/initialized-cookie-path
(concat user-emacs-directory "/.w-firstboot-initialized"))
(unless (file-exists-p winny/initialized-cookie-path)
(message "Detected first boot. Doing initialization steps...")
(package-refresh-contents)
(with-temp-buffer
(write-file winny/initialized-cookie-path))
(message "First boot-only initialization complete."))
(defvar bootstrap-version)
(let ((bootstrap-file
(expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
(bootstrap-version 6))
(unless (file-exists-p bootstrap-file)
(with-current-buffer
(url-retrieve-synchronously
"https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el"
'silent 'inhibit-cookies)
(goto-char (point-max))
(eval-print-last-sexp)))
(load bootstrap-file nil 'nomessage))
(straight-use-package 'use-package)
(require 'use-package)
(require 'cl) ; lexical-let fails to autoload so
; require it to be safe.
(setq use-package-always-defer t) ; Lazy load all packages.
Set use-package-always-ensure
to t
… if one can figure out how to load
stuff from site-lisp
and site-packages
with this setting.
Load the locally maintained is-wsl package which is used later in this file.
(use-package is-wsl
;; Tried adding a ;;;#autoload in the is-wsl.el file. No dice.
:demand t)
Give the desktop window title a nicer look.
(setq-default frame-title-format '("Emacs "
(:eval (if buffer-file-name
(replace-regexp-in-string (regexp-quote (or (getenv "HOME") "")) "~" buffer-file-name)
(buffer-name)))
" [%m] { "
(:eval (string-join (mapcar #'(lambda (w) (buffer-name (window-buffer w))) (window-list)) ", "))
" }"))
This section is inspired by https://github.com/yanghaoxie/emacs.d#setting-related-in-build-in-features
Store backup files in ~/.emacs.d/var/backup/
.
(defvar winny/backup-directory
(concat (file-name-as-directory user-emacs-directory) "var/backup"))
(unless (file-exists-p winny/backup-directory)
(make-directory winny/backup-directory t))
(setq backup-directory-alist
`(("." . ,winny/backup-directory)))
Store auto-save files in ~/.emacs.d/var/auto-save/
(defvar winny/emacs-autosave-directory (concat (file-name-as-directory user-emacs-directory) "var/auto-save/"))
(unless (file-exists-p winny/emacs-autosave-directory)
(make-directory winny/emacs-autosave-directory t))
(setq auto-save-file-name-transforms
`((".*" ,winny/emacs-autosave-directory t)))
(defun show-paren-local-mode (&optional arg)
"Toggle visibility of matching parenthesis for the current buffer.
When ARG is positive or not a number, enable function
`show-paren-mode', else disable it."
(interactive)
(setq-local show-paren-mode
(cond
((numberp arg) (> arg 0))
((not arg) (not show-paren-mode))
(t t)))
(when (called-interactively-p 'interactive)
(message "show-paren-mode %s in current buffer." (if show-paren-mode "enabled" "disabled"))))
(defun add-to-auto-mode-alist (mm extension &rest extensions)
"Add major mode MM for EXTENSION and EXTENSIONS to the `auto-mode-alist'.
EXTENSION may also be a list."
(let ((ls (if (listp extension)
(append extension extensions)
(cons extension extensions))))
(dolist (ext ls)
(add-to-list 'auto-mode-alist (cons (concat "\\." ext "\\'") mm)))
auto-mode-alist))
This section also includes file-format support, as file-formats can be thought of as languages :)
The core racket-mode.
(use-package racket-mode
:ensure t
:config
(setq racket-show-functions 'racket-show-echo-area)
:init
(add-hook
'racket-mode-hook
(defun winny/racket-mode-hook ()
(put 'bit-string-case 'racket-indent-function 'defun)
;; Defer to Emacs default value. Note to self, if I modify this variable
;; I need to use (set-default-value ...)
(setq-local comment-column (default-value 'comment-column))))
(add-hook 'racket-mode-hook
'racket-xp-mode))
And a locally installed scribble.el
for scribble markup.
(use-package scribble)
(use-package fennel-mode
:ensure t
:init
(add-to-list 'auto-mode-alist '("\\.fnl\\'" . fennel-mode)))
(use-package slime
:ensure t
:init
(setq inferior-lisp-program "sbcl"))
I install this via my distro’s package manager, so no use-package
here.
(add-hook 'TeX-mode-hook (lambda ()
(setq word-wrap t)))
(use-package lua-mode
:ensure t
:custom
((lua-indent-level 2)))
I’ve given up on jedi. I am not a fan. It doesn’t work like you expect it to, and is very moody. TODO: look into setting up Python LSP.
(use-package python-mode
:ensure t)
The default ruby mode is not very nice. So use enh-ruby-mode
.
(use-package enh-ruby-mode
:ensure t
:init
;; Not sure if any if this is needed. So commenting it out.
;; (autoload 'enh-ruby-mode "enh-ruby-mode" "Major mode for ruby files" t)
;; (add-to-auto-mode-alist 'enh-ruby-mode "rb")
;; (add-to-list 'interpreter-mode-alist '("ruby" . enh-ruby-mode))
)
Syntax highlighting major mode.
(use-package csharp-mode
:ensure t)
Major mode for csproj and other msbuild project files.
(use-package csproj-mode
:ensure t)
This makes it possible to run some dotnet commands via emacs.
(use-package dotnet
:ensure t
:after csharp-mode
:init
(add-hook 'csharp-mode-hook 'dotnet-mode))
This is the secret sauce for dotnet core support in emacs. It gives code completion, suggestions, errors, and so on. It is the same stuff that VSCode uses internally.
(use-package omnisharp
:ensure t
:after csharp-mode
:after company
:init
(add-hook 'csharp-mode-hook 'omnisharp-mode)
(add-to-list 'company-backends 'company-omnisharp))
Mark the dotnet core .DotSettings
files as xml.
(add-to-list 'auto-mode-alist '("\\.DotSettings\\'" . xml-mode))
(use-package powershell
:ensure t
:hook (powershell-mode
.
(lambda ()
;; No don't override a standard emacs key, really what were they thinking?
(local-unset-key (kbd "M-`"))
;; TODO: bind `powershell-escape-selection' to something else...
)))
(use-package coffee-mode
:ensure t
:init
(setq coffee-tab-width 2))
web-mode is pretty great. It supports all the cool template types.
(use-package web-mode
:ensure t
:config
;; web-mode
(add-to-auto-mode-alist 'web-mode "php" "phtml" "tpl" "[agj]sp" "as[cp]x"
"erb" "mustache" "d?html")
(defadvice web-mode-highlight-part (around tweak-jsx activate)
(if (equal web-mode-content-type "jsx")
(let ((web-mode-enable-part-face nil))
ad-do-it)
ad-do-it))
(setq web-mode-auto-close-style 2
web-mode-enable-auto-closing t
web-mode-code-indent-offset 2
web-mode-css-indent-offset 2
web-mode-markup-indent-offset 2
)
;; (add-hook 'web-mode-hook (lambda ()
;; (setq web-mode-markup-indent-offset 2)
;; (setq web-mode-css-indent-offset 2)
;; (setq web-mode-code-indent-offset 2)))
)
(straight-use-package '(tsi :type git :host github :repo "orzechowskid/tsi.el"))
(straight-use-package '(tsx-mode :type git :host github :repo "orzechowskid/tsx-mode.el" :branch "emacs28"))
A pretty cool framework for modern component web applications.
(use-package svelte-mode
:ensure t)
Maybe I should remove this. Haven’t used a Jade template for a long time.
(use-package jade-mode
:ensure t)
(setq js-indent-level 2)
(use-package typescript-mode
:ensure t)
(use-package tide
:ensure t
:after typescript-mode
:after flycheck
:after company
:after editorconfig
:init
(defun setup-tide-mode ()
(interactive)
(tide-setup)
(flycheck-mode +1)
(setq flycheck-check-syntax-automatically '(save mode-enabled))
(eldoc-mode +1)
(tide-hl-identifier-mode +1)
;; company is an optional dependency. You have to
;; install it separately via package-install
;; `M-x package-install [ret] company`
(company-mode +1))
;; formats the buffer before saving
(add-hook 'before-save-hook 'tide-format-before-save)
(add-hook 'typescript-mode-hook #'setup-tide-mode)
(add-hook 'editorconfig-after-apply-functions (defun winny/fix-tide-indentation (props)
(when (and (boundp 'tide-mode) tide-mode)
(tide-command:configure)))))
(use-package prisma-mode
:after (lsp)
:load-path "~/.emacs.d/site-packages/emacs-prisma-mode")
(use-package php-mode
:ensure t)
scalpp
was a file extension I used for cpp prerocessed code. cool
was a
file extension for a compilers course I took. It was a subset of Scala, so I
used this major mode. coop
is cpp preprocessed code.
(use-package scala-mode
:ensure t
:mode "\\.coo[lp]\\'"
:mode "\\.scalpp\\'")
It turns out golang mode is not strict about indentation despite the toolchain being pretty strict about that sort of thing. So that’s what the hook does.
(use-package go-mode
:ensure t
:after (lsp-mode)
:init
(add-hook 'go-mode-hook #'lsp-deferred)
(add-hook 'go-mode-hook
(defun lsp-go-install-save-hooks ()
(add-hook 'before-save-hook #'lsp-format-buffer t t)
(add-hook 'before-save-hook #'lsp-organize-imports t t)))
(add-hook 'go-mode-hook
(defun winny/go-hook ()
"My Go hook"
(setq-local tab-width 4))))
(use-package haskell-mode
:ensure t
:init
(setq haskell-interactive-popup-errors nil))
(use-package tuareg
:ensure t)
(use-package zig-mode
:ensure t)
(use-package lsp-mode
:ensure t
:bind-keymap (("C-'" . lsp-command-map)))
The nix package language and configuration language.
(use-package nix-mode
:ensure t)
The bash-based packaging format used for archlinux.
(use-package pkgbuild-mode
:ensure t)
(This is installed via the package manager.)
eix app-emacs/ebuild-mode
See https://www.graphviz.org/doc/info/lang.html
(use-package graphviz-dot-mode
:ensure t)
A Java replacement by Google.
(use-package kotlin-mode
:ensure t)
See also groovy-mode for syntax highlighting.
(use-package gradle-mode
:ensure t)
(And Gradle syntax highlighting)
(use-package groovy-mode
:ensure t)
(use-package lsp-java
:after (lsp)
:ensure t
:init (add-hook 'java-mode-hook 'lsp))
(use-package ledger-mode
:ensure t
:after company-mode
:hook
((ledger-mode-hook
.
(lambda ()
(company-mode 1)))))
Always useful to have better CSV tooling.
(use-package csv-mode
:ensure t
:mode "\\.[Cc][Ss][Vv]\\'")
Nice and simple. Just install rust-mode.
(use-package rust-mode
:ensure t)
(use-package swift-mode
:ensure t)
While one could use javascript-mode
, json-mode
restricts the syntax to just the
JSON stuff.
(use-package json-mode
:ensure t)
Yet another silly markup language.
(use-package yaml-mode
:ensure t)
Format XML documents. Not perfect as it depends an xmllint
and that tends to
clean up dirty XML documents (e.g. add DTDs).
(fset 'winny/xml-format
(kmacro-lambda-form [?\C-x ?h ?\C-u ?\M-| ?x ?m ?l ?l ?i ?n ?t ? ?- ?- ?f ?o ?r ?m ?a ?t ? ?- return] 0 "%d"))
Add some other known extensions to xml-mode
.
(add-to-list 'auto-mode-alist '("\\.xsd\\'" . xml-mode)) ; XML Schema Definition
(add-to-list 'auto-mode-alist '("\\.wsdl\\'" . xml-mode)) ; Web Services Description Language
(add-to-list 'auto-mode-alist '("\\.jca\\'" . xml-mode)) ; Java Connector Architecture Adapter files
Tom’s obvious minimal language.
(use-package toml-mode
:ensure t)
For sed(1)
scripts.
(use-package sed-mode
:ensure t)
This adds syntax highlighting for ssh_config
, sshd_config
, known_hosts
,
and authorized_keys
.
(use-package ssh-config-mode
:ensure t)
Also adds major modes for git attributes and git config files.
gitignore-mode
Helps with making sure globs make sense.
(use-package git-modes
:ensure t)
The markdown markup language.
(use-package edit-indirect
:ensure t
:init
(require 'edit-indirect)
(define-key edit-indirect-mode-map (kbd "C-c C-'") 'edit-indirect-commit))
(use-package markdown-mode
:after (edit-indirect)
:ensure t
:init
(require 'markdown-mode)
(setq markdown-asymmetric-header t)
(define-key markdown-mode-map (kbd "C-c C-'") 'markdown-edit-code-block))
The unison synchronization tool has a somewhat weird syntax, so I wrote a major mode to highlight it more accurately.
(use-package unison)
I can’t remember what this does.
(add-hook 'c-mode-common-hook
(lambda ()
(c-set-offset 'substatement-open 0)
(if (assoc 'inexpr-class c-offsets-alist)
(c-set-offset 'inexpr-class 0))))
Set default style and use tabs in C files by default.
(add-hook 'c-mode-hook (lambda ()
(setq indent-tabs-mode t)
(c-set-style "bsd")))
(use-package meson-mode
:ensure t)
See Xah – Emacs: perl-mode vs cperl-mode.
(use-package cperl-mode
:init
(setq cperl-indent-level 4))
(use-package erlang
:ensure t
:init
(require 'erlang-start))
(use-package dockerfile-mode
:ensure t)
(use-package sql-indent
:ensure t
:hook ((sql-mode . sqlind-minor-mode)))
(use-package cmake-mode
:ensure t)
(use-package bison-mode
:ensure t)
(use-package basic-mode
:ensure t
:init
(setq basic-auto-number 20
basic-line-number-cols 4))
(use-package clojure-mode
:ensure t)
(use-package nftables-mode
:ensure t)
(use-package bpftrace-mode
:ensure t)
See calc#Top. Try C-x * ?
(calc-mode-dispatch
). Or dive in with C-x * Q
to “math” it up in the minibuffer then place the result in the kill-ring
(clipboard). Or use C-x * C
to open the typical stack calculator. (By the
way the Q
or C
in the last two key sequences can be lower cased — both
work!)
Suppress the line number in copying cells. See calc#Killing From Stack.
(setq calc-kill-line-numbering nil)
See tramp#Top.
Suppress Save auth info to file ~/.authinfo? [y/n/N/e/?]
. See tramp#Password handling.
(setq auth-source-save-behavior nil)
(use-package sudo-edit
:ensure t
:init
(defalias 'winny/find-current-buffer-as-root #'sudo-edit))
(use-package nhexl-mode
:ensure t
:init
;; defalias needs symbols: it's a function, not a macro.
(defalias 'hex-edit 'nhexl-mode)
(add-hook 'nhexl-mode-hook #'(lambda ()
(setq-local so-long-action 'longlines-mode))))
(mapc (lambda (m) (add-hook (intern (concat (symbol-name m) "-mode-hook"))
(defun whitespace-hook ()
"Hook to make trailing whitespace visible."
(setq-local show-trailing-whitespace t))))
'(c csv c++ python ruby enh-ruby js lisp web racket org TeX haskell makefile))
(defun show-trailing-whitespace (n)
"Toggle the highlight of trailing whitespace for the current buffer.
When N is nil, toggle the highlight setting.
When N is non-negative, enable the highlight setting.
When N is negative, disable the highlight setting."
(interactive "P")
(setq-local show-trailing-whitespace
(cond
((eq n nil) (not show-trailing-whitespace))
((< n 0) nil)
(t t)))
(force-window-update)
(message (if show-trailing-whitespace
"Showing trailing whitespace."
"Hiding trailing whitespace.")))
(global-set-key (kbd "C-x M-w") 'show-trailing-whitespace)
Except on Windows where Disk IO seems to be prohibitively slow. Could just be
work Anti Virus ¯\_(ツ)_/¯. On Windows, typing g
in a dired buffer causes an
excessively long delay (tens of seconds) in a directory with 4000 entries.
(unless (or (member system-type '(ms-dos windows-nt cygwin)) is-wsl)
(add-hook 'dired-mode-hook 'auto-revert-mode))
(eval-after-load 'dired
'(progn
(define-key dired-mode-map (kbd "C-c n") 'dired-create-file)
(defun dired-create-file (file)
"Create a file called FILE.
If FILE already exists, signal an error."
(interactive
(list (read-file-name "Create file: " (dired-current-directory))))
(let* ((expanded (expand-file-name file))
(try expanded)
(dir (directory-file-name (file-name-directory expanded)))
new)
(if (file-exists-p expanded)
(error "Cannot create file %s: file exists" expanded))
;; Find the topmost nonexistent parent dir (variable `new')
(while (and try (not (file-exists-p try)) (not (equal new try)))
(setq new try
try (directory-file-name (file-name-directory try))))
(when (not (file-exists-p dir))
(make-directory dir t))
(write-region "" nil expanded t)
(when new
(dired-add-file new)
(dired-move-to-filename))))))
A better dired.
(use-package dired+
:disabled)
A OFM (like midnight commander) for emacs.
;; (use-package sunrise
;; :disabled
;; :load-path "~/.emacs.d/site-packages/sunrise-commander")
This is a handy side pane with a navigable tree of folders and files. This also configures neotree to sort by file extension.
(defun string</extension (x y)
"Using the file extension, indicate if X is less than Y."
(let ((x-ext (f-ext x))
(y-ext (f-ext y)))
(cond
((string= x-ext y-ext) (string< x y))
((not x-ext) t)
((not y-ext) nil)
(t (string< x-ext y-ext)))))
(use-package neotree
:ensure t
:bind (([f8] . neotree-toggle))
:bind (:map neotree-mode-map
("^" . neotree-select-up-node)
("v" . neotree-select-down-node))
:config (setq neo-filepath-sort-function 'string</extension))
This should be moved to its own emacs lisp file. winny/reload-major-mode
attempts to reload a major mode. This helps when making certain kinds
of changes to el files. No need to restart emacs. Or partially re-evaluate,
only to realize it didn’t work as you expected.
(defun winny/reload-feature (feature &optional force) ; Why the HECK is this
; not standard?
"Reload FEATURE optionally FORCE the `unload-feature' call."
(interactive
(list
(read-feature "Unload feature: " t)
current-prefix-arg))
(let ((f (feature-file feature)))
(unload-feature feature force)
(load f)))
(require 'loadhist) ; For `file-provides'
(defun winny/reload-major-mode ()
"Reload the current major mode.
TODO: This should be generalized to any feature, and will
re-enable any minor or major modes present in the feature's
file."
(interactive)
(letrec ((mode major-mode)
(f (cdr (find-function-library mode)))
(buffers (cl-loop for b in (buffer-list)
when (eq (buffer-local-value 'major-mode b) mode)
collect b)))
(cl-loop for feature in (file-provides f)
do (unload-feature feature t))
(load f)
(cl-loop for b in buffers
do (with-current-buffer b
(funcall mode)))))
Add the following keys to help with navigating custom-mode
:
Key | Command | Description |
---|---|---|
^ | Custom-goto-parent | Go to parent node. |
M-n | winny/forward-child-widget | Go to next configurable option. |
M-p | winny/backward-child-widget | Go to previous configurable option. |
M-RET | Custom-newline | Lazy bind so one doesn’t have to release meta key when wishing to expand/contract a widget. |
The ^
aligns with dired’s usage of ^
to go up one directory.
(require 'cus-edit)
(defconst winny/child-widget-regex "^\\(Hide\\|Show Value\\|Show\\)")
(defun winny/forward-child-widget (&optional arg)
"Navigate to next child widget by ARG.
Use a Negative ARG to navigate backwards."
(interactive "p")
(when (and (looking-at winny/child-widget-regex) (> arg 0))
(setq arg (+ 1 arg)))
(condition-case nil
(progn
(re-search-forward winny/child-widget-regex nil nil arg)
;; Ensure point is at the beginning of the line.
(move-beginning-of-line nil))
(error (ding))))
(defun winny/backward-child-widget (&optional arg)
"Navigate to previous child widget by ARG.
Use a Negative ARG to navigate forwards."
(interactive "p")
(winny/forward-child-widget (- arg)))
(define-key custom-mode-map (kbd "^") 'Custom-goto-parent)
(define-key custom-mode-map (kbd "M-n") 'winny/forward-child-widget)
(define-key custom-mode-map (kbd "M-p") 'winny/backward-child-widget)
(define-key custom-mode-map (kbd "M-RET") 'Custom-newline)
My favorite theme despite occasional low contrast.
(use-package cyberpunk-theme
:ensure t)
(use-package tao-theme
:ensure t)
(use-package nerd-icons
:ensure t
:demand t)
(use-package doom-modeline
:ensure t
:demand t
:after (nerd-icons)
:init (doom-modeline-mode 1))
GitHub - LionyxML/auto-dark-emacs: Auto-Dark-Emacs is an auto changer between…
(use-package auto-dark
:ensure t
:custom
(auto-dark-themes '((modus-vivendi) (modus-operandi)))
:init
;; This major mode initialization will signal an error if it cannot find a
;; valid dark/light mode detection method. Not all computers work with this
;; major mode. Eat the error and carry on.
(condition-case dark-err
(auto-dark-mode)
(error (message "Could not activate auto-dark-mode. Carry on."))))
(load "switch-theme.el" t t)
(setq winny/default-theme 'modus-vivendi)
(use-package smart-mode-line
:ensure t
:disabled
:init
(require smart-mode-line)
(setq sml/col-number-format "%3c"
sml/line-number-format "%4l"
sml/mode-width 'right
sml/numbers-separator ","
sml/replacer-regexp-list '(("^~/\\.emacs\\.d/elpa/" ":ELPA:")
("^~/\\.emacs\\.d/" ":ED:"))
sml/theme 'respectful)
(add-hook 'winny/after-theme-switch-hook 'sml/setup t t))
(defun describe-current-theme ()
"Describe the current theme, ignoring smart-mode-line themes."
(interactive)
(describe-theme
(car
(cl-remove-if (lambda (x)
(string-prefix-p "smart-mode-line" (symbol-name x)))
custom-enabled-themes))))
My Thinkpad x31 is 20 years old so it helps to cut down on excess resources.
See use-package
setup code here. When use-package-always-defer
is set to a
non-nil value, use-package
will set up autoloads (code stubs bound to keys
and declared in the function definition space) instead of loading the entire
package. This results in serious startup time savings. This also saves a lot
on memory usage, since I don’t always reach for the featureful packages within
this configuration.
If a package truly is necessary at startup, consider adding a :demand t
parameter to the use-package
form.
Quick and dirty tests performed on the Thinkpad x31.
use-package-always-defer | Statup time |
---|---|
nil | 52 seconds |
t | 25 seconds |
Instead of running code during startup, consider deferring execution until a particular feature is provided. See an example here.
Try M-x esup RET
. ESUP will spawn a new Emacs process. Once the new Emacs
finished initialization, exit it, then review the startup time breakdown in the
original Emacs frame.
(use-package esup
:ensure t
;; To use MELPA Stable use ":pin mepla-stable",
:pin melpa
:commands (esup))
Bind the emacs profiler to some keys under the C-x M-p
map.
(require 'profiler)
(global-set-key (kbd "C-x M-p s") 'profiler-start)
(global-set-key (kbd "C-x M-p q") 'profiler-stop)
(global-set-key (kbd "C-x M-p r") 'profiler-report)
Function toggle-debug-on-error
is always available, but if there is an error
that prevents M-x toggle-debug-on-error RET
from completing, you won’t be
able to enable this functionality, thereby be unable to get an error trace
(sad). The work around is to make a helper function, then bind it to a key on
the global keymap. In this case C-x \
will toggle debug on error. C-u C-x
\
will toggle debug on quit.
(defun winny/toggle-debug-on-error-or-quit (&optional on-quit)
"Toggle debug on error, or quit with non-nil prefix argument.
When ON-QUIT is non-nil toggle debug on quit instead."
(interactive "P")
(if on-quit
(toggle-debug-on-quit)
(toggle-debug-on-error)))
(global-set-key (kbd "C-x \\") 'winny/toggle-debug-on-error-or-quit)
Ye ole fabulous productivity tool.
In recent org-mode <sTAB
no longer works. One can restore this functionality
using (require 'org-tempo)
— this reimplements the old behavior. On the
other hand the new behavior using C-c C-, s
is much cleaner, allowing the
user to dispatch to any known block type from a menu. It is one extra
keystroke, but I think I’ll live.
See:
- org#Headlines
- org#Catching invisible edits
(use-package org
:pin gnu
:ensure t
:init
(setq org-special-ctrl-a t
org-special-ctrl-k t
org-special-ctrl-k 'error
org-catch-invisible-edits t
org-startup-folded 'fold
;; https://emacs.stackexchange.com/questions/64222/insert-link-to-a-heading-with-id
org-id-link-to-org-use-id 'create-if-interactive
org-image-actual-width 450
user-full-name "Winston Weinert (winny)"
user-mail-address "[email protected]"
org-capture-templates
'(("a" "Anything" entry
(file+datetree "~/files/notes/unsorted.org")
(file "~/.emacs.d/org-capture-templates/unsorted.org"))
("j" "Journal Entry" plain
(file+datetree "~/files/writings/journal/journal.org")
"%?"
:empty-lines 1)
("t" "Todo list item" entry
(file+headline "~/files/notes/todo.org" "Inbox")
(file "~/.emacs.d/org-capture-templates/todo-item.org")))
org-default-notes-file "~/files/notes/unsorted.org"))
(use-package org-contrib
:ensure t
:after org)
C-c C-y
is bound to something that I’ve never used related to timekeeping.
(use-package org-cliplink
:ensure t
:after org
:init
(define-key org-mode-map (kbd "C-c C-y") 'org-cliplink))
(add-hook 'org-mode-hook (defun winny/org-hook ()
(setq word-wrap t)
(turn-on-auto-fill)
(org-indent-mode 1)))
(global-set-key "\C-cl" 'org-store-link)
(global-set-key "\C-ca" 'org-agenda)
(global-set-key "\C-cc" 'org-capture)
(global-set-key "\C-cb" 'org-switchb)
(define-key org-mode-map (kbd "M-n") 'org-next-visible-heading)
(define-key org-mode-map (kbd "M-p") 'org-previous-visible-heading)
(define-key org-mode-map (kbd "<C-M-return>")
(defun winny/org-goto-content ()
"Go to content for heading or create a newline for content."
(interactive)
(org-end-of-meta-data)
(org-show-hidden-entry)
(when (org-at-heading-p)
(open-line 1))))
;; Make it easier to enter/leave org block editing without lifting the Control
;; key.
(define-key org-mode-map (kbd "C-c C-'") 'org-edit-special)
(define-key org-src-mode-map (kbd "C-c C-'") 'org-edit-src-exit)
(define-key org-mode-map (kbd "C-M-u") 'org-up-element)
(defvar winny/org-auto-insert-expiry-pattern-list '()
"A list of regexes like the first element in `auto-mode-alist'
cons cells.")
(defun winny/org-insert-created ()
"Insert created expiry information.
Only insert when the variable the target filing file name matches
a regex in `winny/org-auto-insert-expiry-pattern-list'."
(when (let* ((base-buffer-file-name
(buffer-file-name (buffer-base-buffer (current-buffer))))
(case-fold-search
(file-name-case-insensitive-p base-buffer-file-name)))
(assoc-default base-buffer-file-name
(mapcar #'(lambda (el) (cons el t))
winny/org-auto-insert-expiry-pattern-list)
'string-match))
(save-excursion
(org-back-to-heading)
(org-expiry-insert-created))))
(add-hook 'org-capture-before-finalize-hook 'winny/org-insert-created)
(add-hook 'org-insert-heading-hook 'winny/org-insert-created)
(defun winny/org-table-line-to-definition-list (&optional arg)
"Keyboard macro."
(interactive "p")
(kmacro-exec-ring-item (quote ([4 45 19 124 return 2 2 134217760 4 58 58 5 2 134217760 4 backspace return 11] 0 "%d")) arg))
(defun winny/increment-footnotes (count)
"Increment all footnote numbers in buffer by `COUNT'."
(interactive "p")
(unless count
(setq count 1))
(save-excursion
(goto-char (point-min))
(while (re-search-forward "\\[fn:\\([0-9]+\\)\\]" nil t)
(message "m")
(replace-match (number-to-string (+ count (string-to-number (match-string 1))))
nil nil nil 1))))
(defun afs/org-replace-link-by-link-description ()
"Replace an org link by its description or if empty its address."
(interactive)
(if (org-in-regexp org-bracket-link-regexp 1)
(save-excursion
(let ((remove (list (match-beginning 0) (match-end 0)))
(description (if (match-end 3)
(org-match-string-no-properties 3)
(org-match-string-no-properties 1))))
(apply 'delete-region remove)
(insert description)))))
(with-eval-after-load 'ox
(require 'ox-latex)
(add-to-list 'org-latex-classes
'("beamer"
"\\documentclass\[presentation\]\{beamer\}"
("\\section\{%s\}" . "\\section*\{%s\}")
("\\subsection\{%s\}" . "\\subsection*\{%s\}")
("\\subsubsection\{%s\}" . "\\subsubsection*\{%s\}"))))
Pretty bootstrap based HTML export.
(use-package ox-twbs
:ensure t
:demand t
:after ox)
Export to hugo markdown. Great for blogging.
(use-package ox-hugo
:ensure t
:demand t
:after ox)
(defun winny/new-blog-post ()
(interactive)
(find-file "~/pro/winny.tech/blog.winny.tech/content-org/all-posts.org")
(goto-char (point-max))
(org-previous-visible-heading 1)
(org-meta-return))
Quick and dirty publishing to e-readers. So cozy.
(use-package ox-epub
:ensure t
:demand t
:after ox)
Try C-c l
org-store-link
in a buffer. Tested working in all file-backed
buffers. Works in Info buffers. See org#Handling Links.
With the following code, one can store manpage links. See org#Adding Hyperlink Types.
(with-eval-after-load 'ol
(require 'ol-man))
Cleaner org-mode.
(use-package org-modern
:ensure t
:init
(with-eval-after-load 'org (global-org-modern-mode)))
Refer to The Library of Babel and Babel: Languages.
(use-package ob-async
:ensure t
:after (org)
:init
(require 'ob-async))
And a quick code sample to validate ob-async works.
sleep 10
echo 'Were you able to use emacs?'
(org-babel-do-load-languages 'org-babel-load-languages '((shell . t)))
(org-babel-do-load-languages 'org-babel-load-languages '((python . t)))
(org-babel-do-load-languages 'org-babel-load-languages '((perl . t)))
(use-package deft
:ensure t
:demand t
:config (setq deft-directory "~/files/notes"
deft-extensions '("org")))
Use M-g f
to fold the region. Use M-g d
to delete the fold under point.
Use M-g t
to toggle the fold at point.
(use-package vimish-fold
:ensure t
:demand t
:init
(defun winny/vimish-fold-defun ()
"Fold the defun around point."
(interactive)
(lexical-let ((r (save-excursion (er/mark-defun) (list (region-beginning) (region-end)))))
(vimish-fold (car r) (cadr r))))
(defun winny/vimish-fold-delete (entire-buffer)
"Fold region or entire buffer when ENTIRE-BUFFER is not nil."
(interactive "P")
(if entire-buffer
(vimish-fold-delete-all)
(vimish-fold-delete)))
(global-set-key (kbd "M-g f") #'vimish-fold)
(global-set-key (kbd "M-g M-f") #'vimish-fold)
(global-set-key (kbd "M-g u") #'vimish-fold-unfold)
(global-set-key (kbd "M-g M-u") #'vimish-fold-unfold)
(global-set-key (kbd "M-g t") #'vimish-fold-toggle)
(global-set-key (kbd "M-g M-t") #'vimish-fold-toggle)
(global-set-key (kbd "M-g d") #'vimish-fold-delete)
(global-set-key (kbd "M-g M-d") #'vimish-fold-delete))
When following a symlink into a git repo, display a warning, but don’t prompt if it is okay.
(setq vc-follow-symlinks nil)
The best way to use git. As long as you know C-x g
to open the magit menu,
you are good to go.
(use-package magit
:ensure t
:demand t
:bind (("C-x g" . magit-status)
("C-x M-g" . magit-dispatch)
("C-x M-c" . magit-clone)
:map magit-revision-mode-map
("C-c u" . magit-rev-parent))
:init
(fset 'magit-rev-parent
(kmacro-lambda-form [?\M-< ?\C-s ?p ?a ?r ?e ?n ?t ?: return return] 0 "%d")))
(use-package magit-lfs
:ensure t
:demand t
:after magit)
Work with github and gitlab efficiently.
As of 2022-07-13 there’s an issue with my forge setup, so disable temporarily until a workaround can be determined.
(use-package forge
:disabled
:ensure t)
Sometimes I put texinfo files into ~/docs/info
. Most distros do not package
mysql’s texinfo, for example. It sure beats firing up a web browser!
(add-to-list 'Info-directory-list "~/docs/info" t)
Add a key to easily copy the current info node name. This can be used to share with others how to find docuemantion.
(bind-key "y" #'Info-copy-current-node-name Info-mode-map)
In this repository.
(use-package irfc
:demand t
:hook (irfc-mode
. (lambda ()
(read-only-mode) ; Make read only.
(show-paren-local-mode -1))))
The helpful
package takes over C-h v
, C-h k
, C-h f
providing more
descriptive output and nicer formatting.
(use-package helpful
:ensure t
:demand t
:bind (("C-h v" . helpful-variable)
("C-h k" . helpful-key)
("C-h f" . helpful-callable)))
Say you start typing C-x
. After a brief delay this mode will show all
available keys at the bottom of the screen. This can help with forgetting
keyboard shortcuts, as one tends to do with octopus-hand tools like Emacs.
(use-package which-key
:ensure t
:init
(which-key-mode 1)
:config
;; Address issue with tao-yin. This is a hack.
(set-face-attribute 'which-key-command-description-face nil :inherit nil))
No more guessing if a key is available. This will show a list of all keys
available in a given mode map. Use C-h Y
.
(use-package free-keys
:ensure t
:bind (("C-h Y" . free-keys)))
(defun what-face (pos)
"Describe the face under point.
Prefix argument POS should be a location it the buffer."
(interactive "d")
(let ((face (or (get-char-property (pos) 'read-face-name)
(get-char-property (pos) 'face))))
(if face (message "Face: %s" face) (message "No face at %d" pos))))
See here.
(use-package dictionary
:ensure t)
(use-package direnv
:ensure t
:config
;; Some modes like TRAMP stop working completely if direnv-mode is enabled
;; without direnv in PATH.
(direnv-mode (cond
((executable-find "direnv") 1)
(t (message "direnv executable missable. Deactivating direnv-mode.")
-1))))
See https://www.funtoo.org/Keychain
(use-package keychain-environment
:ensure t
:init
(keychain-refresh-environment))
(use-package vertico
:ensure t
:init
(setq read-file-name-completion-ignore-case t
read-buffer-completion-ignore-case t
completion-ignore-case t)
(vertico-mode))
(use-package all-the-icons
:ensure t
:demand t)
(use-package savehist
:init
(savehist-mode))
;; Enable rich annotations using the Marginalia package
(use-package marginalia
:ensure t
:demand t
;; Either bind `marginalia-cycle' globally or only in the minibuffer
:bind (("M-A" . marginalia-cycle)
:map minibuffer-local-map
("M-A" . marginalia-cycle))
;; The :init configuration is always executed (Not lazy!)
:init
;; Must be in the :init section of use-package such that the mode gets
;; enabled right away. Note that this forces loading the package.
(marginalia-mode))
(use-package all-the-icons-completion
:demand t
:load-path "~/.emacs.d/site-packages/all-the-icons-completion"
:after (marginalia all-the-icons)
:init
(require 'all-the-icons-completion)
(add-hook 'marginalia-mode #'all-the-icons-completion-marginalia-setup)
(all-the-icons-completion-mode))
(add-hook 'after-init-hook
(defun winny/ensure-XDG_RUNTIME_DIR ()
"Ensure XDG_RUNTIME_DIR is set.
Used by qutebrowser and other utilities."
(let ((rd (getenv "XDG_RUNTIME_DIR")))
(when (or (not rd) (string-empty-p rd))
(setenv "XDG_RUNTIME_DIR" (format "/run/user/%d" (user-uid)))))))
This allows for C-u M-x eww RET
to create a new buffer. This is from
https://emacs.stackexchange.com/a/24477/9163 .
(defun modi/force-new-eww-buffer (orig-fun &rest args)
"When prefix argument is used, a new eww buffer will be created,
regardless of whether the current buffer is in `eww-mode'."
(if current-prefix-arg
(with-temp-buffer
(apply orig-fun args))
(apply orig-fun args)))
(advice-add 'eww :around #'modi/force-new-eww-buffer)
It appears the above does not work :(. This is a convenient work around. Just
use M-x eww-new RET
(defun eww-new ()
(interactive)
(let ((url (read-from-minibuffer "Enter URL or keywords: ")))
(switch-to-buffer (generate-new-buffer "*eww*"))
(eww-mode)
(eww url)))
Using writeroom-mode, one can center the text in eww-mode, reduce the paragraph width, and increase line height.
(add-hook 'eww-mode-hook 'writeroom-mode)
(use-package rg
:ensure t
:init
;; Move over the default rg search to `rg/files'.
(rg-define-search rg/files :confirm prefix)
;; Don't prompt for file types. Note: "all" will only search the files known
;; to ripgrep to be interesting. This won't work if working with
;; non-standard file extensions. Instead use "everything", which appears to
;; be what ripgrep does by default anyways.
;;
;; Created https://github.com/dajva/rg.el/issues/131 to memorialize this
;; surprising behavior.
(rg-define-search rg :confirm prefix :files "everything"))
Use f3
as an ergonomic search key.
(define-key global-map (kbd "<f3>") 'isearch-forward)
(define-key global-map (kbd "<S-f3>") 'isearch-backward)
(define-key isearch-mode-map (kbd "<f3>") 'isearch-repeat-forward)
(define-key isearch-mode-map (kbd "<S-f3>") 'isearch-repeat-backward)
Occcur is pretty cool, but not sure why n
and p
do not move the cursor down
and up?
(define-key occur-mode-map (kbd "p") 'previous-line)
(define-key occur-mode-map (kbd "n") 'next-line)
A rather nice incremental search.
(use-package swiper
:ensure t
:bind (("C-x M-s" . swiper)))
Check spelling of prose in writing modes.
(add-hook 'text-mode-hook 'flyspell-mode)
And make it less distracting because jeeeeeeeeeeeeeeeez!
(require 'flyspell)
(setq flyspell-persistent-highlight nil)
;; https://emacs.stackexchange.com/questions/450/intelligent-spell-checking-in-org-mode
(add-to-list 'ispell-skip-region-alist '(":\\(properties\\|LOGBOOK\\|PROPERTIES\\|LOGBOOK\\):" . ":END:"))
(add-to-list 'ispell-skip-region-alist '("#\\+\\(BEGIN_SRC\\|begin_src\\)" . "#\\+\\(END_SRC\\|end_src\\)"))
Enable it globally.
(use-package flycheck
:ensure t
:init
;; Disable the Elisp checkdoc checker. I'm not sure why this is enabled by
;; default as most elisp users write is ad-hoc and
;; undocumented... https://emacs.stackexchange.com/a/10854/9163
(setq-default flycheck-disabled-checkers '(emacs-lisp-checkdoc))
(global-flycheck-mode 1))
(use-package company
:ensure t
:init
(global-set-key (kbd "<C-tab>") 'company-complete)
;; Temporarily disable this hook until implications are understood. Add the
;; line to host.el instead.
;; (add-hook 'after-init-hook 'global-company-mode)
)
C-x o
goes to the next window. But what about going to the previous window?
One can do C-u -1 C-x o
but we can do better than that.
This adds C-x O
to cycle backwards.
(defun other-window-reverse (offset &optional all-frames)
"`other-window' but in reverse."
(interactive "p")
(other-window (- (if (numberp offset) offset 1)) all-frames))
(global-set-key (kbd "C-x O") 'other-window-reverse)
(global-set-key (kbd "<f4>") 'other-window)
(global-set-key (kbd "S-<f4>") 'other-window)
This scrolls the viewport up and down. It keeps the cursor at the same line
except if the line the cursor is presently on scrolls off the screen. Then the
cursor moves to the line closest to the previous line that is still on the
screen. It is bound to M-N
and M-P
.
(defun scroll-up-1 ()
"Scroll up by 1 line."
(interactive)
(scroll-up 1))
(defun scroll-down-1 ()
"Scroll down by 1 line."
(interactive)
(scroll-down 1))
(global-set-key (kbd "M-N") 'scroll-up-1)
(global-set-key (kbd "M-P") 'scroll-down-1)
(use-package buffer-move
:ensure t
:bind (("C-x w p" . buf-move-up)
("C-x w n" . buf-move-down)
("C-x w b" . buf-move-left)
("C-x w f" . buf-move-right)))
(defun traverse-page--recenter-top (&optional count)
"Recenter top, ignoring COUNT."
(when (get-buffer-window)
(recenter-top-bottom 0)))
(advice-add 'forward-page :after #'traverse-page--recenter-top)
(advice-add 'backward-page :after #'traverse-page--recenter-top)
(global-set-key (kbd "<C-M-next>") 'forward-page)
(global-set-key (kbd "<C-M-prior>") 'backward-page)
Navigate history of window/buffer/frame layout. Use C-c <left>
to go to
previous layout, and C-c <right>
to go to next layout.
(winner-mode 1)
(define-key global-map (kbd "C-,") 'winner-undo)
This works by disabling font locking (syntax highlighting) when rendering is taking too long, then restores font locking when scrolling stops.
(use-package fast-scroll
:ensure t
:config
;; Keep `mode-line-format' the same. This addresses a problem with
;; disappearing winum mode-line indicies.
(defun fast-scroll-default-mode-line ()
mode-line-format)
:init
(fast-scroll-mode 1))
New with Emacs 27.1. See M-x so-long-commentary RET
.
(global-so-long-mode 1)
One can use C-M-B
and C-M-f
to go backward and forward between
s-expressions, but sometimes that is a bit awkward. So add keys C-x ,
and
C-x .
to do the same thing.
(global-set-key "\C-x," 'backward-sexp)
(global-set-key "\C-x." 'forward-sexp)
This feature allows for the quick toggle of line numbers. I personally don’t find line numbers very handy, but they help pair programmers communicate which particular code fragment they are talking about.
Type C-x M-l
to toggle line numbers.
(defun enable-line-numbers ()
"Enable line numbers in prog-mode."
(interactive)
(cl-loop for buf in (buffer-list)
collect (with-current-buffer buf
(when (derived-mode-p 'prog-mode)
(display-line-numbers-mode 1))))
(add-hook 'prog-mode-hook 'winny--enable-line-numbers)
(when (called-interactively-p 'interactive)
(message "Line numbers ENABLED in prog-modes."))
t)
(defun disable-line-numbers ()
"Disable line numbers in prog-mode."
(interactive)
(cl-loop for buf in (buffer-list)
collect (with-current-buffer buf
(when (derived-mode-p 'prog-mode)
(display-line-numbers-mode -1))))
(remove-hook 'prog-mode-hook 'winny--enable-line-numbers)
(when (called-interactively-p 'interactive)
(message "Line numbers DISABLED in prog-modes."))
nil)
(defun winny--enable-line-numbers ()
"Internal hook function."
(display-line-numbers-mode 1))
(defun toggle-line-numbers ()
"Toggle visibility of line numbers in prog-mode."
(interactive)
(if (member 'winny--enable-line-numbers prog-mode-hook)
(call-interactively 'disable-line-numbers)
(call-interactively 'enable-line-numbers)))
(global-set-key (kbd "C-x M-l") 'toggle-line-numbers)
Type C-c C-SPC
or C-c SPC
then type the character you wish to navigate to.
Type the subsequent highlighted character when prompted. Viola!
(use-package ace-jump-mode
:ensure t
:init
(define-key global-map (kbd "C-c SPC") 'ace-jump-mode)
(define-key global-map (kbd "C-c C-SPC") 'ace-jump-mode))
Paredit is the best.
(use-package paredit
:ensure t
:init
(dolist (m '(emacs-lisp-mode-hook
racket-mode-hook
lisp-mode-hook
scheme-mode-hook
clojure-mode-hook))
(add-hook m #'paredit-mode))
(add-hook 'paredit-mode-hook
(defun winny/add-paredit-keystrokes ()
"Ensure custom keys are enabled in paredit."
(bind-keys :map paredit-mode-map
("{" . paredit-open-curly)
("}" . paredit-close-curly))
(unless terminal-frame
(bind-keys :map paredit-mode-map
("M-[" . paredit-wrap-square)
("M-{" . paredit-wrap-curly))))))
(use-package paren-face
:ensure t
:config
(setq paren-face-regexp (rx (any "()[]{}")))
(add-to-list 'paren-face-modes 'racket-mode)
(add-to-list 'paren-face-modes 'racket-reply-mode)
(add-to-list 'paren-face-modes 'emacs-lisp-mode)
(add-to-list 'paren-face-modes 'lisp-mode))
(put 'if 'lisp-indent-function 'defun)
Use C-=
to select things around the point such as words, balanced delimiters,
paragraphs, functions, incrementally.
(use-package expand-region
:ensure t
:bind (("C-=" . er/expand-region)))
Configure the editor via .editorconfig
files.
(use-package editorconfig
:ensure t
:config
(setq editorconfig-mode-lighter " EdC")
:init
(editorconfig-mode 1))
Insert matching parenthesis.
(electric-pair-mode 1)
Use 79 chars in each line for filling.
(setq-default fill-column 79)
(setq-default indent-tabs-mode nil)
(setq-default comment-column 0)
One can use M-z
to character. This will delete all text including the first
occurrence of the prompted character. Sometimes this is not ideal, so one can
use C-M-z
to zap up to (but keep) the prompted character.
(global-set-key (kbd "C-M-z") 'zap-up-to-char)
(defun winny/maybe-query-replace-bad-comma (no-prompt)
"Replace occurrences of , followed by a non-space. if `NO-PROMPT' then do don't do a query replace."
(interactive "P")
(funcall
(if no-prompt
'replace-regexp
'query-replace-regexp)
",\\(\\S \\)"
", \\1"))
This unwraps a paragraph into one line.
(defun unfill-region (beg end)
"Unfill the region, joining text paragraphs into a single
logical line. This is useful, e.g., for use with
`visual-line-mode'."
(interactive "*r")
(let ((fill-column (point-max)))
(fill-region beg end)))
(defun winny/kill-whitespace-right ()
"Kill whitespace to right of point."
(interactive)
(delete-region (point) (save-excursion (skip-chars-forward " \t") (point))))
(defun winny/mark-defun ()
(interactive)
(mark-defun)
(when (or (comment-only-p (region-beginning) (region-end))
(looking-at-p "[[:space:]]*$"))
(forward-line 1)))
Create a line similar to # shellcheck disable=SC1234
above the current line
in order to calm down Shellcheck.
(require 'cl) ; for cl-loop
(require 'sh-script) ; For sh-mode-map
(defun winny--extract-shellcheck-error (err)
(and-let* (((eq (flycheck-error-checker err) 'sh-shellcheck)))
(flycheck-error-id err)))
(defun winny/shellcheck-disable-at-line ()
"Insert \"# shellcheck disable=SC...\" line to silence shellcheck errors."
(interactive)
(save-match-data
(save-excursion
(and-let* ((errs
(cl-loop for err in (flycheck-overlay-errors-in (pos-bol) (pos-eol))
if (winny--extract-shellcheck-error err)
collect (winny--extract-shellcheck-error err))))
(beginning-of-line)
(insert (format "# shellcheck disable=%s"
(mapconcat 'identity errs ",")))
(indent-according-to-mode)
(newline-and-indent)))))
(add-hook 'sh-mode-hook
(defun winny--bind-shellcheck-disable ()
(define-key sh-mode-map (kbd "C-c ! k") 'winny/shellcheck-disable-at-line)))
Using Yasnippets. See the documentation.
(use-package yasnippet
:ensure t
:hook
(snippet-mode . (lambda ()
;; Do not force a newline in snippets.
(setq-local require-final-newline nil)))
:init
(make-directory (concat user-emacs-directory "/snippets") :parents)
(yas-global-mode 1))
(use-package yasnippet-snippets
:ensure t)
Type C-w
without a region (selection) to kill the current line. Found this
in Mastering Emacs, a fantastic book that you should also read :).
(use-package whole-line-or-region
:ensure t
:init
(whole-line-or-region-global-mode))
(save-place-mode 1)
(use-package elfeed
:ensure t
;; :after writeroom-mode
;; :hook (elfeed-show-mode . (lambda ()
;; (writeroom-mode 1)
;; (setq-local shr-width (writeroom--calculate-width))))
)
Manage RSS feeds in elfeed.org.
(use-package elfeed-org
:ensure t
:init
(elfeed-org))
(add-hook 'after-save-hook
(defun winny/make-shebanged-file-executable ()
"Make sure scripts with shebang are saved with expected permissions."
(interactive)
(when (and (save-excursion (goto-char (point-min)) (looking-at "#!"))
(not (file-executable-p buffer-file-name)))
(message "Making `%s' executable..." buffer-file-name)
(executable-chmod))))
(use-package shebang-change
:init
;;(winny/add-shebang-change-hooks)
)
(Besides smart-mode-line)
(display-battery-mode
;; Show battery status only if the system can use a battery.
(if (and (fboundp battery-status-function)
(lexical-let ((ac-line-status
(alist-get ?L (funcall battery-status-function))))
(and ac-line-status (not (equal "N/A" ac-line-status)))))
1
-1))
(use-package mode-line-bell
:ensure t
:init
(mode-line-bell-mode 1))
(setq visible-bell t)
(defun revert-all-buffers ()
"Refreshes all open buffers from their respective files."
(interactive)
(dolist (buffer (buffer-list) (message "Refreshed open files"))
(let ((fn (buffer-file-name buffer)))
(when (and fn (not (buffer-modified-p buffer)))
(if (file-exists-p fn)
(progn
(set-buffer buffer)
(revert-buffer t t t))
(message "Backing file `%s' no longer exists! Skipping." fn))))))
(defun kill-all-missing-buffers (no-ask)
"Kill all buffers with missing files.
When prefix argument NO-ASK is non-nil, do not ask before killing
each buffer"
(interactive "P")
(dolist (buffer (buffer-list))
(let ((fn (buffer-file-name buffer)))
(when (and fn (not (file-exists-p fn)))
(if no-ask
(kill-buffer buffer)
(kill-buffer-ask buffer))))))
(defun yank-file-name (choice)
"Copy the the buffer path to the `kill-ring'.
CHOICE can be `?f', `?d', or `?n' for full path, directory path,
or filename respectively. Via
https://stackoverflow.com/a/18814469/2720026"
(interactive "cCopy Buffer Name (F) Full, (D) Directory, (N) Name, (P) Project Path")
(let* ((name (if (eq major-mode 'dired-mode)
(dired-get-filename)
(buffer-file-name)))
(s
(cl-case choice
(?f name)
(?d (file-name-directory name))
(?n (file-name-nondirectory name))
(?p (replace-regexp-in-string (regexp-quote (projectile-project-root)) "" name)))))
(cond
(s
(message "%s copied" s)
(kill-new s))
(t
(message "(No name to copy.)")))))
(defun show-file-name ()
"Show the full path file name in the minibuffer."
(interactive)
(message (buffer-file-name)))
(defalias 'list-buffers 'ibuffer)
Better highlight. Don’t believe I use this?
(use-package highlight
:ensure t)
Highlight Todo’s and XXX.
(use-package hl-todo
:ensure t
:init
(global-hl-todo-mode 1))
Highlight color codes.
(use-package rainbow-mode
:ensure t)
Show a nice screen when emacs starts up or creates a new fram.
(use-package dashboard
:ensure t
:bind (:map dashboard-mode-map
("p" . dashboard-previous-line)
("n" . dashboard-next-line))
:init
(setq ;;initial-buffer-choice (lambda () (get-buffer "*dashboard*"))
dashboard-items '((recents . 15))
dashboard-item-shortcuts '((projects . "j")
(recents . "r")
(bookmarks . "m")
(agenda . "a")
(registers . "e"))
dashboard-image-banner-max-height 50
dashboard-image-banner-max-width 50)
;; Add the hook to startup, but... See second line.
(dashboard-setup-startup-hook)
;; Ensure scratch is hidden
(add-hook 'emacs-startup-hook 'delete-other-windows)
(defun dashboard ()
"Switch to or create the dashboard. "
(interactive)
(let ((buffer "*dashboard*"))
(when (not (get-buffer buffer))
(dashboard-insert-startupify-lists))
(switch-to-buffer buffer)
(revert-buffer))))
(use-package browse-kill-ring
:ensure t
:bind (("C-x y" . browse-kill-ring)))
(global-set-key (kbd "C-x c") 'compile)
I prefer the window manager to handle this, and it only feels familiar in console, where C-z does exactly what it should. It shouldn’t minimize windows, it’s not the same thing.
(when window-system
(global-unset-key (kbd "C-z")))
Pretty nice to see what’s in the registers. Bind it to C-x r v
.
(global-set-key (kbd "C-x r v") 'view-register)
Just in case the M-x
replacement de-jure messes up, keep it bound elsewhere.
(global-set-key (kbd "C-x M-x") 'execute-extended-command)
C-c P f
to find file at point. And C-c P u
to find url at point.
(define-key global-map (kbd "C-c P f") 'find-file-at-point)
(define-key global-map (kbd "C-c P u") 'browse-url-at-point)
Like kill-buffer
but just moves the buffer to the end of the buffer list.
(global-set-key (kbd "C-x K") 'bury-buffer)
Default macro keys are in a weird place so let’s move them over. I had a reason to do this, but I’ve since forgotten.
(Note, f3 is already rebound in a different section. See here. Menu-bar-open
(F10) default is not very useful – just use M-`
.)
(global-set-key (kbd "<f9>") 'kmacro-start-macro-or-insert-counter)
(global-set-key (kbd "<f10>") 'kmacro-end-or-call-macro)
Having to type the default repeat key is torture. C-x z
requires four
actions. Hold down C
, then type x
. Release C
. Type z
. So instead,
just Bind C-x C-z
which means one can rapid-fire repeat with only two
keystrokes per repeat.
(global-set-key (kbd "C-x C-z") 'repeat)
(defun winny/save-last-kill-to-register (register)
"Save the last kill to register."
(interactive (list (register-read-with-preview "Copy last kill to register: ")))
(set-register register (current-kill 0)))
(define-key global-map "\C-xr\C-y" 'winny/save-last-kill-to-register)
This creates a new directory for the advent of code day, then creates a
sample.txt buffer. Finally it creates a .rkt
source file for the day with a
template.
(fset 'new-aoc-day
(kmacro-lambda-form [?\C-x ?d ?~ ?p ?r ?o ?/ ?a ?o ?c ?/ ?2 ?0 ?2 ?1 ?/ return ?+ ?d ?a ?y ?\C-u ?\C-x ?q return return ?\C-x ?\C-f ?s ?a ?m ?p ?l ?e ?. ?t ?x ?t return ?\C-x ?3 ?\C-x ?\C-f return ?^ ?\C- ?\C-e ?\M-w return ?\C-x ?\C-f ?d ?a ?y backspace backspace backspace ?\C-y ?. ?r ?k ?t return ?\M-x ?y ?a return return] 0 "%d"))
This improves the presentation of emacs so it’s less distracting when writing prose. It centers the text, reduces paragraph width, and increases line height. It has application in other modes where reading content can be improved by applying the aforementioned visual tweaks.
(use-package writeroom-mode
:ensure t
:init
(setq writeroom-fullscreen-effect nil))
Manage groups of buffers by project. Also do actions with respect to a
project. A project root can be defined as a git repository, a folder with a
.projectfile
file in it, and so on.
(use-package projectile
:ensure t
:bind-keymap ("C-c p" . projectile-command-map)
:config
(setq projectile-mode-line-prefix " Pro")
:init
;;(setq projectile-project-search-path '("~/pro" "~/code" "~/docs"))
(setq projectile-project-search-path '("~/"))
(projectile-mode 1))
(use-package systemd
:ensure t)
Helper stuff for ansible.
(use-package ansible
:ensure t)
Syntax highlight inventory files
(add-to-list 'auto-mode-alist '("/inventory[^/]*\\'" . conf-unix-mode))
(use-package terraform-mode
:ensure t)
;; Tell vterm to automatically try to compile the module when it's not present.
;; This prevents vterm from prompting the user if they wish to compile and
;; delaying productivity.
(setq vterm-always-compile-module t)
(use-package vterm
:ensure t
:bind (("C-`" . vterm))
:init
(setq vterm-set-bold-hightbright t))
(defun winny/raise-or-create-window-system-frame (display)
"Raise an existing frame in the window system or create a new one.
DISPLAY is the X11 DISPLAY variable contents."
(let ((frames (seq-filter #'(lambda (f) (frame-parameter f 'display)) (frame-list))))
(if (null frames)
(make-frame `((window-system . x)
(display . ,display)))
(select-frame-set-input-focus (car frames)))))
(defun remove-from-list (list-var element)
"Remove ELEMENT from LIST-VAR."
(setq list-var (delete element list-var)))
M-x toggle-word-wrap RET
(defun toggle-word-wrap ()
"Toggle word wrap."
(interactive)
(message (format
"Word wrap %s."
(if (setq word-wrap (not word-wrap))
"enabled"
"disabled"))))
(defun winny/change-prop-line-mode (mode &optional dont-change-mode)
"Change the prop line's major MODE.
If DONT-CHANGE-MODE is not nil, dont change to that MODE first."
(interactive "aMajor mode: \nP")
(unless dont-change-mode
(funcall-interactively mode))
(delete-file-local-variable-prop-line 'mode)
(let ((sans-mode (intern (replace-regexp-in-string "-mode$" "" (symbol-name mode)))))
(add-file-local-variable-prop-line 'mode sans-mode nil)))
Great for experimenting with keyboard shortcuts.
(defun buffer-local-set-key (key func)
(interactive "KSet key on this buffer: \naCommand: ")
(let ((name (format "%s-magic" (buffer-name))))
(eval
`(define-minor-mode ,(intern name)
"Automagically built minor mode to define buffer-local keys."))
(let* ((mapname (format "%s-map" name))
(map (intern mapname)))
(unless (boundp (intern mapname))
(set map (make-sparse-keymap)))
(eval
`(define-key ,map ,key func)))
(funcall (intern name) t)))
(defun hide-fringes ()
"Hide fringes"
(interactive)
(set-window-fringes (selected-window) 0 0))
(defun change-default-directory (target)
"Change DEFAULT-DIRECTORY to TARGET.
Useful for things like vterm, ansi-term, or term. One can change
directory in the child shell but it won't reflect in Emacs. This
allows the user to manually update this."
(interactive "D")
(setq default-directory target))
Enable narrow-to-region
(put 'narrow-to-region 'disabled nil)
White noise for the whole family.
(use-package snowcrash
:demand t)
Like with-editor
, set up server.el
(see server-visit-files
) with C-c
C-c
to “commit” save and close the buffer, and C-c C-k
to revert and close
the buffer (thereby discarding the edits).
(add-hook 'server-visit-hook
(defun winny/server-visit-hook ()
(when (frame-parameter nil 'winny/opened-from-editor)
(buffer-local-set-key (kbd "C-c C-c") (defun winny/server-edit-commit ()
(interactive)
(save-buffer)
(server-edit)))
(buffer-local-set-key (kbd "C-c C-k") (defun winny/server-edit-abort ()
(interactive)
(revert-buffer nil t)
(server-edit))))))
(winny/load-file-when-present "~/.emacs.d/host-post-init.el")
(And load the legacy host.el
for now…)
(winny/load-file-when-present "~/.emacs.d/host.el")
(message "configuration.org evaluation complete.")