diff options
-rw-r--r-- | emacs/.emacs.d/init.el | 3 | ||||
-rw-r--r-- | emacs/.emacs.d/main.el | 2513 |
2 files changed, 332 insertions, 2184 deletions
diff --git a/emacs/.emacs.d/init.el b/emacs/.emacs.d/init.el index 27ab85cc..48fc30a3 100644 --- a/emacs/.emacs.d/init.el +++ b/emacs/.emacs.d/init.el @@ -21,5 +21,6 @@ (when (featurep 'auto-compile) (auto-compile-on-load-mode)) -(load-file (expand-file-name "main.el" user-emacs-directory)) +(let ((gc-cons-threshold most-positive-fixnum)) + (load (expand-file-name "main.el" user-emacs-directory) :nomessage t)) ;;; init ends here diff --git a/emacs/.emacs.d/main.el b/emacs/.emacs.d/main.el index bbfa3164..444b13e2 100644 --- a/emacs/.emacs.d/main.el +++ b/emacs/.emacs.d/main.el @@ -1,261 +1,53 @@ -;;; emacs-config --- Summary -;; #+TITLE: Emacs Configuration for Alan Pearce -;; #+OPTIONS: ^:nil -;; #+PROPERTY: results silent -;; #+PROPERTY: eval no-export -;; #+PROPERTY: header-args :comments link -;;; Commentary: -;;; This is my Emacs configuration. -;;; Header: -;; This is a living document, detailing my Emacs configuration using org-mode -;;; Code: -;;;; Basics -;;;;; Startup -;; Open Emacs with just a plain window. No graphics or messages, please! -(setq inhibit-startup-screen t) -(setq gc-cons-threshold 100000000) -(add-hook 'after-init-hook - (lambda () - (setq gc-cons-threshold 800000))) -(remove-hook 'find-file-hooks #'vc-refresh-state) - -;; Are we running on Windows via the WSL? - -(when (file-exists-p "/proc/sys/kernel/osrelease") - (with-temp-buffer - (insert-file-contents-literally "/proc/sys/kernel/osrelease") - (decode-coding-region (point-min) (point-max) 'utf-8 t) - (when (string-match "Microsoft$" (buffer-string)) - (setq system-type 'gnu/linux/windows)))) - -;;;;; Compatibility - -(if (version< emacs-version "25.0") - (defmacro with-eval-after-load (file &rest body) - `(eval-after-load ,file (lambda () ,@body)))) - -;;;;; Scratch buffers -;; I usually use scratch buffers for any sort of text. If I need a -;; programming mode in one, then I’ll just call it manually. I also like -;; the buffer to be empty. -(setq initial-scratch-message "" - initial-major-mode 'text-mode) - -;;;;; Personal Information -(setq user-full-name "Alan Pearce") -;; #+end_src - -;;;; Packaging - -;;;;; Use-package - -(autoload 'package-installed-p "package.el") -(eval-and-compile - (require 'seq) - (defun is-nix-emacs () - (and invocation-directory - (string-match "^/nix/store" invocation-directory) - (not (null (seq-some (lambda (dir) (string-match "^/nix/store" dir)) load-path))))) - (defvar nix-emacs (is-nix-emacs)) - - (setq tls-checktrust t - gnutls-verify-error t - package-menu-async t - package-user-dir (concat "~/.emacs.d/packages/" emacs-version "/elpa") - package-menu-hide-low-priority t - package-archives '(("gnu" . "https://elpa.gnu.org/packages/") - ("melpa-stable" . "https://stable.melpa.org/packages/") - ("melpa" . "https://melpa.org/packages/"))) - (unless nix-emacs - (setq package-pinned-packages '(("use-package" . melpa-stable) - ("bind-key" . melpa-stable)) - package-archive-priorities '(("melpa" . 10) - ("gnu" . 10) - ("melpa-stable" . 5) - ("marmalade" . 0)))) - (when (eq system-type 'darwin) - (with-eval-after-load "gnutls" - (add-to-list 'gnutls-trustfiles "/etc/ssl/cert.pem"))) - (unless nix-emacs - (unless (package-installed-p 'use-package) - (package-refresh-contents) - (package-install 'use-package)))) -(eval-when-compile (require 'use-package)) -(unless (featurep 'use-package) - (require 'bind-key) - (use-package use-package - :commands (use-package-autoload-keymap) - :defer 5)) -(setq use-package-always-ensure (not nix-emacs) - use-package-always-demand (daemonp) +(setq inhibit-startup-screen t + initial-scratch-message "" + initial-major-mode 'text-mode package-enable-at-startup nil) -;;;;; Helpers - -;;;;;; Hook Helpers - -;; An improvement over add-hook with lamda functions that allows -;; modification and removal, without the boilerplate of an extra function -;; definition. +(eval-when-compile (require 'use-package)) +(setq use-package-always-demand (daemonp)) +;;; Helpers (eval-and-compile (require 'subr-x) (use-package hook-helpers)) -;;;; Customize -;; I don’t really like using customize for normal configuration. -;; Instead, I use it for things that get saved automatically. That’s why -;; I use a different file, which is ignored by the VCS. It also means -;; that it’s not important whether the file exists or not, which is why I -;; pass =:noerror= to =load= +;;; Customize (setq custom-file "~/.emacs.d/custom.el") (load custom-file :noerror :nomessage) -;;;; Styles +(use-package crux + :custom ((crux-reopen-as-root-mode t))) + +;;; Styles ;; I prefer an always-visible cursor. Feels less distracting. -(when (fboundp #'blink-cursor-mode) - (blink-cursor-mode -1)) +(customize-set-variable 'blink-cursor-mode nil) ;; Disable all the bars, unless on OSX, in which case, keep the menu bar. -(when (and menu-bar-mode (not (eq window-system 'ns))) - (menu-bar-mode -1)) -(with-eval-after-load 'scroll-bar - (set-scroll-bar-mode nil)) -(with-eval-after-load 'tooltip - (tooltip-mode -1)) -(with-eval-after-load 'tool-bar - (tool-bar-mode -1)) +(customize-set-variable 'menu-bar-mode nil) +(customize-set-variable 'scroll-bar-mode nil) +(customize-set-variable 'tool-bar-mode nil) (set-fringe-mode '(4 . 4)) ;; Ring the bell sometimes, but not so often (setq ring-bell-function (lambda () (unless (memq this-command - '(isearch-abort abort-recursive-edit exit-minibuffer keyboard-quit undo-tree-undo)) + '(isearch-abort abort-recursive-edit exit-minibuffer keyboard-quit minibuffer-keyboard-quit undo-tree-undo)) (ding)))) -;;;;; Colours - -(load-theme 'spacemacs-light t) - -;; Highlighting quasi-quoted expressions in lisps is quite useful, but I -;; don't need it all the time. I'll keep it around for a while so that I -;; can enable it if needed. -(use-package highlight-stages) +(when (or (daemonp) + window-system) + (load-theme 'eink t)) (global-hl-line-mode +1) -(define-hook-helper first-frame () - (let ((line (face-attribute 'mode-line :underline))) - (set-face-attribute 'mode-line nil :overline line) - (set-face-attribute 'mode-line-inactive nil :overline line) - (set-face-attribute 'mode-line-inactive nil :underline line) - (set-face-attribute 'mode-line nil :box nil) - (set-face-attribute 'mode-line-inactive nil :box nil) - (set-face-attribute 'mode-line-inactive nil :background (face-attribute 'hl-line :background)))) - -;;;;; Fonts - -;; When possible, set up fonts. I don’t have any settings here for X11, -;; because I manage those in my [[file:~/projects/dotfiles/tag-xresources/xresources/main][XResources file]]. -(when (or (display-graphic-p) - (daemonp)) - - (setq-default line-spacing 0.2) - - (defun use-variable-fonts () - (interactive) - (variable-pitch-mode) - (setq cursor-type '(bar . 1))) - - (defun use-fixed-fonts () - (interactive) - (variable-pitch-mode -1) - (setq cursor-type 'box)) - - (defun ap/set-fonts (mono-face mono-font-size variable-face variable-font-size antialias &optional new-line-spacing) - (if (boundp 'ns-antialias-text) - (setq ns-antialias-text antialias)) - (if (boundp 'new-line-spacing) - (setq line-spacing new-line-spacing)) - (when mono-face - (let ((default-font (font-spec :family mono-face :size mono-font-size))) - (add-to-list 'default-frame-alist `(font . ,(format "%s %s" mono-face mono-font-size))) - (set-face-font 'fixed-pitch default-font) - (set-frame-font default-font t t))) - (when variable-face - (set-face-font 'variable-pitch (font-spec :name variable-face :size variable-font-size)))) - - (defun ap/set-fonts-according-to-system () - (interactive) - (cond - ((eq window-system 'w32) - (ap/set-fonts "Liberation Mono" 11 "Segoe UI" 11 t)) - ((or (eq window-system 'mac) - (eq window-system 'ns)) - (let ((displays (string-to-number (shell-command-to-string "system_profiler SPDisplaysDataType | grep \"Online: Yes\" | wc -l")))) - (ap/set-fonts "SF Mono" 12 "Helvetica Neue" 12 t 0.1))) - ((and (eq window-system 'x) - (eq system-type 'gnu/linux/windows)) - (ap/set-fonts "Noto Mono" 12 "Sans" 12 nil))))) - -(add-hook 'first-frame-hook #'ap/set-fonts-according-to-system) - -;; Reduce font decoration. I’m trying to see whether this helps me focus -;; on the right things. -(setq font-lock-maximum-decoration '((dired-mode . 1) - (t . 1))) - -;; Make symbols prettier. Turns out, in many cases, this is already -;; configured, just not enabled. If using the mac-port version of Emacs, -;; it has it's own, more extensive version. - -(if (eq window-system 'mac) - (if (fboundp 'mac-auto-operator-composition-mode) - (mac-auto-operator-composition-mode +1)) - (global-prettify-symbols-mode +1)) - -;;;;; Page Breaks - -;; By default, Emacs displays page breaks as ^L. Lines look much nicer. -;; On Windows, Emacs incorrectly detects that U+2500 (Box Drawings Light -;; Horizontal) can only be displayed with a different font, which is not -;; correct, at least for Liberation Mono. - -(use-package page-break-lines - :defer 5 - :config (progn - (global-page-break-lines-mode) - (unless (eq (char-displayable-p ?─) (char-displayable-p ?a)) - (set-fontset-font "fontset-default" - (cons page-break-lines-char page-break-lines-char) - (face-attribute 'default :family))))) -(require 'f) -(setq frame-title-format (list "Emacs")) - -;;;;; Chrome - -(setq-default cursor-in-non-selected-windows nil) - -(add-to-list 'default-frame-alist '(border-width . 0)) -(add-to-list 'default-frame-alist '(internal-border-width . 0)) -(when (eq system-type 'darwin) - (add-to-list 'default-frame-alist '(ns-transparent-titlebar . t)) - (add-to-list 'default-frame-alist '(ns-appearance . light))) -(when (or (eq window-system 'x) - (eq window-system 'mac)) - (setq window-divider-default-bottom-width 1 - window-divider-default-right-width 1 - window-divider-default-places t) - (window-divider-mode +1)) - +;;; Chrome (use-package minions - :config (progn - (setq minions-mode-line-lighter "#") - (minions-mode +1))) + :custom ((minions-mode-line-lighter "#") + (minions-mode t))) (use-package moody :config (progn @@ -263,48 +55,59 @@ (moody-replace-mode-line-buffer-identification) (moody-replace-vc-mode))) -;;;; Environment Variables - -;; MacOS doesn’t have a reasonable way to set environment variables and -;; read them automatically any more. So, let’s use the -;; [[https://github.com/purcell/exec-path-from-shell][exec-path-from-shell]] package to set up ~exec-path~ and similar -;; variables from whatever my shell configuration is. +(setq frame-title-format (list "Emacs")) -;; On Windows, I like to run Emacs from the system tray menu of VcXsrv. -;; It starts up without an environment in this case as well. +;;; Windows -(use-package exec-path-from-shell - :if (or (eq system-type 'darwin) - (eq system-type 'gnu/linux/windows) - (and (eq system-type 'gnu/linux) - (daemonp))) - :config (progn - (setq exec-path-from-shell-arguments '("-l")) - (exec-path-from-shell-initialize))) +(use-package eyebrowse + :after (evil) + :custom ((eyebrowse-new-workspace #'counsel-projectile-switch-project) + (eyebrowse-mode t)) + :general (evil-window-map + "0" #'eyebrowse-switch-to-window-config-0 + "1" #'eyebrowse-switch-to-window-config-1 + "2" #'eyebrowse-switch-to-window-config-2 + "3" #'eyebrowse-switch-to-window-config-3 + "4" #'eyebrowse-switch-to-window-config-4 + "5" #'eyebrowse-switch-to-window-config-5 + "6" #'eyebrowse-switch-to-window-config-6 + "7" #'eyebrowse-switch-to-window-config-7 + "8" #'eyebrowse-switch-to-window-config-8 + "9" #'eyebrowse-switch-to-window-config-9) + :ghook ('evil-after-load-hook #'eyebrowse-setup-evil-keys)) -(with-eval-after-load 'browse-url - (when (executable-find "xdg-open") - (setq browse-url-browser-function #'browse-url-xdg-open))) +(use-package winner + :custom ((winner-boring-buffers '("*Completions*" "*Help*" "*Apropos*" "*Buffer List*" "*info*" "*Compile-Log*"))) + :general (:keymap evil-window-map + "u" #'winner-undo + "r" #'winner-redo + "C-r" #'winner-redo)) +;;; Dates & Times -;;;;; NixOS sandboxes +(custom-set-variables + '(calendar-week-start-day 1) + '(calendar-date-style 'iso)) -;; I'm currently exploring using nix to create sandboxes for -;; development. This package allows using tools from inside sandboxes, -;; and some convenience commands for building packages and launching shells. +(defun insert-date (prefix) + "Insert the current date. +With PREFIX, use British format. +With two prefix arguments, write out the day and month name." + (interactive "P") + (let ((format (cond + ((not prefix) "%Y-%m-%d") + ((equal prefix '(4)) "%d/%m/%Y") + ((equal prefix '(16)) "%A, %d %B %Y")))) + (insert (format-time-string format)))) -(use-package nix-sandbox - :defines (flycheck-command-wrapper-function - flycheck-executable-find) - :config (progn - (with-eval-after-load 'flycheck - (setq flycheck-command-wrapper-function - (lambda (cmd) - (apply 'nix-shell-command (nix-current-sandbox) - (list (mapconcat 'shell-quote-argument cmd " ")))) - flycheck-executable-find - (lambda (cmd) (nix-executable-find (nix-current-sandbox) cmd)))))) +(defun insert-datetime (prefix) + "Insert current date and time. With PREFIX, use ISO8601 format." + (interactive "P") + (let ((format (cond + ((not prefix) "%Y-%m-%d %H:%M:%S") + ((equal prefix '(4)) "%Y-%m-%dT%H:%M:%SZ")))) + (insert (format-time-string format)))) -;;;; Keybindings +;;; Keybindings ;; I think =set-keyboard-coding-system= stops OS X from doing something ;; annoying to add accents. The modifier setup is to match my @@ -312,7 +115,6 @@ ;; Option/alt, then Control. (when (eq system-type 'darwin) - (set-keyboard-coding-system nil) (custom-set-variables '(mac-option-modifier 'meta) '(mac-right-option-modifier 'none) @@ -320,241 +122,120 @@ '(mac-right-control-modifier 'left) '(mac-command-modifier 'super) '(mac-right-command-modifier 'left) - '(mac-function-modifier 'hyper)) - (unbind-key "s-x")) + '(mac-function-modifier 'hyper))) -(unbind-key "<f4>") -(bind-key* "<f5>" #'compile) -(bind-key* "<f6>" #'kmacro-start-macro-or-insert-counter) -(bind-key* "<f7>" #'kmacro-end-or-call-macro) -(bind-key* "<apps>" #'execute-extended-command) +(use-package general + :functions (general-unbind general-define-key) + :config (progn + (when (eq system-type 'darwin) + (general-unbind "s-x")))) -(unbind-key "C-x C-c") +(use-package ace-link + :after avy + :config (ace-link-setup-default)) -(bind-key* "C-c i" #'insert-char) -(bind-key* "M-/" #'hippie-expand) +;; Popup keybindings following a prefix automatically. -(unbind-key "s-h") -(unbind-key "s-n") -(unbind-key "s-p") -(unbind-key "s-w") +(use-package which-key + :custom ((which-key-mode +1)) + :config (progn + (which-key-setup-side-window-right-bottom))) -(bind-key* "s-x" (define-prefix-command 'super-x-map)) -(bind-key* "s-," #'switch-to-dotfiles) -(bind-key* "C-x M-x" #'execute-extended-command) +;;; Modeline -;;;;; Crux +(use-package ivy + :config (progn + (ivy-mode +1))) +(use-package ivy-hydra) -;; I can replace most of the simple helper/wrapper functions in my -;; configuration with crux.el +(use-package relative-buffers + :custom ((global-relative-buffers-mode t))) -(use-package crux - :bind (("C-x 4 t" . crux-transpose-windows) - ("C-c e" . crux-eval-and-replace) - ("C-c D" . crux-delete-file-and-buffer) - ("C-c R" . crux-rename-file-and-buffer))) +;; transition smex history to amx +(let ((smex-save-file (concat user-emacs-directory "smex-items"))) + (use-package amx + :custom ((amx-history-length 100)))) -;;;; Projects +(use-package counsel + :general ("M-x" #'counsel-M-x)) -(defun switch-to-dotfiles () - "Switch to dotfiles project." - (interactive) - (projectile-switch-project-by-name (car (split-string (shell-command-to-string "ghq list --full-path dotfiles"))))) +;;; Minibuffer -;;;;; Grep +(setq enable-recursive-minibuffers t) +(minibuffer-depth-indicate-mode t) -(use-package grep - :config (progn - (dolist (v '("node_modules" - "bower_components" - ".sass_cache" - ".cache" - ".npm")) - (add-to-list 'grep-find-ignored-directories v)) - (dolist (v '("*.min.js" - "*.bundle.js" - "*.min.css" - "*.lock" - "package-lock.json" - "*.log")) - (add-to-list 'grep-find-ignored-files v)))) - -;;;;; The Silver Searcher - -(use-package ag - :defer 30 - :config (progn - (setq ag-project-root-function #'projectile-project-root) - (add-to-list 'ag-arguments "--hidden"))) +;;; Evil -(use-package wgrep-ag - :after ag) +(use-package evil + :demand t + :init (custom-set-variables '(evil-want-integration t) + '(evil-want-keybinding nil)) + :after (undo-tree) + :custom ((evil-shift-width 2) + (evil-mode-line-format '(before . mode-line-front-space))) + :general + (:states 'motion + "C-;" #'evil-avy-goto-line) + (:states 'normal + ";" #'evil-ex) + :config (progn + (evil-mode +1))) -;;;;; Ripgrep +(use-package evil-collection + :config (evil-collection-init)) -;; Step over Silver Search, here comes a new challenger. +(general-create-definer my-leader-def + :prefix ",") -(use-package ripgrep - :if (executable-find "rg") +(use-package evil-space + :defer 1 :config (progn - (setq ripgrep-arguments '("--hidden")))) + (evil-space-mode))) -(use-package projectile-ripgrep - :after (ripgrep projectile) - :if (executable-find "rg") - :config (bind-key "s r" #'projectile-ripgrep projectile-command-map)) +(use-package evil-surround + :custom ((global-evil-surround-mode t))) -;;;;; Projectile +(use-package evil-commentary + :custom ((evil-commentary-mode t))) -;; Projectile is awesome for working in projects, especially VCS-backed -;; ones. +(use-package evil-magit + :after magit + :custom ((evil-magit-use-y-for-yank nil))) -(add-to-list 'byte-compile-not-obsolete-funcs 'projectile-global-mode) -(use-package projectile - :bind (("s-p" . projectile-switch-project) - ("C-c C-f" . projectile-find-file) - ("s-x s-f" . projectile-find-file) - ("C-x g" . projectile-vc) - ("C-M-g" . projectile-vc)) - :demand t - :config (progn - (projectile-global-mode +1) - (add-to-list 'projectile-globally-ignored-directories ".stversions") - (add-to-list 'projectile-globally-ignored-directories "node_modules") - (add-to-list 'projectile-globally-ignored-files "package-lock.json") - (setq projectile-mode-line "P") - - (defun yarn-install (&optional arg) - (interactive "P") - (projectile-with-default-dir (projectile-project-root) - (cond - ((string-equal (projectile-project-type) "node-yarn") - (cmd-to-echo "yarn" "install")) - (t (cmd-to-echo "npm" "install"))))) - (defalias 'npm-install #'yarn-install) - - (defun yarn-add-dev (package) - (interactive "spackage: ") - (projectile-with-default-dir (projectile-project-root) - (cond - ((string-equal (projectile-project-type) "node-yarn") - (cmd-to-echo "yarn" (concat "add --dev " package))) - (t (cmd-to-echo "npm" (concat "install --save-dev " package)))))) - (defalias 'npm-save-dev #'yarn-add-dev) - - (defun yarn-add (package) - (interactive "spackage: ") - (projectile-with-default-dir (projectile-project-root) - (cond - ((string-equal (projectile-project-type) "node-yarn") - (cmd-to-echo "yarn" (concat "add " package))) - (t (cmd-to-echo "npm" (concat "install --save " package)))))) - (defalias 'npm-save #'yarn-add) - - (defun yarn-remove (package) - (interactive "spackage: ") - (projectile-with-default-dir (projectile-project-root) - (cond - ((string-equal (projectile-project-type) "node-yarn") - (cmd-to-echo "yarn" (concat "remove " package))) - (t (cmd-to-echo "npm" (concat "remove " package)))))) - (defalias 'npm-remove #'yarn-remove) - - (defun yarn-run (cmd) - (interactive (list - (projectile-completing-read "command: " (alist-get 'scripts (json-read-file (expand-file-name "package.json" (projectile-project-root))))))) - (projectile-with-default-dir (projectile-project-root) - (cond - ((string-equal (projectile-project-type) "node-yarn") - (cmd-to-echo "yarn" (concat "run " cmd))) - (t (cmd-to-echo "npm" (concat "run " cmd)))))) - (defalias 'npm-run #'yarn-run) - - (defun npx-run (cmd) - (interactive "scommand: ") - (projectile-with-default-dir (projectile-project-root) - (cmd-to-echo "npx" cmd))) - - (defun npm-version (type) - (interactive (list - (projectile-completing-read "version: " '("major" "minor" "patch" "premajor" "preminor" "prepatch" "prerelease" "from-git")))) - (projectile-with-default-dir (projectile-project-root) - (message (shell-command-to-string (concat "npm" " version " type))))) - - (defun ap/open-project (&optional arg) - (interactive "P") - (let ((project-dir (projectile-completing-read - "Open project: " - (ghq--find-projects-full-path)))) - (projectile-switch-project-by-name - project-dir arg))) - - (defun git-bug (bug) - (interactive "sbug: ") - (projectile-with-default-dir (projectile-project-root) - (call-process-shell-command (concat "git bug " bug)))) - - (defun git-feature (feature) - (interactive "sfeature: ") - (projectile-with-default-dir (projectile-project-root) - (call-process-shell-command (concat "git feature " feature)))) - - (defun git-chore (chore) - (interactive "schore: ") - (projectile-with-default-dir (projectile-project-root) - (call-process-shell-command (concat "git chore " chore)))) - - (setq projectile-switch-project-action #'projectile-commander - projectile-completion-system 'ivy - projectile-create-missing-test-files t) - - (defun ap/projectile-test-suffix (project-type) - (cond - ((member project-type '(node-yarn node-npm)) ".test") - (t (projectile-test-suffix project-type)))) - (setq projectile-test-suffix-function #'ap/projectile-test-suffix) - - (projectile-register-project-type 'node-yarn '("yarn.lock") :run "yarn start" :test "yarn test") - (projectile-register-project-type 'node '("package.json") :run "npm start" :test "npm test"))) +(use-package evil-quickscope + :custom ((global-evil-quickscope-mode t))) -(use-package counsel-projectile - :after (counsel projectile ivy-hydra) - :config (progn - (counsel-projectile-mode +1) - (def-projectile-commander-method ?A - "Find rg on project." - (call-interactively #'counsel-projectile-rg)) - (if (boundp 'counsel-projectile-command-map) - (define-key counsel-projectile-command-map (kbd "s s") #'counsel-projectile-rg)))) +(use-package evil-org + :commands (evil-org-set-key-theme) + :ghook ('org-mode-hook #'evil-org-mode) + :gfhook #'evil-org-set-key-theme) -;;;;; vc +(use-package evil-org-agenda + :ghook ('org-agenda-mode-hook #'evil-org-agenda-set-keys)) -;; This is nice for some things that magit doesn’t do, and for those rare -;; occasions that I’m working with something other than git. +;;; Projects -(use-package vc - :defer t - :bind (("C-x v C" . vc-resolve-conflicts)) - :config (progn - (setq vc-follow-symlinks t) - (setq vc-ignore-dir-regexp (format "\\(%s\\)\\|\\(%s\\)" - vc-ignore-dir-regexp - tramp-file-name-regexp)))) +(use-package projectile + :defines projectile-command-map + :custom ((projectile-mode +1) + (projectile-completion-system 'ivy))) -;;;;; git-gutter-fringe +(use-package counsel-projectile + :after (counsel projectile ivy-hydra) + :custom ((counsel-projectile-mode +1))) -;; It’s nice to be able to see at a glance which lines of a file have -;; changed. This package colours the fringe. I have it set to the right -;; fringe so it doesn’t interfere with flycheck. +(use-package magit + :custom ((global-magit-file-mode +1) + (magit-completing-read-function #'ivy-completing-read))) (eval-when-compile (require 'fringe-helper)) +(use-package git-gutter) (use-package git-gutter-fringe - :defer 2 :config (progn (global-git-gutter-mode 1) ;; places the git gutter outside the margins. - (setq-default fringes-outside-margins t) + (setq-default fringes-outside-margins nil) ;; thin fringe bitmaps (fringe-helper-define 'git-gutter-fr:added '(center repeated) ".XXX....") @@ -564,60 +245,70 @@ ".XXX....") (setq git-gutter-fr:side 'right-fringe))) -;;;;; magit - -;; Magit is my favourite way to use git. I use selective staging all the -;; time. Make sure to set it up with a nice =completing-read-function= - -(use-package magit +(use-package git-messenger :defer 5 - :commands (magit-status) - :config (progn (setq magit-completing-read-function #'ivy-completing-read - magit-popup-use-prefix-argument 'default - magit-repository-directories (list (cons (file-name-as-directory (ghq--find-root)) 3)) - magit-display-buffer-function #'magit-display-buffer-fullcolumn-most-v1 - global-magit-file-mode t) - (add-to-list 'magit-no-confirm 'safe-with-wip)) - :init (add-hook 'magit-mode-hook #'magit-load-config-extensions)) + :custom ((git-messenger:use-magit-popup t))) -;;;;; git-messenger +(use-package git-timemachine) + +(use-package editorconfig + :init (progn + (unless (executable-find "editorconfig") + (warn "Missing `editorconfig' executable."))) + :config (editorconfig-mode +1)) -;; Popup the last commit that changed the line at point. +;;; Completion -(use-package git-messenger - :bind* (("C-x v p" . git-messenger:popup-message)) - :config (progn - (setq git-messenger:use-magit-popup t))) +(use-package company + :custom ((global-company-mode +1) + (company-idle-delay .2) + (company-begin-commands '(self-insert-command)) + (company-auto-complete #'company-explicit-action-p) + (company-auto-complete-chars '(?\ ?\( ?\) ?.))) + :general (:states 'insert + "TAB" #'company-complete)) -;;;;; git-timemachine +(use-package company-box + :custom ((company-box-enable-icon nil)) + :ghook 'company-mode-hook) -;; This package allow me to go through a file’s history with just a few -;; keys. It makes it very easy to figure what what exactly was in a file -;; in the past. I often find it useful when I remember writing something -;; a particular way, but it changed later. +;;; Documentation -(use-package git-timemachine - :commands git-timemachine) +(use-package eldoc + :custom ((global-eldoc-mode +1) + (eldoc-idle-delay 0.5))) -;;;;; ghq +(use-package eldoc-box + :after (eldoc eglot) + :custom ((eldoc-box-hover-mode +1) + (eldoc-box-hover-at-point-mode +1))) -;; [[https://github.com/motemen/ghq][=ghq=]] clones VCS-backed projects to a common directory. It should -;; seem familiar to anyone who's used =go get= before. [[https://github.com/rcoedo/emacs-ghq][=emacs-ghq=]] is a -;; simple wrapper for it. +(use-package ehelp) -(use-package ghq - :if (executable-find "ghq")) +(use-package helpful + :after ehelp + :general (ehelp-map + "k" #'helpful-key + "v" #'helpful-variable + "f" #'helpful-callable)) -;;;; Files +;;; Files -;;;;; Auto-saving +;;;; Auto-saving ;; Auto-save everything to a temporary directory, instead of cluttering ;; the filesystem. I don’t want emacs-specific lockfiles, either. (setq auto-save-file-name-transforms `((".*" ,temporary-file-directory t)) create-lockfiles nil) -;;;;; Backups + +;;;; Auto-reloading + +(use-package autorevert + :custom ((global-auto-revert-mode t) + (auto-revert-verbose t))) + +;;;; Backups ;; I like to keep my backups out of regular folders. I tell emacs to use ;; a subfolder of its configuration directory for that. Also, use the @@ -628,6 +319,7 @@ (setq backup-directory-alist `((".*" . ,backup-dir)) backup-by-copying-when-linked t backup-by-copying-when-mismatch t)) + (if (eq system-type 'darwin) (setq delete-by-moving-to-trash t) (if (and (executable-find "trash") (not (fboundp #'system-move-file-to-trash))) @@ -636,1420 +328,54 @@ nil 0 nil file)))) -;;;;; autorevert - -(use-package autorevert - :init (progn - (global-auto-revert-mode 1) - (setq auto-revert-verbose t - auto-revert-use-notify (not (eq system-type 'darwin))))) - -;;;;; Encoding - -;; UTF-8 is usually appropriate. Note that =prefer-coding-system= expects -;; only a coding system, not a coding system and line ending combination. - -(prefer-coding-system 'utf-8) -(setq-default buffer-file-coding-system 'utf-8-auto-unix) - -;;;;; Buffer-file management - -;; Ask if I want to create a directory when it doesn’t exist. This is -;; especially nice when starting new projects. - -(defun my-create-non-existent-directory () - "Offer to create non-existent directories of found-file." - (let ((parent-directory (file-name-directory buffer-file-name))) - (when (and (not (file-exists-p parent-directory)) - (y-or-n-p (format "Directory `%s' does not exist! Create it? " parent-directory))) - (make-directory parent-directory t)))) -(add-to-list 'find-file-not-found-functions #'my-create-non-existent-directory) - -;; I often want to rename or delete the file that I’m currently visiting -;; with a buffer. - -(defun kill-or-delete-this-buffer-dwim (&optional arg) - "Kill current buffer. With prefix ARG, delete it." - (interactive "P") - (if (equal arg '(4)) - (crux-delete-file-and-buffer) - (if server-buffer-clients - (server-edit) - (let ((buf (buffer-name))) - (when (equal buf "*HTTP Response*") - (other-window 1)) - (kill-buffer buf))))) - -;;;;; Whitespace - -;; Show bad whitespace, so that I can fix it. - -(defun show-trailing-whitespace-on () - "Show trailing whitespace." - (interactive) - (setq-local show-trailing-whitespace t)) -(defun show-trailing-whitespace-off () - "Hide trailing whitespace." - (interactive) - (setq-local show-trailing-whitespace nil)) -(add-hook 'prog-mode-hook #'show-trailing-whitespace-on) -(add-hook 'text-mode-hook #'show-trailing-whitespace-on) - -;;;;; shrink-whitespace - -;; DWIM whitespace removal. So I don’t need =M-SPC=, =M-\= and =C-x o= -;; for similar things any more. - -(use-package shrink-whitespace - :bind ("M-SPC" . shrink-whitespace)) - -;;;;; Tramp - -;; Tramp is awesome. It makes SSH feel Unix-y. The proxy setup is so -;; that I can sudo on remote machines - -(use-package tramp - :defer 7 - :defines tramp-ssh-controlmaster-options - :config (progn - (unless (and (getenv "SSH_AUTH_SOCK") - (file-exists-p (getenv "SSH_AUTH_SOCK"))) - (setenv "SSH_AUTH_SOCK" (format "/run/user/%s/gnupg/S.gpg-agent.ssh" (user-uid)))) - (setq tramp-default-method "ssh" - tramp-default-user-alist '(("\\`su\\(do\\)?\\'" nil "root")) - tramp-backup-directory-alist backup-directory-alist - tramp-completion-reread-directory-timeout 60 - tramp-ssh-controlmaster-options nil - backup-enable-predicate (lambda (name) - (and (normal-backup-enable-predicate name) - (not (let ((method (file-remote-p name 'method))) - (when (stringp method) - (member method '("su" "sudo"))))))) - tramp-shell-prompt-pattern "\\(?:^\\| \\)[^]#$%>\n]*#?[]#$%>❯›] *\\(\\[\\??[0-9;]*[a-zA-Z] *\\)*") - (add-to-list 'tramp-default-proxies-alist '(nil "\\`root\\'" (concat "/" tramp-default-method ":%h:"))) - (add-to-list 'tramp-default-proxies-alist `(,(regexp-quote (system-name)) nil nil)) - (add-to-list 'tramp-default-proxies-alist '("localhost" nil nil)))) - -;;;;; ediff - -;; I like a horizonal diff setup, with everything in one frame. - -(use-package ediff - :defer t - :config (progn - (setq ediff-split-window-function 'split-window-horizontally - ediff-window-setup-function 'ediff-setup-windows-plain))) - -;;;; Indentation - -;; Ah, a complicated topic. One day we’ll all be using elastic -;; tabstops. I’ve recently switched to using two spaces, since elastic -;; tabstops is probably never going to happen. - -(setq-default tab-width 2 - indent-tabs-mode nil) - -;;;;; smart-tabs-mode - -;; Not related to [[smart-tab][=smart-tab=]], this mode indents with tabs and aligns -;; with spaces. Perfect! - -(use-package smart-tabs-mode - :defer 1 - :config (progn - (smart-tabs-insinuate 'c 'cperl 'python))) - -;;;;; editorconfig - -(use-package editorconfig - :init (progn - (unless (executable-find "editorconfig") - (warn "Missing `editorconfig' executable."))) - :config (editorconfig-mode 1)) - -;;;;; dtrt-indent-mode - -;; Sometimes people use different indentation settings. [[https://github.com/jscheid/dtrt-indent][dtrt-indent]] -;; guesses the correct settings for me. - -(use-package dtrt-indent - :config (progn - (define-hook-helper prog-mode () - (unless (and (boundp editorconfig-mode) - editorconfig-mode) - (dtrt-indent-adapt))) - (defadvice dtrt-indent-try-set-offset (after toggle-smart-tabs activate) - (smart-tabs-mode (or indent-tabs-mode -1))))) - -;;;; Security - -;;;;; password-store - -;; This is a frontend to the GPG-powered =pass= program. -(use-package password-store - :defer 15 - :config (progn - (setq password-store-password-length 16))) - -;;;; Buffers - -;;;;; Ibuffer -;; Ibuffer is quite nice for listing all buffers. - -(use-package ibuffer - :bind (("C-x C-b" . ibuffer)) - :config (progn - (setq ibuffer-saved-filter-groups - (quote (("default" - ("org" (mode . org-mode)) - ("emacs" (mode . emacs-lisp-mode)) - ("zsh" (filename . "/zsh")) - ("server" (filename . "/su:root@server")))))) - - ;; Human-readable base-2 size column - (define-ibuffer-column size-h - (:name "Size" :inline t) - (cond - ((> (buffer-size) 1024) - (format "%7.2fK" (/ (buffer-size) 1024.0))) - ((> (buffer-size) 1048576) - (format "%7.2fM" (/ (buffer-size) 1048576.0))) - (t - (format "%8d" (buffer-size))))) - - (setq ibuffer-formats - '((mark modified read-only " " - (name 18 18 :left :elide) - " " - (size-h 9 -1 :right) - " " - (mode 16 16 :left :elide) - " " - filename-and-process))))) - -;;;;; Relative Buffer names - -(use-package relative-buffers - :defer 15 - :config (progn - (global-relative-buffers-mode))) - -;;;;; Narrowing - -;; Enable it without prompting - -(put 'narrow-to-defun 'disabled nil) -(put 'narrow-to-page 'disabled nil) -(put 'narrow-to-region 'disabled nil) - -;;;; Windows - -;; Scrolling is tricky. I use this setup to help me keep track of the -;; point whilst I’m moving about. - -(setq scroll-conservatively 100 - scroll-margin 1 - scroll-preserve-screen-position t - mouse-wheel-scroll-amount '(1 ((shift) . 1) ((control))) - split-height-threshold 80 - split-width-threshold 160 - frame-resize-pixelwise nil) -(if (boundp 'ns-pop-up-frames) - (setq ns-pop-up-frames nil)) - -(when (version<= "26.0" emacs-version) - (use-package display-line-numbers - :config (progn - (setq display-line-numbers-type 'visual) - (global-display-line-numbers-mode +1)))) - -;;;;; eyebrowse - -;; Workspaces, a bit like dwm. On Windows and Linux (at least the WMs -;; I'm likely to use), super+{0-9} are taken from the OS, so use meta -;; instead. On macOS, super makes a lot of sense, as it's used by most -;; programs to switch between program windows or views. - -(use-package eyebrowse - :after (evil) - :config (progn - (setq eyebrowse-new-workspace t) - (when (eq system-type 'darwin) - (bind-keys - ("s-0" . eyebrowse-switch-to-window-config-0) - ("s-1" . eyebrowse-switch-to-window-config-1) - ("s-2" . eyebrowse-switch-to-window-config-2) - ("s-3" . eyebrowse-switch-to-window-config-3) - ("s-4" . eyebrowse-switch-to-window-config-4) - ("s-5" . eyebrowse-switch-to-window-config-5) - ("s-6" . eyebrowse-switch-to-window-config-6) - ("s-7" . eyebrowse-switch-to-window-config-7) - ("s-8" . eyebrowse-switch-to-window-config-8) - ("s-9" . eyebrowse-switch-to-window-config-9))) - (eyebrowse-mode +1) - (with-eval-after-load "evil-vars" - (bind-keys :map evil-window-map - ("0" . eyebrowse-switch-to-window-config-0) - ("1" . eyebrowse-switch-to-window-config-1) - ("2" . eyebrowse-switch-to-window-config-2) - ("3" . eyebrowse-switch-to-window-config-3) - ("4" . eyebrowse-switch-to-window-config-4) - ("5" . eyebrowse-switch-to-window-config-5) - ("6" . eyebrowse-switch-to-window-config-6) - ("7" . eyebrowse-switch-to-window-config-7) - ("8" . eyebrowse-switch-to-window-config-8) - ("9" . eyebrowse-switch-to-window-config-9))) - (define-hook-helper evil-after-load () - :name evil-eyebrowse-setup - (eyebrowse-setup-evil-keys)))) - -;;;; Sessions - -;;;;; winner - -;; Undo, for window-based commands. - -(use-package winner - :config (setq winner-boring-buffers '("*Completions*" "*Help*" "*Apropos*" "*Buffer List*" "*info*" "*Compile-Log*")) - :init (progn - (winner-mode 1))) - -;;;; Blogging - -;; I have a [[https://alanpearce.uk][blog]] that I publish with hugo. - -(use-package easy-hugo - :config (setq easy-hugo-basedir (car (split-string (shell-command-to-string "ghq list --full-path alanpearce.uk"))) - easy-hugo-url "https://alanpearce.uk" - easy-hugo-default-ext ".md")) - -;;;; Completion - -;; Make built-in completion a bit more intelligent, by adding substring -;; and initial-based completion and ignoring case. - -(setq completion-styles '(basic initials partial-completion substring) - completion-ignore-case t - tab-always-indent t) - -;;;;; Company - -;; The main choices for automatic completion in Emacs are company and -;; auto-complete-mode. I’ve not tried auto-complete-mode as company -;; seems to work perfectly well for me. - -(use-package company - :bind* (("C-<tab>" . company-complete)) - :bind (("TAB" . company-complete)) - :init (progn - (setq company-backends '(company-web-html company-tide company-tern company-nxml company-css company-eclim company-semantic company-elisp - company-clang company-xcode company-cmake company-capf - company-files (company-gtags - company-etags company-keywords) company-oddmuse) - company-frontends '(company-pseudo-tooltip-unless-just-one-frontend - company-preview-frontend - company-echo-metadata-frontend) - company-idle-delay .3 - company-begin-commands '(self-insert-command) - company-auto-complete #'company-explicit-action-p - company-auto-complete-chars '(?\ ?\( ?\) ?.) - company-tooltip-align-annotations t - company-selection-wrap-around t) - (global-company-mode +1))) - -(use-package company-web - :after company) - -;;;;; EACL - -;; Auto-complete lines by grepping the project. - -(use-package eacl - :bind (("C-c <tab>" . eacl-complete-line) - ("C-c C-;" . eacl-complete-statement) - ("C-c C-\]" . eacl-complete-snippet) - ("C-c C-/" . eacl-complete-tag))) - - -;;;; Dates & Times - -;;;;; Calendar - -;; Weeks start on Monday for me and I prefer ISO-style dates. -(use-package calendar +(use-package undo-tree :defer 1 - :config (progn - (setq calendar-week-start-day 1) - (calendar-set-date-style 'iso))) - -;; Sometimes I want to insert a date or time into a buffer. -(defun insert-date (prefix) - "Insert the current date. -With PREFIX, use British format. -With two prefix arguments, write out the day and month name." - (interactive "P") - (let ((format (cond - ((not prefix) "%Y-%m-%d") - ((equal prefix '(4)) "%d/%m/%Y") - ((equal prefix '(16)) "%A, %d %B %Y")))) - (insert (format-time-string format)))) - -(defun insert-datetime (prefix) - "Insert current date and time. With PREFIX, use ISO8601 format." - (interactive "P") - (let ((format (cond - ((not prefix) "%Y-%m-%d %H:%M:%S") - ((equal prefix '(4)) "%Y-%m-%dT%H:%M:%SZ")))) - (insert (format-time-string format)))) - -(defun yesterday-time () - "Provide the date/time 24 hours before the time now in the format of `current-time'." - (timer-relative-time (current-time) -86400)) - -;;;; Directories - -;; Dired works quite nicely, but not always in the way I want. I don’t -;; like having so many prompts for recursive operations. Also, when I -;; have two dired windows open, assume that I’m going to be -;; copying/moving files between them. - -(use-package dired - :defer 3 - :ensure nil - :config (progn - (bind-key "<return>" #'dired-find-file dired-mode-map) - (bind-key "^" (lambda () (interactive) (find-alternate-file "..")) dired-mode-map) - (setq dired-dwim-target t - dired-recursive-copies 'top - dired-recursive-deletes (if delete-by-moving-to-trash - 'always - 'top) - dired-listing-switches "-alh") - (when (and (eq system-type 'darwin) (executable-find "gls")) - (setq insert-directory-program (executable-find "gls"))) - (put 'dired-find-alternate-file 'disabled nil))) - -;; Don’t show uninteresting files in dired listings. - -(defun turn-on-dired-omit-mode () - "Enable dired-omit mode." - (interactive) - (dired-omit-mode 1)) - -(use-package dired-x - :commands (dired-omit-mode - dired-expunge) - :ensure nil - :config (progn - (setq dired-omit-files "#\\|\\.$" - dired-omit-verbose nil - dired-find-subdir t - dired-bind-jump nil)) - :init (progn - (add-hook 'dired-mode-hook #'turn-on-dired-omit-mode))) - -;; Expand subfolders like a tree inside the parent - -(with-eval-after-load 'dired - (use-package dired-subtree - :functions (dired-subtree--get-ov - dired-subtree-maybe-up) - :init (progn - (setq dired-subtree-use-backgrounds nil) - (defun dired-subtree-maybe-up () - "Jump up one subtree or directory" - (interactive) - (let ((ov (dired-subtree--get-ov))) - (if ov - (progn (goto-char (overlay-start ov)) - (dired-previous-line 1)) - (dired-up-directory)))) - (bind-key "^" #'dired-subtree-maybe-up dired-mode-map) - (bind-key "i" #'dired-subtree-toggle dired-mode-map)))) - -;;;;; Disk usage - -;; Combine dired and du (disk usage). - -(use-package dired-du - :after dired - :config (progn - (setq dired-du-size-format t))) - -;;;; Documentation - -;;;;; ehelp - -;; ehelp is a less well-known package that’s part of Emacs and slightly -;; improves the normal help commands, mostly by making quitting them easier. - -(use-package ehelp - :bind-keymap ("C-h" . ehelp-map)) - -;;;;; helpful - -(use-package helpful - :after ehelp - :bind (:map ehelp-map - ("k" . helpful-key) - ("v" . helpful-variable) - ("f" . helpful-callable))) - -;;;;; discover-my-major - -;; A nicer way to browse keybindings for major modes. - -(use-package discover-my-major - :bind (("<f1>" . discover-my-major) - ("C-c C-m" . discover-my-major))) - -;;;;; which-key - -;; Popup keybindings following a prefix automatically. - -(use-package which-key - :config (progn - (which-key-mode 1) - (which-key-setup-side-window-right-bottom))) - -;;;;; eldoc - -;; Documentation in the echo-area (where the minibuffer is displayed) is -;; rather useful. - -(use-package eldoc - :commands (eldoc-mode global-eldoc-mode) - :config (progn - (setq eldoc-idle-delay 0.1) - (global-eldoc-mode +1) - (eldoc-add-command 'paredit-backward-delete 'paredit-close-round))) -;;;; Mail - -;;;;; Basics - -(with-eval-after-load "mailcap" - (when (eq system-type 'darwin) - (mailcap-add-mailcap-entry "application" "pdf" '((viewer . "/usr/bin/qlmanage -p %s") (type . "application/pdf"))))) - -(with-eval-after-load "mm-decode" - (add-to-list 'mm-discouraged-alternatives "text/html") - (add-to-list 'mm-discouraged-alternatives "text/richtext")) - -(with-eval-after-load "mml-sec" - (setq mml-secure-openpgp-encrypt-to-self t)) - -;;;; Misc - -(defun ap/remove-extra-cr () - "Remove extraneous CR codes from a file." - (interactive) - (save-excursion - (goto-char (point-min)) - (while (search-forward " - " nil t) - (replace-match "")))) - -;;;;; Auxillary Configuration - -(defvar have-private-key - (file-exists-p (expand-file-name "secring.gpg" "~/.gnupg/"))) - -(defvar gpg-agent-ssh-sock - (or (getenv "GPG_AGENT_INFO") - (concat "/run/user/" (number-to-string (user-uid)) "/gnupg/S.gpg-agent.ssh"))) - -(defun read-gpg-file (file) - "Read (decrypt) given GPG file FILE." - (let ((file-to-decrypt (expand-file-name file user-emacs-directory)) - (ctx (epg-make-context epa-protocol))) - (if (file-exists-p file-to-decrypt) - (epg-decrypt-file ctx file-to-decrypt nil) - (message "Decrypting %s...failed" file-to-decrypt) - (error "File %s does not exist" file-to-decrypt)))) - -(defun load-gpg (file) - "Load FILE if private key is available." - (if have-private-key - (load file) - (message "WARNING: Couldn't load %s (No gpg key found)" file))) - -; load this in a post-frame hook because gpg-agent asks for a password on first -; startup and caches it. Don't want emacs daemon to hang because of gpg-agent. -(defun load-private-data () - "Load encrypted config in file based upon hostname." - (interactive) - (if (not (file-exists-p (expand-file-name (concat (system-name) ".el.gpg") user-emacs-directory))) - (message "No encrypted configuration matches system name `%s'" (system-name)) - (if (not have-private-key) - (message "ERROR: Private GPG key not found") - (unless (or (getenv "GPG_AGENT_INFO") - (getenv "SSH_AUTH_SOCK")) - (start-process "gpg-agent" nil "gpg-agent" "--daemon") - (setenv "SSH_AUTH_SOCK" gpg-agent-ssh-sock)) - (setq password-cache-expiry nil) - (unless (file-exists-p (concat pinentry--socket-dir "pinentry")) - (pinentry-start) - (add-hook 'kill-emacs-hook 'pinentry-stop)) - (add-to-list 'load-suffixes ".el.gpg") - (load-gpg (expand-file-name (system-name) user-emacs-directory))))) - -(defvar first-frame-hook nil - "Hook for running code after first-frame is opened.") - -(defun first-frame-hook-handler (frame) - "Hook run only after first frame FRAME is created." - (remove-hook 'after-make-frame-functions #'first-frame-hook-handler) - (run-at-time nil nil (lambda () (run-hooks 'first-frame-hook)))) - -(if (or (daemonp) - (not (eq 1 (length (frame-list))))) - (add-hook 'after-make-frame-functions #'first-frame-hook-handler) - (run-at-time nil nil (lambda () (run-hooks 'first-frame-hook)))) - -(add-hook 'first-frame-hook #'load-private-data) - -;;;; Minibuffer - -;; Sometimes I want to use the minibuffer, but I’m already inside it. -;; Fortunately, this is possible. Of course, I need to know how many -;; minibuffers there are on the stack. - -(setq enable-recursive-minibuffers t) -(minibuffer-depth-indicate-mode t) - -;; This avoids some issue with the minibuffer and the point being behind -;; the prompt. I don’t remember what exactly. -(setq minibuffer-prompt-properties '(read-only t point-entered minibuffer-avoid-prompt face minibuffer-prompt)) - -;; Occasionally, I exit emacs. I should probably reduce the frequency of this. -(if (daemonp) - (defalias 'exit-emacs #'delete-frame) - (defalias 'exit-emacs #'save-buffers-kill-emacs)) - -;;;;; swiper/ivy - -;; Ivy is the new kid on the completion block. It seems to be a strong -;; replacement for helm so far. - -(use-package swiper - :bind (("C-s" . swiper) - ("C-r" . swiper) - ("C-=" . swiper)) - :demand t - :config (progn - (ivy-mode 1) - (setq ivy-re-builders-alist '((t . ivy--regex-plus)) - ivy-extra-directories '("./")) - (ivy-set-actions 'ivy-switch-buffer '(("k" (lambda (x) - (kill-buffer x) - (ivy--reset-state ivy-last)) - "kill"))) - (add-to-list 'ivy-initial-inputs-alist '(counsel-M-x . "")))) - -(use-package ivy-hydra) - -;;;;; counsel - -(use-package counsel - :config (progn - (bind-key "M-x" #'counsel-M-x) - (bind-key "<apps>" #'counsel-M-x) - (bind-key "<menu>" #'counsel-M-x) - (bind-key "C-c M-x" #'execute-extended-command) - (bind-key "C-x C-f" #'counsel-find-file) - (bind-key "M-y" #'counsel-yank-pop) - (bind-key "C-x i" #'counsel-imenu) - (bind-key "M-y" #'ivy-next-line ivy-minibuffer-map) - (setq counsel-ag-base-command "ag --nocolor --nogroup --hidden %s") - (defadvice counsel-find-file (after find-file-sudo activate) - "Find file as root if necessary." - (when (and buffer-file-name - (not (file-writable-p buffer-file-name))) - (message "File not writable %s" buffer-file-name) - (find-alternate-file (concat "/sudo::" buffer-file-name)))) - (setq counsel-rg-base-command "rg -i --no-heading --line-number --hidden %s ."))) - - -;;;;; smex - -;; Smex is my favourite way to use =M-x=. Counsel’s =counsel-M-x= -;; function uses it internally, so I’m keeping it around, even though I -;; don’t use it directly. - -(use-package smex - :defines (smex-key-advice-ignore-menu-bar) - :commands (smex - smex-update - smex-initialize) - :config (progn - (setq smex-key-advice-ignore-menu-bar t - smex-auto-update nil) - (defun smex-update-after-load (_unused) - (if (boundp 'smex-cache) - (smex-update))) - (add-hook 'after-load-functions 'smex-update-after-load)) - :init (progn - (setq smex-history-length 100 - smex-save-file (concat user-emacs-directory - "smex-items")))) - -;;;;; cmd-to-echo - -;; I’ve been looking for some way to run programming projects (mostly -;; node.js) inside emacs. =cmd-to-echo= seems great for this, as new -;; output pops up in the echo area. - -(use-package cmd-to-echo - :commands (cmd-to-echo) - :config (setq cmd-to-echo-add-output-to-process-buffers t)) -;;;; Modes - -;; Setup some modes for systemd files -(add-to-list 'auto-mode-alist '("\\.service\\'" . conf-mode)) -(add-to-list 'auto-mode-alist '("\\.target\\'" . conf-mode)) -(add-to-list 'auto-mode-alist '("\\.socket\\'" . conf-mode)) - -;; =direnv=’s files are basically shell scripts, it’s a nice way to -;; set environment variables for projects. -(add-to-list 'auto-mode-alist '("\\.envrc\\'" . sh-mode)) + :config (global-undo-tree-mode)) +(use-package goto-chg + :defer 1) -;; Some modes that I don’t really customise much, mostly for -;; configuration files. -(use-package haskell-mode - :mode (("\\.hs\\'" . haskell-mode))) +;;; Directories -(use-package dockerfile-mode - :mode (("Dockerfile\\'" . dockerfile-mode))) +(custom-set-variables + '(dired-dwim-target t) + '(dired-recursive-copies 'top) + '(dired-listing-switches "-alh") + '(dired-recursive-deleted (if delete-by-moving-to-trash + 'always + 'top))) -(use-package docker-compose-mode - :mode (("docker-compose.*.yml" . docker-compose-mode)) - :config (progn - (add-hook 'docker-compose-mode-hook #'company-mode-on))) - -(use-package nix-mode - :mode (("\\.nix\\'" . nix-mode)) - :config (progn - (setq nix-indent-function #'nix-indent-line))) - -(define-derived-mode xmonad-mode haskell-mode "XM") -(add-to-list 'auto-mode-alist '("xmobarrc\\'" . xmonad-mode)) -(add-to-list 'auto-mode-alist '("xmonad.hs\\'" . xmonad-mode)) - -(use-package nginx-mode - :defer t - :mode (("/nginx/servers/" . nginx-mode) - ("/nginx/.*\\.d/" . nginx-mode))) - -(use-package ruby-mode - :mode (("\\.rb\\'" . ruby-mode) - ("\\.cap\\'" . ruby-mode))) - -(use-package go-mode - :mode (("\\.go\\'" . go-mode))) - -(use-package jinja2-mode - :mode (("\\.j2\\'" . jinja2-mode) - ("\\.jinja\\'" . jinja2-mode))) - -(use-package scss-mode - :defer t - :config (progn - (setq scss-compile-at-save nil))) - -(use-package toml-mode - :mode ("\\.toml\\'" . toml-mode)) - -(use-package yaml-mode - :mode (("/group_vars/.*" . yaml-mode) - ("/host_vars/.*" . yaml-mode))) - -(define-derived-mode ansible-mode yaml-mode "Ansible") -(add-to-list 'auto-mode-alist '("\\(?:ansible.+\\|roles/.+/\\(?:tasks\\|handlers\\)\\)/.+\\.yml\\'" . ansible-mode)) - -(define-derived-mode saltstack-mode yaml-mode "Salt") -(add-to-list 'auto-mode-alist '("\\.sls\\'" . saltstack-mode)) - -;;;;; Beancount - -(let ((beancount-dir (car (split-string (shell-command-to-string "ghq list --full-path blais/beancount"))))) - (when (and beancount-dir - (file-directory-p beancount-dir)) - (add-to-list 'load-path (expand-file-name "editors/emacs/" - beancount-dir)) - (use-package beancount - :defines (beancount-use-ido - beancount-mode-map - beancount-mode-map-prefix) - :commands beancount-mode - :bind (:map beancount-mode-map - ("C-c d" . insert-date)) - :config (setq beancount-use-ido nil - beancount-mode-map-prefix (kbd ", a"))))) - -;;;;; Markdown - -(use-package markdown-mode - :defer t) - -;;;;; Org - -;; Org is wünderbar. - -(use-package org - :bind (("C-c C-a" . org-agenda-list) - ("C-c a" . org-agenda) - ("C-c l" . org-store-link)) - :defer 8 - :defines (org-table-duration-custom-format) - :init (setq org-replace-disputed-keys t - org-support-shift-select 'always - org-ellipsis "…") - :config (progn - (setq org-directory "~/Documents/org" - org-agenda-files `(,(concat org-directory "/agenda")) - - org-default-notes-file (concat org-directory "/notes") - - ;; ‘Remember’: new items at top - org-reverse-note-order t - - org-modules '(org-protocol) - - ;; Add time done to ‘done’ tasks - org-log-done 'time - - org-list-allow-alphabetical t - - org-adapt-indentation nil - - org-pretty-entities t - - org-table-duration-custom-format 'seconds - - org-src-fontify-natively nil - - org-blank-before-new-entry '((heading . t) - (plain-list-item . auto)) - org-fontify-done-headline t - - org-goto-interface 'outline-path-completion - - org-outline-path-complete-in-steps nil - - org-todo-keywords '((sequence "BACKLOG(b)" "TODO(t)" "WAIT(w@/!)" "STARTED(s!)" "|" "DONE(d!)") - (sequence "|" "CANCELLED(c@)")) - org-log-into-drawer "LOGBOOK") - (set-register ?o `(file . ,(expand-file-name "organiser.org" org-directory))) - (add-hook 'org-mode-hook #'turn-on-auto-fill) - (org-load-modules-maybe t))) - -(use-package org-src - :ensure nil - :after org - :config (progn - (bind-key "C-x C-s" #'org-edit-src-exit org-src-mode-map))) - -;;;;;;; org-babel - -;; Org’s babel feature is really nice. I use it for this file, and I can -;; use it to communicate between programming languages. Sometime I hope -;; to have my =ledger= setup in an org file with some graph processing -;; with R or something. - -(use-package ob-core - :defer t - :ensure nil - :config (progn - (org-babel-do-load-languages 'org-babel-load-languages - '((ledger . t) - (shell . t))) - (setq org-src-tab-acts-natively t - org-edit-src-content-indentation 0 - org-src-preserve-indentation t))) - -;;;;;;; org-journal - -;; I can use this to keep a journal. I should use it. - -(use-package org-journal - :bind ("s-j" . org-journal-new-entry) - :defer 20 - :config (progn - (setq org-journal-date-format "%A, %d %B %Y" - org-journal-dir "~/Documents/journal/") - - (define-hook-helper org-journal-mode () - (use-variable-fonts) - (text-scale-adjust 4)) - (defun org-journal-display-entry-yesterday () - "Show org-journal entry for yesterday" - (interactive) - (org-journal-read-or-display-entry (yesterday-time))))) - - -;;;; Programming - -;;;;; flycheck - -;; On-the-fly error checking in programming modes? Yes please. - -(use-package flycheck - :defer 5 - :config (progn - (global-flycheck-mode) - (setq flycheck-check-syntax-automatically '(save mode-enabled)) - (setq flycheck-indication-mode 'left-fringe) - (with-eval-after-load 'git-gutter-fringe - (fringe-helper-define 'flycheck-fringe-bitmap-double-arrow '(center repeated) - "XXX.....")) - (if (executable-find "eslint_d") - (setq flycheck-javascript-eslint-executable "eslint_d")))) - -;;;;;; flycheck-pos-tip - -;; Show flycheck errors in a little popup, so I don't lose my place - -(use-package flycheck-pos-tip - :after flycheck - :config (progn - (setq flycheck-display-errors-delay 0.5 - flycheck-pos-tip-timeout 15) - (flycheck-pos-tip-mode 1))) - -;;;;; golang - -;; Go has a few packages to inter-operate with other emacs packages. - -(use-package company-go - :commands company-go - :config (progn - (setq company-go-show-annotation t)) - :init (progn - (define-hook-helper go-mode () - (set (make-local-variable 'company-backends) - '(company-go))))) - -(use-package go-eldoc - :commands go-eldoc-setup - :init (progn - (add-hook 'go-mode-hook #'go-eldoc-setup))) - -(use-package go-projectile - :defer t - :config (progn - (setq go-projectile-switch-gopath 'maybe))) - -;;;;; ggtags - -;; A nice completion backend for programming modes. - -(use-package ggtags - :if (executable-find "gtags") - :commands turn-on-ggtags-mode - :functions (ggtags-navigation-mode-abort) - :config (progn - (bind-key "q" #'ggtags-navigation-mode-abort ggtags-navigation-mode-map)) - :init (progn - (defun turn-on-ggtags-mode () - (interactive) - (ggtags-mode 1)) - (add-hook 'c-mode-common-hook #'turn-on-ggtags-mode))) - -;;;;; dumb-jump - -;; A "clever" way of implementing go-to-definition across languages: use -;; a project-wide text search and apply heuristics to the results to -;; guess a definition. - -(use-package dumb-jump - :bind (("M-g o" . dumb-jump-go-other-window) - ("M-g j" . dumb-jump-go) - ("M-g x" . dumb-jump-go-prefer-external) - ("M-g z" . dumb-jump-go-prefer-external-other-window)) - :config (setq dumb-jump-selector 'ivy)) - -;;;;; imenu-anywhere - -;; This is like imenu, but shows functions (or similar top-level -;; entities) across buffers in the same project. Neat! - -(use-package imenu-anywhere - :if (featurep 'helm-imenu) - :bind ("C-x C-." . ivy-imenu-anywhere)) - -;;;;; Lisps - -;;;;;; All - -;; Lisp modes don’t seem to have a common ancestor. So I made a custom -;; hook which I trigger in every lispy-mode. - -(defcustom lisp-mode-common-hook nil - "Hook run when entering any Lisp mode." - :type 'hook - :group 'lisp) - -(create-hook-helper lisp-mode-setup () - :hooks (emacs-lisp-mode-hook - scheme-mode-hook - lisp-mode-hook - clojure-mode-hook) - (run-hooks 'lisp-mode-common-hook)) - -;;;;;;; Redshank - -;; Lisp syntax allows for really easy refactoring. Redshank gives some -;; operations that aren’t part of paredit, like extracting variables into -;; let bindings. -(use-package redshank - :after (paredit) - :config (progn - (add-hook 'lisp-mode-common-hook #'turn-on-redshank-mode))) - -;;;;;; Emacs Lisp - -;; Go-to function for elisp. Except it works through the entire Emacs ecosystem. - -(use-package elisp-slime-nav - :commands elisp-slime-nav-mode - :init (progn - (add-hook 'emacs-lisp-mode-hook #'elisp-slime-nav-mode))) - -;; Interactive elisp - -(use-package ielm - :defer t - :ensure nil - :config (progn - (define-hook-helper ielm-mode () - (run-hooks 'lisp-mode-common-hook)))) - -;;;;;; Scheme & Lisp - -;; I don’t work with these as often as I would like - -(define-hook-helper lisp-mode () - (set (make-local-variable 'lisp-indent-function) - #'common-lisp-indent-function)) - -;;;;;;; geiser - -;; A REPL thing for Scheme. Hopefully I’ll get to use it more in the -;; future. - -(use-package geiser - :commands (geiser-mode - geiser - run-geiser - run-racket)) - -;;;;;;; slime - -;; A REPL thing (and more) for Lisp. - -(use-package slime - :commands (slime) - :config (progn - (let ((ql-slime-helper (expand-file-name "~/quicklisp/slime-helper.el"))) - (if (file-exists-p ql-slime-helper) - (load ql-slime-helper)) - (slime-setup '(slime-fancy slime-asdf))) - (setq common-lisp-hyperspec-root "file://opt/local/share/doc/lisp/HyperSpec-7-0/" - inferior-lisp-program (or (executable-find "sbcl") - (executable-find "ccl64"))))) - -;;;;;; Clojure - -(use-package clojure-mode - :defer t - :init (progn - (define-hook-helper cider-repl-mode () - (highlight-changes-mode -1)))) - -(use-package clj-refactor - :defer t - :functions (clj-refactor-mode cljr-add-keybindings-with-prefix) - :config (progn - (cljr-add-keybindings-with-prefix "C-c C-m")) - :init (progn - (define-hook-helper clojure-mode () - (clj-refactor-mode 1)))) - -;;;;;;; cider - -;; A REPL thing for Clojure - -(use-package cider - :defer t - :config (progn - (setq nrepl-hide-special-buffers t) - (unbind-key "C-c C-f" cider-mode-map))) - -;;;;; Auto-compile - -;; Auto-compile emacs lisp when saving, asynchronously. -(use-package auto-async-byte-compile - :config (add-hook 'emacs-lisp-mode-hook #'enable-auto-async-byte-compile-mode)) - -;;;;; cc-mode - -;; Although I don’t use C or C++, setting up the mode is helpful because -;; quite a few other modes are derived from it. - -(use-package cc-mode - :defer 5 - :config (progn - (setq c-default-style '((java-mode . "java") - (awk-mode . "awk") - (other . "k&r")) - c-basic-offset 4) - (c-set-offset 'case-label '+))) - -;;;;; quickrun - -;; It’s nice to be able to quickly evaluate some code. Although I don’t -;; really seem to use it. -(use-package quickrun - :bind (("C-c C-e" . quickrun))) - -;;;;; Web development - -;;;;;; js2-mode - -;; This mode is really great for editing Javascript. It turns code into -;; an AST internally, so it can work with it almost like a lisp. - -(use-package js2-mode - :mode (("\\.js\\'" . js2-mode)) - :interpreter ("node" . js2-mode) - :functions (js2-next-error - js2--struct-put) - :config (progn - (define-key js2-mode-map [menu-bar Javascript] nil) - (add-hook 'js2-mode-hook #'js2-imenu-extras-mode) - (defun ap/js2-prev-error () - (interactive) - (js2-next-error -1)) - (bind-key "M-g M-n" #'js2-next-error js2-mode-map) - (bind-key "M-g M-p" #'ap/js2-prev-error js2-mode-map) - (advice-add #'js--multi-line-declaration-indentation :around (lambda (orig-fun &rest args) nil)) - (setq js2-basic-offset 2 - js-switch-indent-offset 2 - js2-include-node-externs t - js2-highlight-level 1 - js2-strict-missing-semi-warning nil - js2-strict-inconsistent-return-warning nil))) - -;;;;;;; typescript-mode - -(use-package typescript-mode - :config (progn - (setq typescript-indent-level 2))) - -;;;;;;; rjsx-mode - -;; A set of advice for js2-jsx-mode to work better with React. - -(use-package rjsx-mode - :after js2-mode - :if (fboundp #'js2--struct-put) - :mode (("\\.jsx\\'" . rjsx-mode))) - -;;;;;;; mocha - -(use-package mocha - :after js2-mode - :config (progn - (setq mocha-reporter "spec" - mocha-options "--bail") - (defun mocha-test-project () - "Test the current project." - (interactive) - (mocha-run "'./{src,lib,test}/**/*.test.js'")))) - -;;;;;;; js2-refactor - -;; Thanks to the AST provided by js2-mode, refactoring is possible. This -;; library implements some refactorings. - -(use-package js2-refactor - :after js2-mode - :config (progn - (bind-key "C-k" #'js2r-kill js2-mode-map) - (add-hook 'js2-mode-hook #'js2-refactor-mode) - (js2r-add-keybindings-with-prefix "C-c C-m"))) - -;;;;;;; add-node-modules-path +;;; Shells -;; Inside a javascript project, it's common to install tools locally to -;; the project. This will allows emacs to find their executables. - -(use-package add-node-modules-path - :config (progn - (create-hook-helper js2-mode () - :hooks (js2-mode-hook - typescript-mode-hook) - :name node-modules-flycheck - (add-node-modules-path) - (when (executable-find "tslint") - (setq-local flycheck-typescript-tslint-executable (executable-find "tslint"))) - (when (executable-find "eslint") - (setq-local flycheck-javascript-eslint-executable (executable-find "eslint"))) - (when (executable-find "prettier-standard") - (setq-local flycheck-javascript-standard-executable (executable-find "prettier-standard"))) - (when (executable-find "standard") - (setq-local flycheck-javascript-standard-executable (executable-find "standard")))))) - -;;;;;;; Prettier - -;; Reformat files on save - -(use-package prettier-js - :config (progn - (add-hook 'typescript-mode #'prettier-js-mode) - (add-hook 'js2-mode #'prettier-js-mode))) - -;;;;;;; Indium - -;; Javascript with an inferior node.js process and a debugger? Awesome. - -;; To debug with node, use version 6.9.1 or later of node and run it with -;; ~--inspect~ and, to break on the first line, ~--debug-brk~. - -;; For Chrom*, it needs to be launched with -;; ~--remote-debugging-port=9222~ - -;; Node will tell you to open an URL in Chrome: - -;; ~chrome-devtools://inspector.html?...&ws=127.0.0.1:PORT/PATH~ - -;; Instead, do this: - -;; ~M-x indium-connect-to-nodejs RET 127.0.0.1 RET PORT RET~ - -(use-package indium - :config (progn - (add-hook 'js2-mode-hook #'indium-interaction-mode))) - -;;;;;; tide - -;; Let's write some typescript - -(use-package tide - :after (typescript-mode company flycheck) - :hook ((typescript-mode . tide-setup))) - -;;;;;; tern - -;; Tern understands javascript. It adds really clever documented -;; completions, besides other IDE-like things. - -(use-package tern - :if (executable-find "tern") - :defer 5 - :config (progn - (setq tern-command (list (executable-find "tern"))) - (create-hook-helper tern-mode-on () - :hooks (js2-mode-hook) - (tern-mode +1)))) - -(with-eval-after-load 'tern - (use-package company-tern)) - -;;;;;; json-mode - -(use-package json-mode - :mode (("\\.json\\'" . json-mode) - ("\\.sailsrc\\'" . json-mode) - ("composer\\.lock\\'" . json-mode) - ("\\.tern-project\\'" . json-mode))) - -;;;;;; restclient - -;; Restclient is really nice. It’s like a scratchpad for HTTP api -;; calls. Feels a bit like using =org-babel=. I wonder if there’s an -;; integration between the two yet. - -(use-package restclient - :mode ("\\.api\\'" . restclient-mode) - :config (progn - (defun imenu-restclient-sections () - (setq imenu-prev-index-position-function nil) - (add-to-list 'imenu-generic-expression '("Services" "^## ?\\(.+\\)$" 1) t) - (add-to-list 'imenu-generic-expression '("Calls" "^# ?\\(.+\\)$" 1) t)) - (add-hook 'restclient-mode-hook #'imenu-restclient-sections))) - -(use-package company-restclient - :after (company restclient) - :init (add-to-list 'company-backends #'company-restclient t)) - -;;;;;; sgml-mode - -;; This is for HTML, since old versions of HTML were derived from SGML. -(use-package sgml-mode - :defer t - :config (setq sgml-basic-offset 2)) - -;;;;;; emmet-mode - -;; Emmet is really nice to write HTML quickly. Especially with -;; frameworks that require multiple nested elements to do anything useful. -(use-package emmet-mode - :commands (emmet-mode) - :init (progn - (setq emmet-indentation 2 - emmet-self-closing-tag-style " /") - (add-hook 'sgml-mode-hook #'emmet-mode) - (add-hook 'web-mode-hook #'emmet-mode) - (add-hook 'css-mode-hook #'emmet-mode))) - -;;;;;; web-mode - -;; This mode handles just about every templating language out ther, which -;; is really nice, because it handles the HTML part the same way in all -;; of them as well. - -(use-package web-mode - :mode (("/views/.*\\.php\\'" . web-mode) - ("\\.html\\'" . web-mode) - ("/templates/.*\\.php\\'" . web-mode) - ("\\.handlebars\\'" . web-mode) - ("\\.ejs\\'" . web-mode) - ("\\.njk\\'" . web-mode)) - :config (progn - (setq web-mode-code-indent-offset 2 - web-mode-css-indent-offset 2 - web-mode-markup-indent-offset 2 - web-mode-style-padding 0 - web-mode-script-padding 0 - web-mode-comment-style 2 - web-mode-enable-auto-pairing nil - web-mode-enable-auto-quoting nil))) - -;;;;; Live coding - -;; Sometimes I might want to show off my emacs usage. - -(defun live-coding () - "Configure display for live coding." - (interactive) - (ap/set-fonts "SF Mono" 18 nil nil t 0.1) - (global-command-log-mode 1)) - -(defun live-coding-stop () - "Revert live coding display configuration." - (interactive) - (ap/set-fonts-according-to-system) - (global-command-log-mode -1)) - -;;;;;; command-log-mode - -(use-package command-log-mode - :defines (command-log-mode-key-binding-open-log) - :config (progn - (setq command-log-mode-key-binding-open-log nil - command-log-mode-auto-show t - command-log-mode-is-global t))) - -;;;; Systems - -(use-package kubernetes - :commands (kubernetes-overview)) - -(use-package k8s-mode - :config (progn - (setq k8s-site-docs-version "v1.11" - k8s-search-documentation-browser-function #'browse-url-chrome))) +(use-package eshell + :functions (eshell/pwd) + :custom ((eshell-prompt-function (lambda () + (concat (eshell/pwd) "\n$ "))) + (eshell-prompt-regexp "^[$][[:blank:]]"))) -(use-package kubernetes-evil - :after (kubernetes evil)) +(use-package shell + :general (:keymaps 'shell-mode-map + "C-d" #'comint-delchar-or-maybe-eof)) -;;;; Spelling +(use-package comint + :general (:keymaps 'comint-mode-map + "C-c C-l" #'counsel-shell-history)) -(use-package ispell - :bind (("<f8>" . ispell-word)) - :config (progn - (cond - ((executable-find "aspell") (setq ispell-program-name "aspell" - ispell-dictionary "british" - ispell-really-aspell t - ispell-really-hunspell nil)) - ((executable-find "hunspell") (setq ispell-program-name "hunspell" - ispell-really-aspell nil - ispell-really-hunspell t))))) - -(use-package flyspell - :config (progn - (defun flyspell-detect-ispell-args (&optional run-together) - "If RUN-TOGETHER is true, spell check the CamelCase words. - Please note RUN-TOGETHER will make aspell less capable. So it should only be used in prog-mode-hook." - (let (args) - (when ispell-program-name - (cond - ((string-match "aspell$" ispell-program-name) - (setq args (list "--sug-mode=ultra")) - (if run-together - (setq args (append args '("--run-together" "--run-together-limit=16" "--run-together-min=2"))))) - ((string-match "hunspell$" ispell-program-name) - (setq args nil)))) - args)) - ;; `ispell-extra-args' is *always* used when start CLI aspell process - (setq-default ispell-extra-args (flyspell-detect-ispell-args t)) - ;; (setq ispell-cmd-args (flyspell-detect-ispell-args)) - (defadvice ispell-word (around my-ispell-word activate) - (let ((old-ispell-extra-args ispell-extra-args)) - (ispell-kill-ispell t) - ;; use emacs original arguments - (setq ispell-extra-args (flyspell-detect-ispell-args)) - ad-do-it - ;; restore our own ispell arguments - (setq ispell-extra-args old-ispell-extra-args) - (ispell-kill-ispell t))) - - (defadvice flyspell-auto-correct-word (around my-flyspell-auto-correct-word activate) - (let* ((old-ispell-extra-args ispell-extra-args)) - (ispell-kill-ispell t) - ;; use emacs original arguments - (setq ispell-extra-args (flyspell-detect-ispell-args)) - ad-do-it - ;; restore our own ispell arguments - (setq ispell-extra-args old-ispell-extra-args) - (ispell-kill-ispell t))) - (setq flyspell-issue-message-flag nil) - - (defun fly-text-mode-hook-setup () - ;; Turn off RUN-TOGETHER option when spell check text-mode - (setq-local ispell-extra-args (flyspell-detect-ispell-args))) - (add-hook 'text-mode-hook 'fly-text-mode-hook-setup) - (add-hook 'prog-mode-hook #'flyspell-prog-mode))) - -;;;;; Dictionary - -;; One thing I miss from macOS is the "look up" functionality to define -;; words by a certain gesture. =define-word= can provide something -;; similar, at least in Emacs. - -(use-package define-word - :bind ("M-<f8>" . define-word-at-point)) - -;;;;; Style checking - -;; [[https://github.com/ValeLint/vale][Vale]] is a linter, but for prose. Neat idea! Salesman is a bad term. - -(use-package flycheck-vale - :if (executable-find "vale") - :config (progn - (add-to-list 'flycheck-vale-modes 'org-mode) - (add-to-list 'flycheck-vale-modes 'org-journal-mode) - (flycheck-vale-setup))) +;;; Major modes -;;;; Scripting +;;;; js +(custom-set-variables '(js-indent-level 2) + '(js-enabled-frameworks '(javascript))) -;; Make a shell-script buffer executable after saving it, if it has a shebang. +;;;; typescript +(custom-set-variables '(typescript-indent-level 2)) -(add-hook 'after-save-hook - #'executable-make-buffer-file-executable-if-script-p) +;;;; shell +(general-add-hook 'sh-mode-hook + (lambda () + (general-add-hook 'after-save-hook + #'executable-make-buffer-file-executable-if-script-p :append :local))) +(add-to-list 'auto-mode-alist '("\\.env\\'" . conf-unix-mode)) (use-package sh-script :mode (("\\.zsh\\'" . shell-script-mode) ("zshenv\\'" . shell-script-mode) @@ -2059,301 +385,122 @@ With two prefix arguments, write out the day and month name." (add-hook 'shell-mode-hook 'ansi-color-for-comint-mode-on) -;;;;; eshell - -;; I should try to get into the habit of using this more. It’s really -;; nice, when I remember to use it. - -(use-package eshell - :bind ("C-c s" . eshell) - :defer 10 - :functions (eshell/pwd) - :defines (eshell-prompt-function - eshell-prompt-regexp) - :config (progn - (setq eshell-directory-name "~/.emacs.d/eshell" - eshell-prompt-function (lambda () - (concat - (eshell/pwd) - "\n$ ")) - eshell-prompt-regexp (rx bol (char "$") blank)) - (define-hook-helper eshell-load () - (bind-key "C-c C-l" #'counsel-esh-history eshell-mode-map)))) - -(use-package em-smart - :ensure nil - :commands eshell-smart-initialize - :config (progn - (setq eshell-where-to-jump 'begin - eshell-review-quick-commands nil - eshell-smart-space-goes-to-end t))) - -(autoload #'eshell/cd "em-dirs") -(defun eshell-goto-current-dir (&optional arg) - "Open `default-directory' in eshell. -Pass optional ARG to `eshell' (which see)." - (interactive "P") - (let ((dir default-directory)) - (eshell arg) - (eshell/cd dir))) -(bind-key "C-c S" #'eshell-goto-current-dir) - -;;;;;; Shells - -(use-package shell - :defer t - :ensure nil - :config (define-key shell-mode-map - (kbd "C-d") 'comint-delchar-or-eof-or-kill-buffer)) - -(use-package comint - :defer t - :ensure nil - :config (bind-key "C-c C-l" #'counsel-shell-history comint-mode-map)) - -(defun comint-delchar-or-eof-or-kill-buffer (arg) - "DWIM command for ^D to behave like in shells. -Pass ARG to `comint-delchar-or-maybe-eof'." - (interactive "p") - (if (null (get-buffer-process (current-buffer))) - (kill-buffer) - (comint-delchar-or-maybe-eof arg))) - -;;;; Text editing - -;; Emacs has an editor within. - -(put 'upcase-region 'disabled nil) -(put 'downcase-region 'disabled nil) -(setq sentence-end-double-space t - line-move-visual nil) -(visual-line-mode +1) -(global-subword-mode +1) - -;;;;; align - -;; =Align= is a useful command to line things up, once given some rules. -;; The most important one for me is JSON property alignment. - -(use-package align - :defer 10 - :ensure nil - :config (progn - (add-to-list 'align-rules-list - '(colon-key-value - (regexp . ":\\(\\s-*\\)") - (modes . '(js2-mode)))))) - -;;;;; Clipboard - -;; I like to use the clipboard more than the primary selection in X11. - -(setq select-enable-clipboard t - save-interprogram-paste-before-kill t) -(if (functionp 'x-cut-buffer-or-selection-value) - (setq interprogram-paste-function 'x-cut-buffer-or-selection-value)) -(when (boundp 'x-select-request-type) - (setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING))) - -;;;;; avy - -;; Avy is a really nice way to move around files, like ace-jump-mode, but -;; somehow I prefer it. - -(use-package avy - :defer 5 - :bind* (("M-g g" . avy-goto-line) - ("M-g M-g" . avy-goto-line) - ("M-r" . avy-goto-word-1) - ("C-c SPC" . avy-goto-char-timer)) - :config (progn - (avy-setup-default) - (setq avy-all-windows nil))) - -;;;;;; ace-link - -;; Visit any link. Despite the name, this works with avy. - -(use-package ace-link - :after avy - :config (progn - (ace-link-setup-default))) - -;;;;; goto-chg - -;; This is like popping the mark, only it filters to only change areas -;; and doesn’t go back to the same place more than once. - -(use-package goto-chg) - -;;;;; paredit +;;;; nix +(custom-set-variables '(nix-indent-function #'nix-indent-line)) +;;;; gitlab-ci.yml +(with-eval-after-load 'git-gutter-fringe + (fringe-helper-define 'flycheck-fringe-bitmap-double-arrow '(center repeated) + "XXX.....")) -;; Balanced parentheses in lisps are nice, but all the refactoring and -;; movement commands are much more interesting. +(custom-set-variables '(gitlab-ci-url "https://gitlab.satoshipay.tech")) +(use-package gitlab-ci-mode-flycheck + :ghook ('gitlab-ci-mode-hook (list #'gitlab-ci-mode-flycheck-enable + #'flycheck-mode))) -(use-package paredit - :config (progn - (add-hook 'lisp-mode-common-hook #'enable-paredit-mode) - (put #'paredit-forward-delete 'delete-selection 'supersede) - (put #'paredit-backward-delete 'delete-selection 'supersede) - (add-hook 'eval-expression-minibuffer-setup-hook #'enable-paredit-mode))) +;;;; *ignore +(use-package gitignore-mode + :mode ((".dockerignore\\'" . gitignore-mode))) -;;;;; undo-tree +;;;; kubernetes +(custom-set-variables '(k8s-site-docs-version "v1.13")) -;; Emacs’ default handling of undo is a bit confusing. Undo-tree makes -;; it much clearer. It’s especially helpful for protoyping and refactoring. +;;;; beancount -(use-package undo-tree - :config (progn - (global-undo-tree-mode) - ;; Keep region when undoing in region - (defadvice undo-tree-undo (around keep-region activate) - (if (use-region-p) - (let ((m (set-marker (make-marker) (mark))) - (p (set-marker (make-marker) (point)))) - ad-do-it - (goto-char p) - (set-mark m) - (set-marker p nil) - (set-marker m nil)) - ad-do-it)))) - -;;;;; replace - -(with-eval-after-load "replace.el" - (setq case-replace nil)) - -;;;; Evil - -(setq evil-want-keybinding nil) - -(use-package evil-leader - ;; must load before evil-mode - :init (global-evil-leader-mode +1) - :config (progn - (evil-leader/set-leader ",") - (evil-leader/set-key - "h" help-map - "w" evil-window-map - "x" ctl-x-map - "s" #'save-buffer - "q" #'kill-or-delete-this-buffer-dwim - "p" projectile-command-map - "v" #'split-window-right - "o" #'other-window - "y" #'evil-avy-goto-line - "bb" #'ivy-switch-buffer - "bx" #'kill-this-buffer - "br" #'revert-buffer - "dd" #'dired - "fs" #'save-buffer - "ff" #'find-file - "fw" #'write-file - "fd" #'crux-delete-file-and-buffer - "fr" #'crux-rename-file-and-buffer - "gs" #'magit-status - "gm" #'magit-dispatch-popup - "gt" #'git-timemachine - "bb" #'evil-switch-to-windows-last-buffer - "bi" #'ibuffer - "bz" #'bury-buffer - ";" #'counsel-M-x))) +(use-package beancount + :load-path "~/projects/bitbucket.org/blais/beancount/editors/emacs") -(use-package evil - :defines evil-window-map - :config (progn - (evil-mode +1) - (setq-default evil-shift-width 2) - (bind-keys :map evil-normal-state-map - (";" . evil-ex)) - (define-key evil-motion-state-map (kbd "C-;") #'evil-avy-goto-line) - (define-key evil-normal-state-map [escape] #'keyboard-quit) - (define-key evil-visual-state-map [escape] #'keyboard-quit) - (define-key evil-window-map (kbd "q") #'kill-or-delete-this-buffer-dwim) - (define-key minibuffer-local-map [escape] #'minibuffer-keyboard-quit) - (define-key minibuffer-local-ns-map [escape] #'minibuffer-keyboard-quit) - (define-key minibuffer-local-completion-map [escape] #'minibuffer-keyboard-quit) - (define-key minibuffer-local-must-match-map [escape] #'minibuffer-keyboard-quit) - (define-key minibuffer-local-isearch-map [escape] #'minibuffer-keyboard-quit) - (evil-define-key 'normal emacs-lisp-mode-map (kbd "K") #'elisp-slime-nav-describe-elisp-thing-at-point))) +;;;; org -(use-package evil-collection - :if evil-mode - :init (progn - (setq evil-collection-company-use-tng nil)) - :config (evil-collection-init)) +(custom-set-variables '(org-ellipsis "…") + `(org-directory "~/Documents/org")) -(use-package evil-commentary - :if evil-mode - :config (evil-commentary-mode +1)) +(use-package org-journal + :gfhook (#'variable-pitch-mode) + :custom ((org-journal-date-format "%A, %d %B %Y") + (org-journal-dir "~/Documents/journal"))) -(use-package evil-magit - :if evil-mode - :defines (evil-magit-use-y-for-yank) - :after magit - :config (progn - (setq evil-magit-use-y-for-yank nil))) +;;;; emacs-lisp -(use-package evil-quickscope - :if evil-mode - :config (progn - (global-evil-quickscope-mode +1))) +(use-package auto-async-byte-compile + :ghook ('emacs-lisp-mode-hook #'enable-auto-async-byte-compile-mode)) -(use-package evil-org - :if evil-mode - :config (progn - (define-hook-helper org-mode () - (evil-org-mode +1)) - (define-hook-helper evil-org-mode () - (when (fboundp 'evil-org-set-key-theme) - (evil-org-set-key-theme))))) +;;;; web modes (tsx, html) -(use-package evil-org-agenda - :if evil-mode - :after evil-org - :functions (evil-org-agenda-set-keys) - :config (progn - (require 'evil-org-agenda) - (evil-org-agenda-set-keys))) +(use-package web-mode + :mode (("\\.tsx" . web-mode)) + :custom ((web-mode-enable-auto-pairing nil) + (web-mode-code-indent-offset 2) + (web-mode-markup-indent-offset 2) + (web-mode-css-indent-offset 2) + (web-mode-style-padding 0) + (web-mode-script-padding 0))) + +;;; IDE features + +(fringe-helper-define 'left-vertical-bar '(center repeated) + "XXX.....") +(use-package flymake + :custom ((flymake-error-bitmap '(left-vertical-bar compilation-error)) + (flymake-warning-bitmap '(left-vertical-bar compilation-warning)))) + +(use-package lsp-mode + :ghook ('typescript-mode-hook #'lsp) + :custom ((lsp-auto-guess-root t) + (lsp-prefer-flymake t) + (lsp-enable-symbol-highlighting nil))) + +(use-package lsp-ui + :after lsp-mode + :ghook ('lsp-mode-hook) + :custom ((lsp-ui-sideline-enable nil))) -(use-package evil-snipe - :defines (evil-snipe-scope) - :if evil-mode - :config (progn - (setq evil-snipe-scope 'visible) - (evil-snipe-mode +1) - (add-hook 'magit-mode-hook #'turn-off-evil-snipe-override-mode))) +;; Inside a javascript project, it's common to install tools locally to +;; the project. This will allows emacs to find their executables. -(use-package evil-surround - :if evil-mode - :config (progn - (global-evil-surround-mode +1))) +(use-package add-node-modules-path + :ghook ('(js2-mode-hook + typescript-mode-hook) #'add-node-modules-path)) -(use-package evil-space - :if evil-mode - :config (progn - (evil-space-mode +1))) +;;;; Reformat on save -(use-package sentence-navigation - :if (and evil-mode (boundp 'sentence-nav-evil-forward)) - :config (progn - (define-key evil-motion-state-map ")" #'sentence-nav-evil-forward) - (define-key evil-motion-state-map "(" #'sentence-nav-evil-backward) - (define-key evil-motion-state-map "g)" #'sentence-nav-evil-forward-end) - (define-key evil-motion-state-map "g(" #'sentence-nav-evil-backward-end) - (define-key evil-outer-text-objects-map "s" #'sentence-nav-evil-a-sentence) - (define-key evil-inner-text-objects-map "s" #'sentence-nav-evil-inner-sentence))) - -;;;; End - -;; Start a server if possible. A daemon is already a server. -(use-package server - :defer 2 - :if (not (daemonp)) - :config (unless (server-running-p server-name) - (server-start))) +(use-package prettier-js + :ghook ('(typescript-mode-hook js2-mode-hook) #'prettier-js-mode t)) + +;;; Take me to my leader + +(my-leader-def + :keymaps 'normal + "h" '(:keymap ehelp-map) + "w" '(:keymap evil-window-map) + "x" '(:keymap ctl-x-map) + "q" #'evil-delete-buffer + "p" projectile-command-map + "v" #'split-window-right + "o" #'other-window + "u" #'universal-argument + ";" #'counsel-M-x + "bb" #'ivy-switch-buffer + "bx" #'kill-this-buffer + "br" #'revert-buffer + "dd" #'dired + "fs" #'save-buffer + "ff" #'find-file + "fw" #'write-file + "fd" #'crux-delete-file-and-buffer + "fr" #'crux-rename-file-and-buffer + "gs" #'magit-status + "gm" #'git-messenger:popup-message + "gg" #'magit-dispatch + "gn" #'git-gutter:next-hunk + "gp" #'git-gutter:previous-hunk + "gi" #'git-gutter:popup-hunk + "gs" #'git-gutter:stage-hunk + "go" #'git-gutter:revert-hunk + "gt" #'git-timemachine + "bi" #'ibuffer + "bz" #'bury-buffer + "iu" #'counsel-unicode-char) ;; # Local Variables: -;; # lentic-init: lentic-orgel-org-init ;; # flycheck-disabled-checkers: 'emacs-lisp-checkdoc ;; # End: |