From 15c61c0beebeb2d9645ac7cd3736d21fe286dd3a Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 14 Dec 2019 11:30:56 +0000 Subject: chore(emacs): Move emacs config to tools/emacs --- .gitignore | 11 -- README.md | 6 - init.el | 168 ------------------------ init/bindings.el | 54 -------- init/custom.el | 52 -------- init/eshell-setup.el | 68 ---------- init/functions.el | 266 -------------------------------------- init/look-and-feel.el | 115 ---------------- init/mail-setup.el | 98 -------------- init/modes.el | 36 ------ init/nixos.el | 103 --------------- init/settings.el | 65 ---------- init/term-setup.el | 37 ------ tools/emacs/.gitignore | 11 ++ tools/emacs/README.md | 6 + tools/emacs/init.el | 168 ++++++++++++++++++++++++ tools/emacs/init/bindings.el | 54 ++++++++ tools/emacs/init/custom.el | 52 ++++++++ tools/emacs/init/eshell-setup.el | 68 ++++++++++ tools/emacs/init/functions.el | 266 ++++++++++++++++++++++++++++++++++++++ tools/emacs/init/look-and-feel.el | 115 ++++++++++++++++ tools/emacs/init/mail-setup.el | 98 ++++++++++++++ tools/emacs/init/modes.el | 36 ++++++ tools/emacs/init/nixos.el | 103 +++++++++++++++ tools/emacs/init/settings.el | 65 ++++++++++ tools/emacs/init/term-setup.el | 37 ++++++ 26 files changed, 1079 insertions(+), 1079 deletions(-) delete mode 100644 .gitignore delete mode 100644 README.md delete mode 100644 init.el delete mode 100644 init/bindings.el delete mode 100644 init/custom.el delete mode 100644 init/eshell-setup.el delete mode 100644 init/functions.el delete mode 100644 init/look-and-feel.el delete mode 100644 init/mail-setup.el delete mode 100644 init/modes.el delete mode 100644 init/nixos.el delete mode 100644 init/settings.el delete mode 100644 init/term-setup.el create mode 100644 tools/emacs/.gitignore create mode 100644 tools/emacs/README.md create mode 100644 tools/emacs/init.el create mode 100644 tools/emacs/init/bindings.el create mode 100644 tools/emacs/init/custom.el create mode 100644 tools/emacs/init/eshell-setup.el create mode 100644 tools/emacs/init/functions.el create mode 100644 tools/emacs/init/look-and-feel.el create mode 100644 tools/emacs/init/mail-setup.el create mode 100644 tools/emacs/init/modes.el create mode 100644 tools/emacs/init/nixos.el create mode 100644 tools/emacs/init/settings.el create mode 100644 tools/emacs/init/term-setup.el diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 7b666905f8..0000000000 --- a/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.smex-items -*token* -auto-save-list/ -clones/ -elpa/ -irc.el -local.el -other/ -scripts/ -themes/ -*.elc diff --git a/README.md b/README.md deleted file mode 100644 index 2dd067a910..0000000000 --- a/README.md +++ /dev/null @@ -1,6 +0,0 @@ -emacs.d -======== - -This contains my emacs.d folder. - -I use emacs for many things. diff --git a/init.el b/init.el deleted file mode 100644 index 66d38cd9fc..0000000000 --- a/init.el +++ /dev/null @@ -1,168 +0,0 @@ -;;; init.el --- Package bootstrapping. -*- lexical-binding: t; -*- - -;; Packages are installed via Nix configuration, this file only -;; initialises the newly loaded packages. - -(require 'use-package) -(require 'seq) - -(package-initialize) - -;; Add 'init' folder that contains other settings to load. -(add-to-list 'load-path (concat user-emacs-directory "init")) - -;; Initialise all packages installed via Nix. -;; -;; TODO: Generate this section in Nix for all packages that do not -;; require special configuration. - -;; -;; Packages providing generic functionality. -;; - -(use-package ace-window - :bind (("C-x o" . ace-window)) - :init - (setq aw-keys '(?f ?j ?d ?k ?s ?l ?a) - aw-scope 'frame)) - -(use-package auth-source-pass :init (auth-source-pass-enable)) - -(use-package avy - :bind (("M-j" . avy-goto-char) - ("M-p" . avy-pop-mark) - ("M-g g" . avy-goto-line))) - -(use-package browse-kill-ring) - -(use-package company - :hook ((prog-mode . company-mode)) - :bind (:map rust-mode-map ("" . company-indent-or-complete-common) - :map lisp-mode-map ("" . company-indent-or-complete-common)) - :init (setq company-tooltip-align-annotations t)) - -(use-package dash) -(use-package dash-functional) -(use-package edit-server :init (edit-server-start)) -(use-package gruber-darker-theme) -(use-package ht) -(use-package hydra) -(use-package idle-highlight-mode :hook ((prog-mode . idle-highlight-mode))) -(use-package paredit :hook ((lisp-mode . paredit-mode) - (emacs-lisp-mode . paredit-mode))) -(use-package multiple-cursors) -(use-package pinentry - :init - (setq epa-pinentry-mode 'loopback) - (pinentry-start)) - -(use-package rainbow-delimiters :hook (prog-mode . rainbow-delimiters-mode)) -(use-package rainbow-mode) -(use-package s) -(use-package smartparens :init (smartparens-global-mode)) -(use-package string-edit) -(use-package telephone-line) ;; configuration happens outside of use-package -(use-package undo-tree :init (global-undo-tree-mode)) -(use-package uuidgen) -(use-package which-key :init (which-key-mode t)) - -;; -;; Applications in emacs -;; - -(use-package magit - :bind ("C-c g" . magit-status) - :init (setq magit-repository-directories '(("/home/vincent/projects" . 2)))) - -(use-package password-store) -(use-package pg) -(use-package restclient) - -;; -;; Packages providing language-specific functionality -;; - -(use-package cargo - :hook ((rust-mode . cargo-minor-mode) - (cargo-process-mode . visual-line-mode)) - :bind (:map cargo-minor-mode-map ("C-c C-c C-l" . ignore))) - -(use-package dockerfile-mode) - -(use-package eglot - :init (defvar rust-eglot-initialized nil) - :hook ((rust-mode . (lambda () - (unless rust-eglot-initialized - (call-interactively #'eglot) - (setq rust-eglot-initialized t)))))) - -(use-package erlang - :hook ((erlang-mode . (lambda () - ;; Don't indent after '>' while I'm writing - (local-set-key ">" 'self-insert-command))))) - -(use-package go-mode) -(use-package haskell-mode) - -(use-package jq-mode - :init (add-to-list 'auto-mode-alist '("\\.jq\\'" . jq-mode))) - -(use-package kotlin-mode - :bind (:map kotlin-mode-map ("" . indent-relative))) - -(use-package markdown-mode - :init - (add-to-list 'auto-mode-alist '("\\.txt\\'" . markdown-mode)) - (add-to-list 'auto-mode-alist '("\\.markdown\\'" . markdown-mode)) - (add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-mode))) - -(use-package markdown-toc) - -(use-package nix-mode - :bind (:map nix-mode-map ("" . nix-indent-line))) - -(use-package nginx-mode) -(use-package rust-mode) -(use-package terraform-mode) -(use-package toml-mode) -(use-package web-mode) -(use-package yaml-mode) - -;; -;; EXWM / NixOS related packages -;; - -;; Configure a few basics before moving on to package-specific initialisation. -(setq custom-file (concat user-emacs-directory "init/custom.el")) -(load custom-file) - -(defvar home-dir (expand-file-name "~")) - -;; Seed RNG -(random t) - -(defun load-other-settings () - (mapc 'require '(nixos - mail-setup - look-and-feel - functions - settings - modes - bindings - term-setup - eshell-setup)) - (telephone-line-setup) - (ace-window-display-mode) - - (use-package sly - :init (setq inferior-lisp-program (concat (nix-store-path "sbcl") "/bin/sbcl")) - ;;(add-to-list 'company-backends 'sly-company) - )) - - -;; Some packages can only be initialised after the rest of the -;; settings has been applied: - -(add-hook 'after-init-hook 'load-other-settings) -(put 'narrow-to-region 'disabled nil) -(put 'upcase-region 'disabled nil) diff --git a/init/bindings.el b/init/bindings.el deleted file mode 100644 index f10869a532..0000000000 --- a/init/bindings.el +++ /dev/null @@ -1,54 +0,0 @@ -;; Various keybindings, most of them taken from starter-kit-bindings - -;; Font size -(define-key global-map (kbd "C-+") 'text-scale-increase) -(define-key global-map (kbd "C--") 'text-scale-decrease) - -;; Use regex searches by default. -(global-set-key (kbd "\C-r") 'isearch-backward-regexp) -(global-set-key (kbd "M-%") 'query-replace-regexp) -(global-set-key (kbd "C-M-s") 'isearch-forward) -(global-set-key (kbd "C-M-r") 'isearch-backward) -(global-set-key (kbd "C-M-%") 'query-replace) - -;; Counsel stuff: -(global-set-key (kbd "C-c r g") 'counsel-rg) - -;; imenu instead of insert-file -(global-set-key (kbd "C-x i") 'imenu) - -;; Window switching. (C-x o goes to the next window) -(windmove-default-keybindings) ;; Shift+direction - -;; Start eshell or switch to it if it's active. -(global-set-key (kbd "C-x m") 'eshell) - -;; Start a new eshell even if one is active. -(global-set-key (kbd "C-x M") (lambda () (interactive) (eshell t))) - -(global-set-key (kbd "C-x p") 'ivy-browse-repositories) -(global-set-key (kbd "M-g M-g") 'goto-line-with-feedback) - -(global-set-key (kbd "C-c w") 'whitespace-cleanup) -(global-set-key (kbd "C-c a") 'align-regexp) - -;; Browse URLs (very useful for Gitlab's SSH output!) -(global-set-key (kbd "C-c b p") 'browse-url-at-point) -(global-set-key (kbd "C-c b b") 'browse-url) - -;; Goodness from @magnars -;; I don't need to kill emacs that easily -;; the mnemonic is C-x REALLY QUIT -(global-set-key (kbd "C-x r q") 'save-buffers-kill-terminal) -(global-set-key (kbd "C-x C-c") 'delete-frame) - -;; Open Fefes Blog -(global-set-key (kbd "C-c C-f") 'fefes-blog) - -;; Open a file in project: -(global-set-key (kbd "C-c f") 'project-find-file) - -;; Use swiper instead of isearch -(global-set-key "\C-s" 'swiper) - -(provide 'bindings) diff --git a/init/custom.el b/init/custom.el deleted file mode 100644 index 4c92f0d32f..0000000000 --- a/init/custom.el +++ /dev/null @@ -1,52 +0,0 @@ -(custom-set-variables - ;; custom-set-variables was added by Custom. - ;; If you edit it by hand, you could mess it up, so be careful. - ;; Your init file should contain only one such instance. - ;; If there is more than one, they won't work right. - '(ac-auto-show-menu 0.8) - '(ac-delay 0.2) - '(aprila-nixops-path "/home/vincent/projects/langler/nixops") - '(aprila-release-author "Vincent Ambo ") - '(aprila-releases-path "/home/vincent/projects/langler/docs/releases") - '(avy-background t) - '(cargo-process--custom-path-to-bin "env CARGO_INCREMENTAL=1 cargo") - '(cargo-process--enable-rust-backtrace 1) - '(custom-enabled-themes (quote (gruber-darker))) - '(custom-safe-themes - (quote - ("d61fc0e6409f0c2a22e97162d7d151dee9e192a90fa623f8d6a071dbf49229c6" "3c83b3676d796422704082049fc38b6966bcad960f896669dfc21a7a37a748fa" "89336ca71dae5068c165d932418a368a394848c3b8881b2f96807405d8c6b5b6" default))) - '(elnode-send-file-program "/run/current-system/sw/bin/cat") - '(frame-brackground-mode (quote dark)) - '(global-auto-complete-mode t) - '(intero-debug nil) - '(intero-global-mode t nil (intero)) - '(intero-package-version "0.1.31") - '(kubernetes-commands-display-buffer-function (quote display-buffer)) - '(magit-log-show-gpg-status t) - '(ns-alternate-modifier (quote none)) - '(ns-command-modifier (quote control)) - '(ns-right-command-modifier (quote meta)) - '(require-final-newline (quote visit-save))) -(custom-set-faces - ;; custom-set-faces was added by Custom. - ;; If you edit it by hand, you could mess it up, so be careful. - ;; Your init file should contain only one such instance. - ;; If there is more than one, they won't work right. - '(default ((t (:foreground "#e4e4ef" :background "#181818")))) - '(rainbow-delimiters-depth-1-face ((t (:foreground "#2aa198")))) - '(rainbow-delimiters-depth-2-face ((t (:foreground "#b58900")))) - '(rainbow-delimiters-depth-3-face ((t (:foreground "#268bd2")))) - '(rainbow-delimiters-depth-4-face ((t (:foreground "#dc322f")))) - '(rainbow-delimiters-depth-5-face ((t (:foreground "#859900")))) - '(rainbow-delimiters-depth-6-face ((t (:foreground "#268bd2")))) - '(rainbow-delimiters-depth-7-face ((t (:foreground "#cb4b16")))) - '(rainbow-delimiters-depth-8-face ((t (:foreground "#d33682")))) - '(rainbow-delimiters-depth-9-face ((t (:foreground "#839496")))) - '(term-color-black ((t (:background "#282828" :foreground "#282828")))) - '(term-color-blue ((t (:background "#96a6c8" :foreground "#96a6c8")))) - '(term-color-cyan ((t (:background "#1fad83" :foreground "#1fad83")))) - '(term-color-green ((t (:background "#73c936" :foreground "#73c936")))) - '(term-color-magenta ((t (:background "#9e95c7" :foreground "#9e95c7")))) - '(term-color-red ((t (:background "#f43841" :foreground "#f43841")))) - '(term-color-white ((t (:background "#f5f5f5" :foreground "#f5f5f5")))) - '(term-color-yellow ((t (:background "#ffdd33" :foreground "#ffdd33"))))) diff --git a/init/eshell-setup.el b/init/eshell-setup.el deleted file mode 100644 index 0b23c5a2d1..0000000000 --- a/init/eshell-setup.el +++ /dev/null @@ -1,68 +0,0 @@ -;; EShell configuration - -(require 'eshell) - -;; Generic settings -;; Hide banner message ... -(setq eshell-banner-message "") - -;; Prompt configuration -(defun clean-pwd (path) - "Turns a path of the form /foo/bar/baz into /f/b/baz - (inspired by fish shell)" - (let* ((hpath (replace-regexp-in-string home-dir - "~" - path)) - (current-dir (split-string hpath "/")) - (cdir (last current-dir)) - (head (butlast current-dir))) - (concat (mapconcat (lambda (s) - (if (string= "" s) nil - (substring s 0 1))) - head - "/") - (if head "/" nil) - (car cdir)))) - -(defun vcprompt (&optional args) - "Call the external vcprompt command with optional arguments. - VCPrompt" - (replace-regexp-in-string - "\n" "" - (shell-command-to-string (concat "vcprompt" args)))) - -(defmacro with-face (str &rest properties) - `(propertize ,str 'face (list ,@properties))) - -(defun prompt-f () - "EShell prompt displaying VC info and such" - (concat - (with-face (concat (clean-pwd (eshell/pwd)) " ") :foreground "#96a6c8") - (if (= 0 (user-uid)) - (with-face "#" :foreground "#f43841") - (with-face "$" :foreground "#73c936")) - (with-face " " :foreground "#95a99f"))) - - -(setq eshell-prompt-function 'prompt-f) -(setq eshell-highlight-prompt nil) -(setq eshell-prompt-regexp "^.+? \\((\\(git\\|svn\\|hg\\|darcs\\|cvs\\|bzr\\):.+?) \\)?[$#] ") - -;; Ignore version control folders in autocompletion -(setq eshell-cmpl-cycle-completions nil - eshell-save-history-on-exit t - eshell-cmpl-dir-ignore "\\`\\(\\.\\.?\\|CVS\\|\\.svn\\|\\.git\\)/\\'") - -;; Load some EShell extensions -(eval-after-load 'esh-opt - '(progn - (require 'em-term) - (require 'em-cmpl) - ;; More visual commands! - (add-to-list 'eshell-visual-commands "ssh") - (add-to-list 'eshell-visual-commands "tail") - (add-to-list 'eshell-visual-commands "sl"))) - -(setq eshell-directory-name "~/.config/eshell/") - -(provide 'eshell-setup) diff --git a/init/functions.el b/init/functions.el deleted file mode 100644 index 8b96a0e737..0000000000 --- a/init/functions.el +++ /dev/null @@ -1,266 +0,0 @@ -(require 's) -;; A few handy functions I use in init.el (or not, but they're nice to -;; have) - -(defun custom-download-theme (url filename) - "Downloads a theme through HTTP and places it in ~/.emacs.d/themes" - - ;; Ensure the directory exists - (unless (file-exists-p "~/.emacs.d/themes") - (make-directory "~/.emacs.d/themes")) - - ;; Adds the themes folder to the theme load path (if not already - ;; there) - (unless (member "~/.emacs.d/themes" custom-theme-load-path) - (add-to-list 'custom-theme-load-path "~/.emacs.d/themes")) - - ;; Download file if it doesn't exist. - - (let ((file - (concat "~/.emacs.d/themes/" filename))) - (unless (file-exists-p file) - (url-copy-file url file)))) - -(defun custom-download-script (url filename) - "Downloads an Elisp script, places it in ~/.emacs/other and then loads it" - - ;; Ensure the directory exists - (unless (file-exists-p "~/.emacs.d/other") - (make-directory "~/.emacs.d/other")) - - ;; Download file if it doesn't exist. - (let ((file - (concat "~/.emacs.d/other/" filename))) - (unless (file-exists-p file) - (url-copy-file url file)) - - (load file))) - -(defun keychain-password (account &optional keychain) - "Returns the password for the account, by default it's looked up in the Login.keychain but a - different keychain can be specified." - (let ((k (if keychain keychain "Login.keychain"))) - (replace-regexp-in-string - "\n" "" - (shell-command-to-string (concat "security find-generic-password -w -a " - account - " " - k))))) - -;; This clones a git repository to 'foldername in .emacs.d -;; if there isn't already a folder with that name -(defun custom-clone-git (url foldername) - "Clones a git repository to .emacs.d/foldername" - (let ((fullpath (concat "~/.emacs.d/" foldername))) - (unless (file-exists-p fullpath) - (async-shell-command (concat "git clone " url " " fullpath))))) - -(defun load-file-if-exists (filename) - (if (file-exists-p filename) - (load filename))) - -(defun goto-line-with-feedback () - "Show line numbers temporarily, while prompting for the line number input" - (interactive) - (unwind-protect - (progn - (setq-local display-line-numbers t) - (let ((target (read-number "Goto line: "))) - (avy-push-mark) - (goto-line target))) - (setq-local display-line-numbers nil))) - - -(defun untabify-buffer () - (interactive) - (untabify (point-min) (point-max))) - -(defun indent-buffer () - (interactive) - (indent-region (point-min) (point-max))) - -(defun cleanup-buffer () - "Perform a bunch of operations on the whitespace content of a buffer. -Including indent-buffer, which should not be called automatically on save." - (interactive) - (untabify-buffer) - (delete-trailing-whitespace) - (indent-buffer)) - -;; These come from the emacs starter kit - -(defun esk-add-watchwords () - (font-lock-add-keywords - nil '(("\\<\\(FIX\\(ME\\)?\\|TODO\\|DEBUG\\|HACK\\|REFACTOR\\|NOCOMMIT\\)" - 1 font-lock-warning-face t)))) - -(defun esk-sudo-edit (&optional arg) - (interactive "p") - (if (or arg (not buffer-file-name)) - (find-file (concat "/sudo:root@localhost:" (read-file-name "File: "))) - (find-alternate-file (concat "/sudo:root@localhost:" buffer-file-name)))) - -;; Open Fefes blog -(defun fefes-blog () - (interactive) - (eww "https://blog.fefe.de/")) - -;; Open this machines NixOS config -(defun nix-config () - (interactive) - (find-file "/etc/nixos/configuration.nix")) - -;; Open the NixOS man page -(defun nixos-man () - (interactive) - (man "configuration.nix")) - -;; Open local emacs configuration -(defun emacs-config () - (interactive) - (dired "~/.emacs.d/")) - -;; Get the nix store path for a given derivation. -;; If the derivation has not been built before, this will trigger a build. -(defun nix-store-path (derivation) - (let ((expr (concat "with import {}; " derivation))) - (s-chomp (shell-command-to-string (concat "nix-build -E '" expr "'"))))) - -(defun insert-nix-store-path () - (interactive) - (let ((derivation (read-string "Derivation name (in ): "))) - (insert (nix-store-path derivation)))) - -(defun toggle-force-newline () - "Buffer-local toggle for enforcing final newline on save." - (interactive) - (setq-local require-final-newline (not require-final-newline)) - (message "require-final-newline in buffer %s is now %s" - (buffer-name) - require-final-newline)) - -;; Helm includes a command to run external applications, which does -;; not seem to exist in ivy. This implementation uses some of the -;; logic from Helm to provide similar functionality using ivy. -(defun list-external-commands () - "Creates a list of all external commands available on $PATH - while filtering NixOS wrappers." - (cl-loop - for dir in (split-string (getenv "PATH") path-separator) - when (and (file-exists-p dir) (file-accessible-directory-p dir)) - for lsdir = (cl-loop for i in (directory-files dir t) - for bn = (file-name-nondirectory i) - when (and (not (s-contains? "-wrapped" i)) - (not (member bn completions)) - (not (file-directory-p i)) - (file-executable-p i)) - collect bn) - append lsdir into completions - finally return (sort completions 'string-lessp))) - -(defun run-external-command (cmd) - "Execute the specified command and notify the user when it - finishes." - (message "Starting %s..." cmd) - (set-process-sentinel - (start-process-shell-command cmd nil cmd) - (lambda (process event) - (when (string= event "finished\n") - (message "%s process finished." process))))) - -(defun ivy-run-external-command () - "Prompts the user with a list of all installed applications and - lets them select one to launch." - - (interactive) - (let ((external-commands-list (list-external-commands))) - (ivy-read "Command:" external-commands-list - :require-match t - :history 'external-commands-history - :action #'run-external-command))) - -(defun ivy-password-store (&optional password-store-dir) - "Custom version of password-store integration with ivy that - actually uses the GPG agent correctly." - - (interactive) - (ivy-read "Copy password of entry: " - (password-store-list (or password-store-dir (password-store-dir))) - :require-match t - :keymap ivy-pass-map - :action (lambda (entry) - (let ((password (auth-source-pass-get 'secret entry))) - (password-store-clear) - (kill-new password) - (setq password-store-kill-ring-pointer kill-ring-yank-pointer) - (message "Copied %s to the kill ring. Will clear in %s seconds." - entry (password-store-timeout)) - (setq password-store-timeout-timer - (run-at-time (password-store-timeout) - nil 'password-store-clear)))))) - -(defun ivy-browse-repositories () - "Select a git repository and open its associated magit buffer." - - (interactive) - (ivy-read "Repository: " - (magit-list-repos) - :require-match t - :sort t - :action #'magit-status)) - -(defun warmup-gpg-agent (arg &optional exit) - "Function used to warm up the GPG agent before use. This is - useful in cases where there is no easy way to make pinentry run - in the correct context (such as when sending email)." - (interactive) - (message "Warming up GPG agent") - (epg-sign-string (epg-make-context) "dummy") - nil) - -(defun bottom-right-window-p () - "Determines whether the last (i.e. bottom-right) window of the - active frame is showing the buffer in which this function is - executed." - (let* ((frame (selected-frame)) - (right-windows (window-at-side-list frame 'right)) - (bottom-windows (window-at-side-list frame 'bottom)) - (last-window (car (seq-intersection right-windows bottom-windows)))) - (eq (current-buffer) (window-buffer last-window)))) - -(defun inferior-erlang-nix-shell () - "Start an inferior Erlang process from the root of the current - project." - (interactive) - (inferior-erlang - (format "nix-shell --command erl %s" (cdr (project-current))))) - -(defun intero-fix-ghci-panic () - "Disable deferring of out of scope variable errors, which - triggers a bug in the interactive Emacs REPL printing a panic - under certain conditions." - - (interactive) - (let* ((root (intero-project-root)) - (package-name (intero-package-name)) - (backend-buffer (intero-buffer 'backend)) - (name (format "*intero:%s:%s:repl*" - (file-name-nondirectory root) - package-name)) - (setting ":set -fno-defer-out-of-scope-variables\n")) - (when (get-buffer name) - (with-current-buffer (get-buffer name) - (goto-char (point-max)) - (let ((process (get-buffer-process (current-buffer)))) - (when process (process-send-string process setting))))))) - -;; Brute-force fix: Ensure the setting is injected every time the REPL -;; is selected. -;; -;; Upstream issue: https://github.com/commercialhaskell/intero/issues/569 -(advice-add 'intero-repl :after (lambda (&rest r) (intero-fix-ghci-panic)) - '((name . intero-panic-fix))) -(advice-add 'intero-repl-load :after (lambda (&rest r) (intero-fix-ghci-panic)) - '((name . intero-panic-fix))) - -(provide 'functions) diff --git a/init/look-and-feel.el b/init/look-and-feel.el deleted file mode 100644 index 3d480bd5f4..0000000000 --- a/init/look-and-feel.el +++ /dev/null @@ -1,115 +0,0 @@ -;;; -*- lexical-binding: t; -*- - -;; Hide those ugly tool bars: -(tool-bar-mode 0) -(scroll-bar-mode 0) -(menu-bar-mode 0) -(add-hook 'after-make-frame-functions - (lambda (frame) (scroll-bar-mode 0))) - -;; Don't do any annoying things: -(setq ring-bell-function 'ignore) -(setq initial-scratch-message "") - -;; Remember layout changes -(winner-mode 1) - -;; Usually emacs will run as a proper GUI application, in which case a few -;; extra settings are nice-to-have: -(when window-system - (setq frame-title-format '(buffer-file-name "%f" ("%b"))) - (mouse-wheel-mode t) - (blink-cursor-mode -1)) - -;; Configure editor fonts -(let ((font (format "Input Mono-%d" 12))) - (setq default-frame-alist `((font-backend . "xft") - (font . ,font))) - (set-frame-font font t t)) - -;; Display battery in mode-line's misc section on adho: -(when (equal "adho" (system-name)) - (setq battery-mode-line-format " %b%p%%") - (display-battery-mode)) - -;; Configure telephone-line -(defun telephone-misc-if-last-window () - "Renders the mode-line-misc-info string for display in the - mode-line if the currently active window is the last one in the - frame. - - The idea is to not display information like the current time, - load, battery levels in all buffers." - - (when (bottom-right-window-p) - (telephone-line-raw mode-line-misc-info t))) - -(defun telephone-line-setup () - (telephone-line-defsegment telephone-line-last-window-segment () - (telephone-misc-if-last-window)) - - ;; Display the current EXWM workspace index in the mode-line - (telephone-line-defsegment telephone-line-exwm-workspace-index () - (when (bottom-right-window-p) - (format "[%s]" exwm-workspace-current-index))) - - ;; Define a highlight font for ~ important ~ information in the last - ;; window. - (defface special-highlight '((t (:foreground "white" :background "#5f627f"))) "") - (add-to-list 'telephone-line-faces - '(highlight . (special-highlight . special-highlight))) - - (setq telephone-line-lhs - '((nil . (telephone-line-position-segment)) - (accent . (telephone-line-buffer-segment)))) - - (setq telephone-line-rhs - '((accent . (telephone-line-major-mode-segment)) - (nil . (telephone-line-last-window-segment - telephone-line-exwm-workspace-index)) - (highlight . (telephone-line-notmuch-counts)))) - - (setq telephone-line-primary-left-separator 'telephone-line-tan-left - telephone-line-primary-right-separator 'telephone-line-tan-right - telephone-line-secondary-left-separator 'telephone-line-tan-hollow-left - telephone-line-secondary-right-separator 'telephone-line-tan-hollow-right) - - (telephone-line-mode 1)) - -;; Auto refresh buffers -(global-auto-revert-mode 1) - -;; Use clipboard properly -(setq select-enable-clipboard t) - -;; Show in-progress chords in minibuffer -(setq echo-keystrokes 0.1) - -;; Show column numbers in all buffers -(column-number-mode t) - -;; Highlight currently active line -(global-hl-line-mode t) - -(defalias 'yes-or-no-p 'y-or-n-p) -(defalias 'auto-tail-revert-mode 'tail-mode) - -;; Style line numbers (shown with M-g g) -(setq linum-format - (lambda (line) - (propertize - (format (concat " %" - (number-to-string - (length (number-to-string - (line-number-at-pos (point-max))))) - "d ") - line) - 'face 'linum))) - -;; Display tabs as 2 spaces -(setq tab-width 2) - -;; Don't wrap around when moving between buffers -(setq windmove-wrap-around nil) - -(provide 'look-and-feel) diff --git a/init/mail-setup.el b/init/mail-setup.el deleted file mode 100644 index 1700ccddd3..0000000000 --- a/init/mail-setup.el +++ /dev/null @@ -1,98 +0,0 @@ -(require 'notmuch) -(require 'counsel-notmuch) - -(global-set-key (kbd "C-c m") 'notmuch-hello) -(global-set-key (kbd "C-c C-m") 'counsel-notmuch) -(global-set-key (kbd "C-c C-e n") 'notmuch-mua-new-mail) - -(setq notmuch-cache-dir (format "%s/.cache/notmuch" (getenv "HOME"))) -(make-directory notmuch-cache-dir t) - -;; Cache addresses for completion: -(setq notmuch-address-save-filename (concat notmuch-cache-dir "/addresses")) - -;; Don't spam my home folder with drafts: -(setq notmuch-draft-folder "drafts") ;; relative to notmuch database - -;; Mark things as read when archiving them: -(setq notmuch-archive-tags '("-inbox" "-unread" "+archive")) - -;; Show me saved searches that I care about: -(setq notmuch-saved-searches - '((:name "inbox" :query "tag:inbox" :count-query "tag:inbox AND tag:unread" :key "i") - (:name "aprila-dev" :query "tag:aprila-dev" :count-query "tag:aprila-dev AND tag:unread" :key "d") - (:name "gitlab" :query "tag:gitlab" :key "g") - (:name "sent" :query "tag:sent" :key "t") - (:name "drafts" :query "tag:draft"))) -(setq notmuch-show-empty-saved-searches t) - -;; Mail sending configuration -(setq send-mail-function 'sendmail-send-it) ;; sendmail provided by MSMTP -(setq notmuch-always-prompt-for-sender t) -(setq notmuch-mua-user-agent-function - (lambda () (format "Emacs %s; notmuch.el %s" emacs-version notmuch-emacs-version))) -(setq mail-host-address (system-name)) -(setq notmuch-mua-cite-function #'message-cite-original-without-signature) - -;; Close mail buffers after sending mail -(setq message-kill-buffer-on-exit t) - -;; Ensure sender is correctly passed to msmtp -(setq mail-specify-envelope-from t - message-sendmail-envelope-from 'header - mail-envelope-from 'header) - -;; Store sent mail in the correct folder per account -(setq notmuch-maildir-use-notmuch-insert nil) -(setq notmuch-fcc-dirs '(("mail@tazj.in" . "tazjin/Sent") - ;; Not a mistake, Office365 apparently - ;; renames IMAP folders (!) to your local - ;; language instead of providing translations - ;; in the UI m( - ("vincent@aprila.no" . "aprila/Sende element"))) - -;; I don't use drafts but I instinctively hit C-x C-s constantly, lets -;; handle that gracefully. -(define-key notmuch-message-mode-map (kbd "C-x C-s") #'ignore) - -;; MSMTP decrypts passwords using pass, but pinentry doesn't work -;; correctly in that setup. This forces a warmup of the GPG agent -;; before sending the message. -;; -;; Note that the sending function is advised because the provided hook -;; for this seems to run at the wrong time. -(advice-add 'notmuch-mua-send-common :before 'warmup-gpg-agent) - -;; Define a telephone-line segment for displaying the count of unread, -;; important mails in the last window's mode-line: -(defvar *last-notmuch-count-redraw* 0) -(defvar *current-notmuch-count* nil) - -(defun update-display-notmuch-counts () - "Update and render the current state of the notmuch unread - count for display in the mode-line. - - The offlineimap-timer runs every 2 minutes, so it does not make - sense to refresh this much more often than that." - - (when (> (- (float-time) *last-notmuch-count-redraw*) 30) - (setq *last-notmuch-count-redraw* (float-time)) - (let* ((inbox-unread (notmuch-saved-search-count "tag:inbox and tag:unread")) - (devel-unread (notmuch-saved-search-count "tag:aprila-dev and tag:unread")) - (notmuch-count (format "I: %s; D: %s" inbox-unread devel-unread))) - (setq *current-notmuch-count* notmuch-count))) - - (when (and (bottom-right-window-p) - ;; Only render if the initial update is done and there - ;; are unread mails: - *current-notmuch-count* - (not (equal *current-notmuch-count* "I: 0; D: 0"))) - *current-notmuch-count*)) - -(telephone-line-defsegment telephone-line-notmuch-counts () - "This segment displays the count of unread notmuch messages in - the last window's mode-line (if unread messages are present)." - - (update-display-notmuch-counts)) - -(provide 'mail-setup) diff --git a/init/modes.el b/init/modes.el deleted file mode 100644 index 19ed2a6843..0000000000 --- a/init/modes.el +++ /dev/null @@ -1,36 +0,0 @@ -;; Initializes modes I use. - -(add-hook 'prog-mode-hook 'esk-add-watchwords) - -;; Use auto-complete as completion at point -(defun set-auto-complete-as-completion-at-point-function () - (setq completion-at-point-functions '(auto-complete))) - -(add-hook 'auto-complete-mode-hook - 'set-auto-complete-as-completion-at-point-function) - -;; Enable rainbow-delimiters for all things programming -(add-hook 'prog-mode-hook 'rainbow-delimiters-mode) - -;; Enable Paredit & Company in Emacs Lisp mode -(add-hook 'emacs-lisp-mode-hook 'company-mode) - -;; Always highlight matching brackets -(show-paren-mode 1) - -;; Always auto-close parantheses and other pairs -;; (replaced by smartparens) -;; (electric-pair-mode) - -;; Keep track of recent files -(recentf-mode) - -;; Easily navigate sillycased words -(global-subword-mode 1) - -;; Transparently open compressed files -(auto-compression-mode t) - -;; Show available key chord completions - -(provide 'modes) diff --git a/init/nixos.el b/init/nixos.el deleted file mode 100644 index e384e9b77d..0000000000 --- a/init/nixos.el +++ /dev/null @@ -1,103 +0,0 @@ -;; Configure additional settings if this is one of my NixOS machines -;; (i.e. if ExWM is required) -;; -*- lexical-binding: t; -*- - -(require 's) -(require 'f) -(require 'dash) - -(defun pulseaudio-ctl (cmd) - (shell-command (concat "pulseaudio-ctl " cmd)) - (message "Volume command: %s" cmd)) - -(defun volume-mute () (interactive) (pulseaudio-ctl "mute")) -(defun volume-up () (interactive) (pulseaudio-ctl "up")) -(defun volume-down () (interactive) (pulseaudio-ctl "down")) - -(defun brightness-up () - (interactive) - (shell-command "exec light -A 10") - (message "Brightness increased")) - -(defun brightness-down () - (interactive) - (shell-command "exec light -U 10") - (message "Brightness decreased")) - -(defun lock-screen () - (interactive) - (shell-command "screen-lock")) - -(defun generate-randr-config () - (-flatten `(,(-map (lambda (n) (list n "DP2")) (number-sequence 1 7)) - (0 "eDP1") - ,(-map (lambda (n) (list n "eDP1")) (number-sequence 8 9))))) - -(use-package exwm - :hook ((exwm-update-class . (lambda () - ;; Make class name the buffer name - (exwm-workspace-rename-buffer exwm-class-name)))) - :init - (progn - (require 'exwm-config) - - (fringe-mode 3) - - (setq exwm-workspace-number 10) - - ;; 's-r': Reset - (exwm-input-set-key (kbd "s-r") #'exwm-reset) - ;; 's-w': Switch workspace - (exwm-input-set-key (kbd "s-w") #'exwm-workspace-switch) - ;; 's-N': Switch to certain workspace - (dotimes (i 10) - (exwm-input-set-key (kbd (format "s-%d" i)) - `(lambda () - (interactive) - (exwm-workspace-switch-create ,i)))) - - ;; Launch applications with completion (dmenu style!) - (exwm-input-set-key (kbd "s-d") #'ivy-run-external-command) - (exwm-input-set-key (kbd "s-p") #'ivy-password-store) - (exwm-input-set-key (kbd "C-s-p") '(lambda () - (interactive) - (ivy-password-store "~/.aprila-secrets"))) - - ;; Add Alacritty selector to a key - (exwm-input-set-key (kbd "C-x t") #'counsel-switch-to-alacritty) - - ;; Toggle between line-mode / char-mode - (exwm-input-set-key (kbd "C-c C-t C-t") #'exwm-input-toggle-keyboard) - - ;; Volume keys - (exwm-input-set-key (kbd "") #'volume-mute) - (exwm-input-set-key (kbd "") #'volume-up) - (exwm-input-set-key (kbd "") #'volume-down) - - ;; Brightness keys - (exwm-input-set-key (kbd "") #'brightness-down) - (exwm-input-set-key (kbd "") #'brightness-up) - (exwm-input-set-key (kbd "") #'lock-screen) - - ;; Line-editing shortcuts - (exwm-input-set-simulation-keys - '(([?\C-d] . delete) - ([?\C-w] . ?\C-c))) - - ;; Enable EXWM - (exwm-enable) - - ;; Show time in the mode line - (display-time-mode) - - ;; Configure xrandr when running on laptop - (when (equal (shell-command-to-string "hostname") "adho\n") - (require 'exwm-randr) - (setq exwm-randr-workspace-output-plist (generate-randr-config)) - (exwm-randr-enable)) - - ;; Let buffers move seamlessly between workspaces - (setq exwm-workspace-show-all-buffers t) - (setq exwm-layout-show-all-buffers t))) - -(provide 'nixos) diff --git a/init/settings.el b/init/settings.el deleted file mode 100644 index 2e4dedc0a5..0000000000 --- a/init/settings.el +++ /dev/null @@ -1,65 +0,0 @@ -(require 'prescient) -(require 'ivy-prescient) -(require 'uniquify) -(require 'ivy-pass) - -;; Make ivy go! -(ivy-mode 1) -(counsel-mode 1) - -(setq ivy-use-virtual-buffers t) -(setq enable-recursive-minibuffers t) - -;; Enable support for prescient in ivy & configure it -(ivy-prescient-mode) -(prescient-persist-mode) -(add-to-list 'ivy-prescient-excluded-commands 'counsel-rg) - -;; Move files to trash when deleting -(setq delete-by-moving-to-trash t) - -;; We don't live in the 80s, but we're also not a shitty web app. -(setq gc-cons-threshold 20000000) - -(setq uniquify-buffer-name-style 'forward) - -; Fix some defaults -(setq visible-bell nil - inhibit-startup-message t - color-theme-is-global t - sentence-end-double-space nil - shift-select-mode nil - uniquify-buffer-name-style 'forward - whitespace-style '(face trailing lines-tail tabs) - whitespace-line-column 80 - default-directory "~" - fill-column 80 - ediff-split-window-function 'split-window-horizontally) - -(add-to-list 'safe-local-variable-values '(lexical-binding . t)) -(add-to-list 'safe-local-variable-values '(whitespace-line-column . 80)) - -(set-default 'indent-tabs-mode nil) - -;; UTF-8 please -(setq locale-coding-system 'utf-8) ; pretty -(set-terminal-coding-system 'utf-8) ; pretty -(set-keyboard-coding-system 'utf-8) ; pretty -(set-selection-coding-system 'utf-8) ; please -(prefer-coding-system 'utf-8) ; with sugar on top - -;; Make emacs behave sanely (overwrite selected text) -(delete-selection-mode 1) - -;; Keep your temporary files in tmp, emacs! -(setq auto-save-file-name-transforms - `((".*" ,temporary-file-directory t))) -(setq backup-directory-alist - `((".*" . ,temporary-file-directory))) - -(remove-hook 'kill-buffer-query-functions 'server-kill-buffer-query-function) - -;; Show time in 24h format -(setq display-time-24hr-format t) - -(provide 'settings) diff --git a/init/term-setup.el b/init/term-setup.el deleted file mode 100644 index a2a71be9ee..0000000000 --- a/init/term-setup.el +++ /dev/null @@ -1,37 +0,0 @@ -;; Utilities for Alacritty buffers. - -(defun open-or-create-alacritty-buffer (buffer-name) - "Switch to the buffer with BUFFER-NAME or create a - new buffer running Alacritty." - (let ((buffer (get-buffer buffer-name))) - (if (not buffer) - (run-external-command "alacritty") - (switch-to-buffer buffer)))) - -(defun is-alacritty-buffer (buffer) - "Determine whether BUFFER runs Alacritty." - (and (equal 'exwm-mode (buffer-local-value 'major-mode buffer)) - (s-starts-with? "Alacritty" (buffer-name buffer)))) - -(defun counsel-switch-to-alacritty () - "Switch to a (multi-)term buffer or create one." - (interactive) - (let ((terms (-map #'buffer-name - (-filter #'is-alacritty-buffer (buffer-list))))) - (if terms - (ivy-read "Switch to Alacritty buffer: " - (cons "New terminal" terms) - :caller 'counsel-switch-to-alacritty - :require-match t - :action #'open-or-create-alacritty-buffer) - (run-external-command "alacritty")))) - -(defun alacritty-rename () - "Rename the current terminal buffer." - (interactive) - (let* ((buffer (get-buffer (buffer-name)))) - (if (is-alacritty-buffer buffer) - (rename-buffer (format "Alacritty<%s>" (read-string "New terminal name: "))) - (error "This function is only intended to rename Alacritty buffers.")))) - -(provide 'term-setup) diff --git a/tools/emacs/.gitignore b/tools/emacs/.gitignore new file mode 100644 index 0000000000..7b666905f8 --- /dev/null +++ b/tools/emacs/.gitignore @@ -0,0 +1,11 @@ +.smex-items +*token* +auto-save-list/ +clones/ +elpa/ +irc.el +local.el +other/ +scripts/ +themes/ +*.elc diff --git a/tools/emacs/README.md b/tools/emacs/README.md new file mode 100644 index 0000000000..2dd067a910 --- /dev/null +++ b/tools/emacs/README.md @@ -0,0 +1,6 @@ +emacs.d +======== + +This contains my emacs.d folder. + +I use emacs for many things. diff --git a/tools/emacs/init.el b/tools/emacs/init.el new file mode 100644 index 0000000000..66d38cd9fc --- /dev/null +++ b/tools/emacs/init.el @@ -0,0 +1,168 @@ +;;; init.el --- Package bootstrapping. -*- lexical-binding: t; -*- + +;; Packages are installed via Nix configuration, this file only +;; initialises the newly loaded packages. + +(require 'use-package) +(require 'seq) + +(package-initialize) + +;; Add 'init' folder that contains other settings to load. +(add-to-list 'load-path (concat user-emacs-directory "init")) + +;; Initialise all packages installed via Nix. +;; +;; TODO: Generate this section in Nix for all packages that do not +;; require special configuration. + +;; +;; Packages providing generic functionality. +;; + +(use-package ace-window + :bind (("C-x o" . ace-window)) + :init + (setq aw-keys '(?f ?j ?d ?k ?s ?l ?a) + aw-scope 'frame)) + +(use-package auth-source-pass :init (auth-source-pass-enable)) + +(use-package avy + :bind (("M-j" . avy-goto-char) + ("M-p" . avy-pop-mark) + ("M-g g" . avy-goto-line))) + +(use-package browse-kill-ring) + +(use-package company + :hook ((prog-mode . company-mode)) + :bind (:map rust-mode-map ("" . company-indent-or-complete-common) + :map lisp-mode-map ("" . company-indent-or-complete-common)) + :init (setq company-tooltip-align-annotations t)) + +(use-package dash) +(use-package dash-functional) +(use-package edit-server :init (edit-server-start)) +(use-package gruber-darker-theme) +(use-package ht) +(use-package hydra) +(use-package idle-highlight-mode :hook ((prog-mode . idle-highlight-mode))) +(use-package paredit :hook ((lisp-mode . paredit-mode) + (emacs-lisp-mode . paredit-mode))) +(use-package multiple-cursors) +(use-package pinentry + :init + (setq epa-pinentry-mode 'loopback) + (pinentry-start)) + +(use-package rainbow-delimiters :hook (prog-mode . rainbow-delimiters-mode)) +(use-package rainbow-mode) +(use-package s) +(use-package smartparens :init (smartparens-global-mode)) +(use-package string-edit) +(use-package telephone-line) ;; configuration happens outside of use-package +(use-package undo-tree :init (global-undo-tree-mode)) +(use-package uuidgen) +(use-package which-key :init (which-key-mode t)) + +;; +;; Applications in emacs +;; + +(use-package magit + :bind ("C-c g" . magit-status) + :init (setq magit-repository-directories '(("/home/vincent/projects" . 2)))) + +(use-package password-store) +(use-package pg) +(use-package restclient) + +;; +;; Packages providing language-specific functionality +;; + +(use-package cargo + :hook ((rust-mode . cargo-minor-mode) + (cargo-process-mode . visual-line-mode)) + :bind (:map cargo-minor-mode-map ("C-c C-c C-l" . ignore))) + +(use-package dockerfile-mode) + +(use-package eglot + :init (defvar rust-eglot-initialized nil) + :hook ((rust-mode . (lambda () + (unless rust-eglot-initialized + (call-interactively #'eglot) + (setq rust-eglot-initialized t)))))) + +(use-package erlang + :hook ((erlang-mode . (lambda () + ;; Don't indent after '>' while I'm writing + (local-set-key ">" 'self-insert-command))))) + +(use-package go-mode) +(use-package haskell-mode) + +(use-package jq-mode + :init (add-to-list 'auto-mode-alist '("\\.jq\\'" . jq-mode))) + +(use-package kotlin-mode + :bind (:map kotlin-mode-map ("" . indent-relative))) + +(use-package markdown-mode + :init + (add-to-list 'auto-mode-alist '("\\.txt\\'" . markdown-mode)) + (add-to-list 'auto-mode-alist '("\\.markdown\\'" . markdown-mode)) + (add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-mode))) + +(use-package markdown-toc) + +(use-package nix-mode + :bind (:map nix-mode-map ("" . nix-indent-line))) + +(use-package nginx-mode) +(use-package rust-mode) +(use-package terraform-mode) +(use-package toml-mode) +(use-package web-mode) +(use-package yaml-mode) + +;; +;; EXWM / NixOS related packages +;; + +;; Configure a few basics before moving on to package-specific initialisation. +(setq custom-file (concat user-emacs-directory "init/custom.el")) +(load custom-file) + +(defvar home-dir (expand-file-name "~")) + +;; Seed RNG +(random t) + +(defun load-other-settings () + (mapc 'require '(nixos + mail-setup + look-and-feel + functions + settings + modes + bindings + term-setup + eshell-setup)) + (telephone-line-setup) + (ace-window-display-mode) + + (use-package sly + :init (setq inferior-lisp-program (concat (nix-store-path "sbcl") "/bin/sbcl")) + ;;(add-to-list 'company-backends 'sly-company) + )) + + +;; Some packages can only be initialised after the rest of the +;; settings has been applied: + +(add-hook 'after-init-hook 'load-other-settings) +(put 'narrow-to-region 'disabled nil) +(put 'upcase-region 'disabled nil) diff --git a/tools/emacs/init/bindings.el b/tools/emacs/init/bindings.el new file mode 100644 index 0000000000..f10869a532 --- /dev/null +++ b/tools/emacs/init/bindings.el @@ -0,0 +1,54 @@ +;; Various keybindings, most of them taken from starter-kit-bindings + +;; Font size +(define-key global-map (kbd "C-+") 'text-scale-increase) +(define-key global-map (kbd "C--") 'text-scale-decrease) + +;; Use regex searches by default. +(global-set-key (kbd "\C-r") 'isearch-backward-regexp) +(global-set-key (kbd "M-%") 'query-replace-regexp) +(global-set-key (kbd "C-M-s") 'isearch-forward) +(global-set-key (kbd "C-M-r") 'isearch-backward) +(global-set-key (kbd "C-M-%") 'query-replace) + +;; Counsel stuff: +(global-set-key (kbd "C-c r g") 'counsel-rg) + +;; imenu instead of insert-file +(global-set-key (kbd "C-x i") 'imenu) + +;; Window switching. (C-x o goes to the next window) +(windmove-default-keybindings) ;; Shift+direction + +;; Start eshell or switch to it if it's active. +(global-set-key (kbd "C-x m") 'eshell) + +;; Start a new eshell even if one is active. +(global-set-key (kbd "C-x M") (lambda () (interactive) (eshell t))) + +(global-set-key (kbd "C-x p") 'ivy-browse-repositories) +(global-set-key (kbd "M-g M-g") 'goto-line-with-feedback) + +(global-set-key (kbd "C-c w") 'whitespace-cleanup) +(global-set-key (kbd "C-c a") 'align-regexp) + +;; Browse URLs (very useful for Gitlab's SSH output!) +(global-set-key (kbd "C-c b p") 'browse-url-at-point) +(global-set-key (kbd "C-c b b") 'browse-url) + +;; Goodness from @magnars +;; I don't need to kill emacs that easily +;; the mnemonic is C-x REALLY QUIT +(global-set-key (kbd "C-x r q") 'save-buffers-kill-terminal) +(global-set-key (kbd "C-x C-c") 'delete-frame) + +;; Open Fefes Blog +(global-set-key (kbd "C-c C-f") 'fefes-blog) + +;; Open a file in project: +(global-set-key (kbd "C-c f") 'project-find-file) + +;; Use swiper instead of isearch +(global-set-key "\C-s" 'swiper) + +(provide 'bindings) diff --git a/tools/emacs/init/custom.el b/tools/emacs/init/custom.el new file mode 100644 index 0000000000..4c92f0d32f --- /dev/null +++ b/tools/emacs/init/custom.el @@ -0,0 +1,52 @@ +(custom-set-variables + ;; custom-set-variables was added by Custom. + ;; If you edit it by hand, you could mess it up, so be careful. + ;; Your init file should contain only one such instance. + ;; If there is more than one, they won't work right. + '(ac-auto-show-menu 0.8) + '(ac-delay 0.2) + '(aprila-nixops-path "/home/vincent/projects/langler/nixops") + '(aprila-release-author "Vincent Ambo ") + '(aprila-releases-path "/home/vincent/projects/langler/docs/releases") + '(avy-background t) + '(cargo-process--custom-path-to-bin "env CARGO_INCREMENTAL=1 cargo") + '(cargo-process--enable-rust-backtrace 1) + '(custom-enabled-themes (quote (gruber-darker))) + '(custom-safe-themes + (quote + ("d61fc0e6409f0c2a22e97162d7d151dee9e192a90fa623f8d6a071dbf49229c6" "3c83b3676d796422704082049fc38b6966bcad960f896669dfc21a7a37a748fa" "89336ca71dae5068c165d932418a368a394848c3b8881b2f96807405d8c6b5b6" default))) + '(elnode-send-file-program "/run/current-system/sw/bin/cat") + '(frame-brackground-mode (quote dark)) + '(global-auto-complete-mode t) + '(intero-debug nil) + '(intero-global-mode t nil (intero)) + '(intero-package-version "0.1.31") + '(kubernetes-commands-display-buffer-function (quote display-buffer)) + '(magit-log-show-gpg-status t) + '(ns-alternate-modifier (quote none)) + '(ns-command-modifier (quote control)) + '(ns-right-command-modifier (quote meta)) + '(require-final-newline (quote visit-save))) +(custom-set-faces + ;; custom-set-faces was added by Custom. + ;; If you edit it by hand, you could mess it up, so be careful. + ;; Your init file should contain only one such instance. + ;; If there is more than one, they won't work right. + '(default ((t (:foreground "#e4e4ef" :background "#181818")))) + '(rainbow-delimiters-depth-1-face ((t (:foreground "#2aa198")))) + '(rainbow-delimiters-depth-2-face ((t (:foreground "#b58900")))) + '(rainbow-delimiters-depth-3-face ((t (:foreground "#268bd2")))) + '(rainbow-delimiters-depth-4-face ((t (:foreground "#dc322f")))) + '(rainbow-delimiters-depth-5-face ((t (:foreground "#859900")))) + '(rainbow-delimiters-depth-6-face ((t (:foreground "#268bd2")))) + '(rainbow-delimiters-depth-7-face ((t (:foreground "#cb4b16")))) + '(rainbow-delimiters-depth-8-face ((t (:foreground "#d33682")))) + '(rainbow-delimiters-depth-9-face ((t (:foreground "#839496")))) + '(term-color-black ((t (:background "#282828" :foreground "#282828")))) + '(term-color-blue ((t (:background "#96a6c8" :foreground "#96a6c8")))) + '(term-color-cyan ((t (:background "#1fad83" :foreground "#1fad83")))) + '(term-color-green ((t (:background "#73c936" :foreground "#73c936")))) + '(term-color-magenta ((t (:background "#9e95c7" :foreground "#9e95c7")))) + '(term-color-red ((t (:background "#f43841" :foreground "#f43841")))) + '(term-color-white ((t (:background "#f5f5f5" :foreground "#f5f5f5")))) + '(term-color-yellow ((t (:background "#ffdd33" :foreground "#ffdd33"))))) diff --git a/tools/emacs/init/eshell-setup.el b/tools/emacs/init/eshell-setup.el new file mode 100644 index 0000000000..0b23c5a2d1 --- /dev/null +++ b/tools/emacs/init/eshell-setup.el @@ -0,0 +1,68 @@ +;; EShell configuration + +(require 'eshell) + +;; Generic settings +;; Hide banner message ... +(setq eshell-banner-message "") + +;; Prompt configuration +(defun clean-pwd (path) + "Turns a path of the form /foo/bar/baz into /f/b/baz + (inspired by fish shell)" + (let* ((hpath (replace-regexp-in-string home-dir + "~" + path)) + (current-dir (split-string hpath "/")) + (cdir (last current-dir)) + (head (butlast current-dir))) + (concat (mapconcat (lambda (s) + (if (string= "" s) nil + (substring s 0 1))) + head + "/") + (if head "/" nil) + (car cdir)))) + +(defun vcprompt (&optional args) + "Call the external vcprompt command with optional arguments. + VCPrompt" + (replace-regexp-in-string + "\n" "" + (shell-command-to-string (concat "vcprompt" args)))) + +(defmacro with-face (str &rest properties) + `(propertize ,str 'face (list ,@properties))) + +(defun prompt-f () + "EShell prompt displaying VC info and such" + (concat + (with-face (concat (clean-pwd (eshell/pwd)) " ") :foreground "#96a6c8") + (if (= 0 (user-uid)) + (with-face "#" :foreground "#f43841") + (with-face "$" :foreground "#73c936")) + (with-face " " :foreground "#95a99f"))) + + +(setq eshell-prompt-function 'prompt-f) +(setq eshell-highlight-prompt nil) +(setq eshell-prompt-regexp "^.+? \\((\\(git\\|svn\\|hg\\|darcs\\|cvs\\|bzr\\):.+?) \\)?[$#] ") + +;; Ignore version control folders in autocompletion +(setq eshell-cmpl-cycle-completions nil + eshell-save-history-on-exit t + eshell-cmpl-dir-ignore "\\`\\(\\.\\.?\\|CVS\\|\\.svn\\|\\.git\\)/\\'") + +;; Load some EShell extensions +(eval-after-load 'esh-opt + '(progn + (require 'em-term) + (require 'em-cmpl) + ;; More visual commands! + (add-to-list 'eshell-visual-commands "ssh") + (add-to-list 'eshell-visual-commands "tail") + (add-to-list 'eshell-visual-commands "sl"))) + +(setq eshell-directory-name "~/.config/eshell/") + +(provide 'eshell-setup) diff --git a/tools/emacs/init/functions.el b/tools/emacs/init/functions.el new file mode 100644 index 0000000000..8b96a0e737 --- /dev/null +++ b/tools/emacs/init/functions.el @@ -0,0 +1,266 @@ +(require 's) +;; A few handy functions I use in init.el (or not, but they're nice to +;; have) + +(defun custom-download-theme (url filename) + "Downloads a theme through HTTP and places it in ~/.emacs.d/themes" + + ;; Ensure the directory exists + (unless (file-exists-p "~/.emacs.d/themes") + (make-directory "~/.emacs.d/themes")) + + ;; Adds the themes folder to the theme load path (if not already + ;; there) + (unless (member "~/.emacs.d/themes" custom-theme-load-path) + (add-to-list 'custom-theme-load-path "~/.emacs.d/themes")) + + ;; Download file if it doesn't exist. + + (let ((file + (concat "~/.emacs.d/themes/" filename))) + (unless (file-exists-p file) + (url-copy-file url file)))) + +(defun custom-download-script (url filename) + "Downloads an Elisp script, places it in ~/.emacs/other and then loads it" + + ;; Ensure the directory exists + (unless (file-exists-p "~/.emacs.d/other") + (make-directory "~/.emacs.d/other")) + + ;; Download file if it doesn't exist. + (let ((file + (concat "~/.emacs.d/other/" filename))) + (unless (file-exists-p file) + (url-copy-file url file)) + + (load file))) + +(defun keychain-password (account &optional keychain) + "Returns the password for the account, by default it's looked up in the Login.keychain but a + different keychain can be specified." + (let ((k (if keychain keychain "Login.keychain"))) + (replace-regexp-in-string + "\n" "" + (shell-command-to-string (concat "security find-generic-password -w -a " + account + " " + k))))) + +;; This clones a git repository to 'foldername in .emacs.d +;; if there isn't already a folder with that name +(defun custom-clone-git (url foldername) + "Clones a git repository to .emacs.d/foldername" + (let ((fullpath (concat "~/.emacs.d/" foldername))) + (unless (file-exists-p fullpath) + (async-shell-command (concat "git clone " url " " fullpath))))) + +(defun load-file-if-exists (filename) + (if (file-exists-p filename) + (load filename))) + +(defun goto-line-with-feedback () + "Show line numbers temporarily, while prompting for the line number input" + (interactive) + (unwind-protect + (progn + (setq-local display-line-numbers t) + (let ((target (read-number "Goto line: "))) + (avy-push-mark) + (goto-line target))) + (setq-local display-line-numbers nil))) + + +(defun untabify-buffer () + (interactive) + (untabify (point-min) (point-max))) + +(defun indent-buffer () + (interactive) + (indent-region (point-min) (point-max))) + +(defun cleanup-buffer () + "Perform a bunch of operations on the whitespace content of a buffer. +Including indent-buffer, which should not be called automatically on save." + (interactive) + (untabify-buffer) + (delete-trailing-whitespace) + (indent-buffer)) + +;; These come from the emacs starter kit + +(defun esk-add-watchwords () + (font-lock-add-keywords + nil '(("\\<\\(FIX\\(ME\\)?\\|TODO\\|DEBUG\\|HACK\\|REFACTOR\\|NOCOMMIT\\)" + 1 font-lock-warning-face t)))) + +(defun esk-sudo-edit (&optional arg) + (interactive "p") + (if (or arg (not buffer-file-name)) + (find-file (concat "/sudo:root@localhost:" (read-file-name "File: "))) + (find-alternate-file (concat "/sudo:root@localhost:" buffer-file-name)))) + +;; Open Fefes blog +(defun fefes-blog () + (interactive) + (eww "https://blog.fefe.de/")) + +;; Open this machines NixOS config +(defun nix-config () + (interactive) + (find-file "/etc/nixos/configuration.nix")) + +;; Open the NixOS man page +(defun nixos-man () + (interactive) + (man "configuration.nix")) + +;; Open local emacs configuration +(defun emacs-config () + (interactive) + (dired "~/.emacs.d/")) + +;; Get the nix store path for a given derivation. +;; If the derivation has not been built before, this will trigger a build. +(defun nix-store-path (derivation) + (let ((expr (concat "with import {}; " derivation))) + (s-chomp (shell-command-to-string (concat "nix-build -E '" expr "'"))))) + +(defun insert-nix-store-path () + (interactive) + (let ((derivation (read-string "Derivation name (in ): "))) + (insert (nix-store-path derivation)))) + +(defun toggle-force-newline () + "Buffer-local toggle for enforcing final newline on save." + (interactive) + (setq-local require-final-newline (not require-final-newline)) + (message "require-final-newline in buffer %s is now %s" + (buffer-name) + require-final-newline)) + +;; Helm includes a command to run external applications, which does +;; not seem to exist in ivy. This implementation uses some of the +;; logic from Helm to provide similar functionality using ivy. +(defun list-external-commands () + "Creates a list of all external commands available on $PATH + while filtering NixOS wrappers." + (cl-loop + for dir in (split-string (getenv "PATH") path-separator) + when (and (file-exists-p dir) (file-accessible-directory-p dir)) + for lsdir = (cl-loop for i in (directory-files dir t) + for bn = (file-name-nondirectory i) + when (and (not (s-contains? "-wrapped" i)) + (not (member bn completions)) + (not (file-directory-p i)) + (file-executable-p i)) + collect bn) + append lsdir into completions + finally return (sort completions 'string-lessp))) + +(defun run-external-command (cmd) + "Execute the specified command and notify the user when it + finishes." + (message "Starting %s..." cmd) + (set-process-sentinel + (start-process-shell-command cmd nil cmd) + (lambda (process event) + (when (string= event "finished\n") + (message "%s process finished." process))))) + +(defun ivy-run-external-command () + "Prompts the user with a list of all installed applications and + lets them select one to launch." + + (interactive) + (let ((external-commands-list (list-external-commands))) + (ivy-read "Command:" external-commands-list + :require-match t + :history 'external-commands-history + :action #'run-external-command))) + +(defun ivy-password-store (&optional password-store-dir) + "Custom version of password-store integration with ivy that + actually uses the GPG agent correctly." + + (interactive) + (ivy-read "Copy password of entry: " + (password-store-list (or password-store-dir (password-store-dir))) + :require-match t + :keymap ivy-pass-map + :action (lambda (entry) + (let ((password (auth-source-pass-get 'secret entry))) + (password-store-clear) + (kill-new password) + (setq password-store-kill-ring-pointer kill-ring-yank-pointer) + (message "Copied %s to the kill ring. Will clear in %s seconds." + entry (password-store-timeout)) + (setq password-store-timeout-timer + (run-at-time (password-store-timeout) + nil 'password-store-clear)))))) + +(defun ivy-browse-repositories () + "Select a git repository and open its associated magit buffer." + + (interactive) + (ivy-read "Repository: " + (magit-list-repos) + :require-match t + :sort t + :action #'magit-status)) + +(defun warmup-gpg-agent (arg &optional exit) + "Function used to warm up the GPG agent before use. This is + useful in cases where there is no easy way to make pinentry run + in the correct context (such as when sending email)." + (interactive) + (message "Warming up GPG agent") + (epg-sign-string (epg-make-context) "dummy") + nil) + +(defun bottom-right-window-p () + "Determines whether the last (i.e. bottom-right) window of the + active frame is showing the buffer in which this function is + executed." + (let* ((frame (selected-frame)) + (right-windows (window-at-side-list frame 'right)) + (bottom-windows (window-at-side-list frame 'bottom)) + (last-window (car (seq-intersection right-windows bottom-windows)))) + (eq (current-buffer) (window-buffer last-window)))) + +(defun inferior-erlang-nix-shell () + "Start an inferior Erlang process from the root of the current + project." + (interactive) + (inferior-erlang + (format "nix-shell --command erl %s" (cdr (project-current))))) + +(defun intero-fix-ghci-panic () + "Disable deferring of out of scope variable errors, which + triggers a bug in the interactive Emacs REPL printing a panic + under certain conditions." + + (interactive) + (let* ((root (intero-project-root)) + (package-name (intero-package-name)) + (backend-buffer (intero-buffer 'backend)) + (name (format "*intero:%s:%s:repl*" + (file-name-nondirectory root) + package-name)) + (setting ":set -fno-defer-out-of-scope-variables\n")) + (when (get-buffer name) + (with-current-buffer (get-buffer name) + (goto-char (point-max)) + (let ((process (get-buffer-process (current-buffer)))) + (when process (process-send-string process setting))))))) + +;; Brute-force fix: Ensure the setting is injected every time the REPL +;; is selected. +;; +;; Upstream issue: https://github.com/commercialhaskell/intero/issues/569 +(advice-add 'intero-repl :after (lambda (&rest r) (intero-fix-ghci-panic)) + '((name . intero-panic-fix))) +(advice-add 'intero-repl-load :after (lambda (&rest r) (intero-fix-ghci-panic)) + '((name . intero-panic-fix))) + +(provide 'functions) diff --git a/tools/emacs/init/look-and-feel.el b/tools/emacs/init/look-and-feel.el new file mode 100644 index 0000000000..3d480bd5f4 --- /dev/null +++ b/tools/emacs/init/look-and-feel.el @@ -0,0 +1,115 @@ +;;; -*- lexical-binding: t; -*- + +;; Hide those ugly tool bars: +(tool-bar-mode 0) +(scroll-bar-mode 0) +(menu-bar-mode 0) +(add-hook 'after-make-frame-functions + (lambda (frame) (scroll-bar-mode 0))) + +;; Don't do any annoying things: +(setq ring-bell-function 'ignore) +(setq initial-scratch-message "") + +;; Remember layout changes +(winner-mode 1) + +;; Usually emacs will run as a proper GUI application, in which case a few +;; extra settings are nice-to-have: +(when window-system + (setq frame-title-format '(buffer-file-name "%f" ("%b"))) + (mouse-wheel-mode t) + (blink-cursor-mode -1)) + +;; Configure editor fonts +(let ((font (format "Input Mono-%d" 12))) + (setq default-frame-alist `((font-backend . "xft") + (font . ,font))) + (set-frame-font font t t)) + +;; Display battery in mode-line's misc section on adho: +(when (equal "adho" (system-name)) + (setq battery-mode-line-format " %b%p%%") + (display-battery-mode)) + +;; Configure telephone-line +(defun telephone-misc-if-last-window () + "Renders the mode-line-misc-info string for display in the + mode-line if the currently active window is the last one in the + frame. + + The idea is to not display information like the current time, + load, battery levels in all buffers." + + (when (bottom-right-window-p) + (telephone-line-raw mode-line-misc-info t))) + +(defun telephone-line-setup () + (telephone-line-defsegment telephone-line-last-window-segment () + (telephone-misc-if-last-window)) + + ;; Display the current EXWM workspace index in the mode-line + (telephone-line-defsegment telephone-line-exwm-workspace-index () + (when (bottom-right-window-p) + (format "[%s]" exwm-workspace-current-index))) + + ;; Define a highlight font for ~ important ~ information in the last + ;; window. + (defface special-highlight '((t (:foreground "white" :background "#5f627f"))) "") + (add-to-list 'telephone-line-faces + '(highlight . (special-highlight . special-highlight))) + + (setq telephone-line-lhs + '((nil . (telephone-line-position-segment)) + (accent . (telephone-line-buffer-segment)))) + + (setq telephone-line-rhs + '((accent . (telephone-line-major-mode-segment)) + (nil . (telephone-line-last-window-segment + telephone-line-exwm-workspace-index)) + (highlight . (telephone-line-notmuch-counts)))) + + (setq telephone-line-primary-left-separator 'telephone-line-tan-left + telephone-line-primary-right-separator 'telephone-line-tan-right + telephone-line-secondary-left-separator 'telephone-line-tan-hollow-left + telephone-line-secondary-right-separator 'telephone-line-tan-hollow-right) + + (telephone-line-mode 1)) + +;; Auto refresh buffers +(global-auto-revert-mode 1) + +;; Use clipboard properly +(setq select-enable-clipboard t) + +;; Show in-progress chords in minibuffer +(setq echo-keystrokes 0.1) + +;; Show column numbers in all buffers +(column-number-mode t) + +;; Highlight currently active line +(global-hl-line-mode t) + +(defalias 'yes-or-no-p 'y-or-n-p) +(defalias 'auto-tail-revert-mode 'tail-mode) + +;; Style line numbers (shown with M-g g) +(setq linum-format + (lambda (line) + (propertize + (format (concat " %" + (number-to-string + (length (number-to-string + (line-number-at-pos (point-max))))) + "d ") + line) + 'face 'linum))) + +;; Display tabs as 2 spaces +(setq tab-width 2) + +;; Don't wrap around when moving between buffers +(setq windmove-wrap-around nil) + +(provide 'look-and-feel) diff --git a/tools/emacs/init/mail-setup.el b/tools/emacs/init/mail-setup.el new file mode 100644 index 0000000000..1700ccddd3 --- /dev/null +++ b/tools/emacs/init/mail-setup.el @@ -0,0 +1,98 @@ +(require 'notmuch) +(require 'counsel-notmuch) + +(global-set-key (kbd "C-c m") 'notmuch-hello) +(global-set-key (kbd "C-c C-m") 'counsel-notmuch) +(global-set-key (kbd "C-c C-e n") 'notmuch-mua-new-mail) + +(setq notmuch-cache-dir (format "%s/.cache/notmuch" (getenv "HOME"))) +(make-directory notmuch-cache-dir t) + +;; Cache addresses for completion: +(setq notmuch-address-save-filename (concat notmuch-cache-dir "/addresses")) + +;; Don't spam my home folder with drafts: +(setq notmuch-draft-folder "drafts") ;; relative to notmuch database + +;; Mark things as read when archiving them: +(setq notmuch-archive-tags '("-inbox" "-unread" "+archive")) + +;; Show me saved searches that I care about: +(setq notmuch-saved-searches + '((:name "inbox" :query "tag:inbox" :count-query "tag:inbox AND tag:unread" :key "i") + (:name "aprila-dev" :query "tag:aprila-dev" :count-query "tag:aprila-dev AND tag:unread" :key "d") + (:name "gitlab" :query "tag:gitlab" :key "g") + (:name "sent" :query "tag:sent" :key "t") + (:name "drafts" :query "tag:draft"))) +(setq notmuch-show-empty-saved-searches t) + +;; Mail sending configuration +(setq send-mail-function 'sendmail-send-it) ;; sendmail provided by MSMTP +(setq notmuch-always-prompt-for-sender t) +(setq notmuch-mua-user-agent-function + (lambda () (format "Emacs %s; notmuch.el %s" emacs-version notmuch-emacs-version))) +(setq mail-host-address (system-name)) +(setq notmuch-mua-cite-function #'message-cite-original-without-signature) + +;; Close mail buffers after sending mail +(setq message-kill-buffer-on-exit t) + +;; Ensure sender is correctly passed to msmtp +(setq mail-specify-envelope-from t + message-sendmail-envelope-from 'header + mail-envelope-from 'header) + +;; Store sent mail in the correct folder per account +(setq notmuch-maildir-use-notmuch-insert nil) +(setq notmuch-fcc-dirs '(("mail@tazj.in" . "tazjin/Sent") + ;; Not a mistake, Office365 apparently + ;; renames IMAP folders (!) to your local + ;; language instead of providing translations + ;; in the UI m( + ("vincent@aprila.no" . "aprila/Sende element"))) + +;; I don't use drafts but I instinctively hit C-x C-s constantly, lets +;; handle that gracefully. +(define-key notmuch-message-mode-map (kbd "C-x C-s") #'ignore) + +;; MSMTP decrypts passwords using pass, but pinentry doesn't work +;; correctly in that setup. This forces a warmup of the GPG agent +;; before sending the message. +;; +;; Note that the sending function is advised because the provided hook +;; for this seems to run at the wrong time. +(advice-add 'notmuch-mua-send-common :before 'warmup-gpg-agent) + +;; Define a telephone-line segment for displaying the count of unread, +;; important mails in the last window's mode-line: +(defvar *last-notmuch-count-redraw* 0) +(defvar *current-notmuch-count* nil) + +(defun update-display-notmuch-counts () + "Update and render the current state of the notmuch unread + count for display in the mode-line. + + The offlineimap-timer runs every 2 minutes, so it does not make + sense to refresh this much more often than that." + + (when (> (- (float-time) *last-notmuch-count-redraw*) 30) + (setq *last-notmuch-count-redraw* (float-time)) + (let* ((inbox-unread (notmuch-saved-search-count "tag:inbox and tag:unread")) + (devel-unread (notmuch-saved-search-count "tag:aprila-dev and tag:unread")) + (notmuch-count (format "I: %s; D: %s" inbox-unread devel-unread))) + (setq *current-notmuch-count* notmuch-count))) + + (when (and (bottom-right-window-p) + ;; Only render if the initial update is done and there + ;; are unread mails: + *current-notmuch-count* + (not (equal *current-notmuch-count* "I: 0; D: 0"))) + *current-notmuch-count*)) + +(telephone-line-defsegment telephone-line-notmuch-counts () + "This segment displays the count of unread notmuch messages in + the last window's mode-line (if unread messages are present)." + + (update-display-notmuch-counts)) + +(provide 'mail-setup) diff --git a/tools/emacs/init/modes.el b/tools/emacs/init/modes.el new file mode 100644 index 0000000000..19ed2a6843 --- /dev/null +++ b/tools/emacs/init/modes.el @@ -0,0 +1,36 @@ +;; Initializes modes I use. + +(add-hook 'prog-mode-hook 'esk-add-watchwords) + +;; Use auto-complete as completion at point +(defun set-auto-complete-as-completion-at-point-function () + (setq completion-at-point-functions '(auto-complete))) + +(add-hook 'auto-complete-mode-hook + 'set-auto-complete-as-completion-at-point-function) + +;; Enable rainbow-delimiters for all things programming +(add-hook 'prog-mode-hook 'rainbow-delimiters-mode) + +;; Enable Paredit & Company in Emacs Lisp mode +(add-hook 'emacs-lisp-mode-hook 'company-mode) + +;; Always highlight matching brackets +(show-paren-mode 1) + +;; Always auto-close parantheses and other pairs +;; (replaced by smartparens) +;; (electric-pair-mode) + +;; Keep track of recent files +(recentf-mode) + +;; Easily navigate sillycased words +(global-subword-mode 1) + +;; Transparently open compressed files +(auto-compression-mode t) + +;; Show available key chord completions + +(provide 'modes) diff --git a/tools/emacs/init/nixos.el b/tools/emacs/init/nixos.el new file mode 100644 index 0000000000..e384e9b77d --- /dev/null +++ b/tools/emacs/init/nixos.el @@ -0,0 +1,103 @@ +;; Configure additional settings if this is one of my NixOS machines +;; (i.e. if ExWM is required) +;; -*- lexical-binding: t; -*- + +(require 's) +(require 'f) +(require 'dash) + +(defun pulseaudio-ctl (cmd) + (shell-command (concat "pulseaudio-ctl " cmd)) + (message "Volume command: %s" cmd)) + +(defun volume-mute () (interactive) (pulseaudio-ctl "mute")) +(defun volume-up () (interactive) (pulseaudio-ctl "up")) +(defun volume-down () (interactive) (pulseaudio-ctl "down")) + +(defun brightness-up () + (interactive) + (shell-command "exec light -A 10") + (message "Brightness increased")) + +(defun brightness-down () + (interactive) + (shell-command "exec light -U 10") + (message "Brightness decreased")) + +(defun lock-screen () + (interactive) + (shell-command "screen-lock")) + +(defun generate-randr-config () + (-flatten `(,(-map (lambda (n) (list n "DP2")) (number-sequence 1 7)) + (0 "eDP1") + ,(-map (lambda (n) (list n "eDP1")) (number-sequence 8 9))))) + +(use-package exwm + :hook ((exwm-update-class . (lambda () + ;; Make class name the buffer name + (exwm-workspace-rename-buffer exwm-class-name)))) + :init + (progn + (require 'exwm-config) + + (fringe-mode 3) + + (setq exwm-workspace-number 10) + + ;; 's-r': Reset + (exwm-input-set-key (kbd "s-r") #'exwm-reset) + ;; 's-w': Switch workspace + (exwm-input-set-key (kbd "s-w") #'exwm-workspace-switch) + ;; 's-N': Switch to certain workspace + (dotimes (i 10) + (exwm-input-set-key (kbd (format "s-%d" i)) + `(lambda () + (interactive) + (exwm-workspace-switch-create ,i)))) + + ;; Launch applications with completion (dmenu style!) + (exwm-input-set-key (kbd "s-d") #'ivy-run-external-command) + (exwm-input-set-key (kbd "s-p") #'ivy-password-store) + (exwm-input-set-key (kbd "C-s-p") '(lambda () + (interactive) + (ivy-password-store "~/.aprila-secrets"))) + + ;; Add Alacritty selector to a key + (exwm-input-set-key (kbd "C-x t") #'counsel-switch-to-alacritty) + + ;; Toggle between line-mode / char-mode + (exwm-input-set-key (kbd "C-c C-t C-t") #'exwm-input-toggle-keyboard) + + ;; Volume keys + (exwm-input-set-key (kbd "") #'volume-mute) + (exwm-input-set-key (kbd "") #'volume-up) + (exwm-input-set-key (kbd "") #'volume-down) + + ;; Brightness keys + (exwm-input-set-key (kbd "") #'brightness-down) + (exwm-input-set-key (kbd "") #'brightness-up) + (exwm-input-set-key (kbd "") #'lock-screen) + + ;; Line-editing shortcuts + (exwm-input-set-simulation-keys + '(([?\C-d] . delete) + ([?\C-w] . ?\C-c))) + + ;; Enable EXWM + (exwm-enable) + + ;; Show time in the mode line + (display-time-mode) + + ;; Configure xrandr when running on laptop + (when (equal (shell-command-to-string "hostname") "adho\n") + (require 'exwm-randr) + (setq exwm-randr-workspace-output-plist (generate-randr-config)) + (exwm-randr-enable)) + + ;; Let buffers move seamlessly between workspaces + (setq exwm-workspace-show-all-buffers t) + (setq exwm-layout-show-all-buffers t))) + +(provide 'nixos) diff --git a/tools/emacs/init/settings.el b/tools/emacs/init/settings.el new file mode 100644 index 0000000000..2e4dedc0a5 --- /dev/null +++ b/tools/emacs/init/settings.el @@ -0,0 +1,65 @@ +(require 'prescient) +(require 'ivy-prescient) +(require 'uniquify) +(require 'ivy-pass) + +;; Make ivy go! +(ivy-mode 1) +(counsel-mode 1) + +(setq ivy-use-virtual-buffers t) +(setq enable-recursive-minibuffers t) + +;; Enable support for prescient in ivy & configure it +(ivy-prescient-mode) +(prescient-persist-mode) +(add-to-list 'ivy-prescient-excluded-commands 'counsel-rg) + +;; Move files to trash when deleting +(setq delete-by-moving-to-trash t) + +;; We don't live in the 80s, but we're also not a shitty web app. +(setq gc-cons-threshold 20000000) + +(setq uniquify-buffer-name-style 'forward) + +; Fix some defaults +(setq visible-bell nil + inhibit-startup-message t + color-theme-is-global t + sentence-end-double-space nil + shift-select-mode nil + uniquify-buffer-name-style 'forward + whitespace-style '(face trailing lines-tail tabs) + whitespace-line-column 80 + default-directory "~" + fill-column 80 + ediff-split-window-function 'split-window-horizontally) + +(add-to-list 'safe-local-variable-values '(lexical-binding . t)) +(add-to-list 'safe-local-variable-values '(whitespace-line-column . 80)) + +(set-default 'indent-tabs-mode nil) + +;; UTF-8 please +(setq locale-coding-system 'utf-8) ; pretty +(set-terminal-coding-system 'utf-8) ; pretty +(set-keyboard-coding-system 'utf-8) ; pretty +(set-selection-coding-system 'utf-8) ; please +(prefer-coding-system 'utf-8) ; with sugar on top + +;; Make emacs behave sanely (overwrite selected text) +(delete-selection-mode 1) + +;; Keep your temporary files in tmp, emacs! +(setq auto-save-file-name-transforms + `((".*" ,temporary-file-directory t))) +(setq backup-directory-alist + `((".*" . ,temporary-file-directory))) + +(remove-hook 'kill-buffer-query-functions 'server-kill-buffer-query-function) + +;; Show time in 24h format +(setq display-time-24hr-format t) + +(provide 'settings) diff --git a/tools/emacs/init/term-setup.el b/tools/emacs/init/term-setup.el new file mode 100644 index 0000000000..a2a71be9ee --- /dev/null +++ b/tools/emacs/init/term-setup.el @@ -0,0 +1,37 @@ +;; Utilities for Alacritty buffers. + +(defun open-or-create-alacritty-buffer (buffer-name) + "Switch to the buffer with BUFFER-NAME or create a + new buffer running Alacritty." + (let ((buffer (get-buffer buffer-name))) + (if (not buffer) + (run-external-command "alacritty") + (switch-to-buffer buffer)))) + +(defun is-alacritty-buffer (buffer) + "Determine whether BUFFER runs Alacritty." + (and (equal 'exwm-mode (buffer-local-value 'major-mode buffer)) + (s-starts-with? "Alacritty" (buffer-name buffer)))) + +(defun counsel-switch-to-alacritty () + "Switch to a (multi-)term buffer or create one." + (interactive) + (let ((terms (-map #'buffer-name + (-filter #'is-alacritty-buffer (buffer-list))))) + (if terms + (ivy-read "Switch to Alacritty buffer: " + (cons "New terminal" terms) + :caller 'counsel-switch-to-alacritty + :require-match t + :action #'open-or-create-alacritty-buffer) + (run-external-command "alacritty")))) + +(defun alacritty-rename () + "Rename the current terminal buffer." + (interactive) + (let* ((buffer (get-buffer (buffer-name)))) + (if (is-alacritty-buffer buffer) + (rename-buffer (format "Alacritty<%s>" (read-string "New terminal name: "))) + (error "This function is only intended to rename Alacritty buffers.")))) + +(provide 'term-setup) -- cgit 1.4.1