summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--.gitignore2
-rw-r--r--.gitmodules4
-rw-r--r--init.org659
3 files changed, 665 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 5a1c0bf..86b24fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,4 +16,6 @@ ncmpcpp/*/error.log
# getmail oldmail file
oldmail-*
+/early-init.el
+/init.el
/var \ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
index 452a4ed..6a5b74d 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -14,6 +14,10 @@
path = lib/dash
url = git@github.com:magnars/dash.el.git
no-makeinfo = dash-template.texi
+ # dash creates a `dir' dash info file, which makes git think
+ # that the submodule is dirty. so, let's ignore the untracked
+ # files of dash's submodule
+ ignore = untracked
[submodule "diff-hl"]
path = lib/diff-hl
url = git@github.com:dgutov/diff-hl.git
diff --git a/init.org b/init.org
new file mode 100644
index 0000000..371ec1e
--- /dev/null
+++ b/init.org
@@ -0,0 +1,659 @@
+#+title: =aminb='s Literate Emacs Configuration
+#+author: Amin Bandali
+#+babel: :cache yes
+#+property: header-args :tangle yes
+
+* About
+:PROPERTIES:
+:CUSTOM_ID: about
+:END:
+
+This org file is my literate configuration for GNU Emacs, and is
+tangled to [[./init.el][init.el]]. Packages are installed and managed using [[https://github.com/emacscollective/borg][Borg]].
+
+** Installation
+
+I'd like to have a fully reproducible Emacs setup (part of the reason
+why I store my configuration in this repository) but unfortunately out
+of the box, that's not achievable with =package.el=, not currently
+anyway. So, I've opted to use Borg. For what it's worth, I briefly
+experimented with [[https://github.com/raxod502/straight.el][straight.el]], but found that it added about 2 seconds
+to my init time; which is unacceptable for me: I use Emacs as my
+window manager (via EXWM) and coming from bspwm, I'm too used to
+having fast startup times.
+
+* Contents :toc_1:noexport:
+
+- [[#about][About]]
+- [[#header][Header]]
+- [[#initial-setup][Initial setup]]
+- [[#core][Core]]
+- [[#post-init][Post initialization]]
+- [[#footer][Footer]]
+
+* Header
+:PROPERTIES:
+:CUSTOM_ID: header
+:END:
+
+** First line
+
+#+begin_src emacs-lisp :comments none
+;;; init.el --- Amin Bandali's Emacs config -*- lexical-binding: t ; eval: (view-mode 1)-*-
+#+end_src
+
+Enable =view-mode=, which both makes the file read-only (as a reminder
+that =init.el= is an auto-generated file, not supposed to be edited),
+and provides some convenient key bindings for browsing through the
+file.
+
+** License
+
+#+begin_src emacs-lisp :comments none
+;; Copyright (C) 2018 Amin Bandali <amin@aminb.org>
+
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+#+end_src
+
+** Commentary
+
+#+begin_src emacs-lisp :comments none
+;;; Commentary:
+
+;; Emacs configuration of Amin Bandali, computer scientist and functional
+;; programmer.
+
+;; THIS FILE IS AUTO-GENERATED FROM `init.org'.
+#+end_src
+
+** Naming conventions
+
+The conventions below were inspired by [[https://github.com/hlissner/doom-emacs][Doom]]'s conventions, found
+[[https://github.com/hlissner/doom-emacs/blob/5dacbb7cb1c6ac246a9ccd15e6c4290def67757c/core/core.el#L3-L17][here]]. Naturally, I use my initials, =ab=, instead of =doom=.
+
+#+begin_src emacs-lisp :comments none
+;; Naming conventions:
+;;
+;; ab-... public variables or non-interactive functions
+;; ab--... private anything (non-interactive), not safe for direct use
+;; ab/... an interactive function; safe for M-x or keybinding
+;; ab:... an evil operator, motion, or command
+;; ab|... a hook function
+;; ab*... an advising function
+;; ab@... a hydra command
+;; ...! a macro
+#+end_src
+
+* Initial setup
+:PROPERTIES:
+:CUSTOM_ID: initial-setup
+:END:
+
+#+begin_src emacs-lisp :comments none
+;;; Code:
+#+end_src
+
+** Emacs initialization
+
+I'd like to do a couple of measurements of Emacs' startup time. First,
+let's see how long Emacs takes to start up, before even loading
+=init.el=, i.e. =user-init-file=:
+
+#+begin_src emacs-lisp
+(defvar ab--before-user-init-time (current-time)
+ "Value of `current-time' when Emacs begins loading `user-init-file'.")
+(message "Loading Emacs...done (%.3fs)"
+ (float-time (time-subtract ab--before-user-init-time
+ before-init-time)))
+#+end_src
+
+Also, temporarily increase ~gc-cons-threshhold~ and
+~gc-cons-percentage~ during startup to reduce garbage collection
+frequency. Clearing the ~file-name-handler-alist~ seems to help reduce
+startup time as well.
+
+#+begin_src emacs-lisp
+(defvar ab--gc-cons-threshold gc-cons-threshold)
+(defvar ab--gc-cons-percentage gc-cons-percentage)
+(defvar ab--file-name-handler-alist file-name-handler-alist)
+(setq gc-cons-threshold (* 400 1024 1024) ; 400 MiB
+ gc-cons-percentage 0.6
+ file-name-handler-alist nil
+ ;; sidesteps a bug when profiling with esup
+ esup-child-profile-require-level 0)
+#+end_src
+
+Of course, we'd like to set them back to their defaults once we're
+done initializing.
+
+#+begin_src emacs-lisp
+(add-hook
+ 'after-init-hook
+ (lambda ()
+ (let ((elapsed (float-time (time-subtract (current-time)
+ ab--before-user-init-time))))
+ (message "Loading %s...done (%.3fs) [after-init]"
+ user-init-file elapsed))
+ (setq gc-cons-threshold ab--gc-cons-threshold
+ gc-cons-percentage ab--gc-cons-percentage
+ file-name-handler-alist ab--file-name-handler-alist)))
+#+end_src
+
+Increase the number of lines kept in message logs (the =*Messages*=
+buffer).
+
+#+begin_src emacs-lisp
+(setq message-log-max 20000)
+#+end_src
+
+Optionally, we could suppress some byte compiler warnings like below,
+but for now I've decided to keep them enabled. See documentation for
+~byte-compile-warnings~ for more details.
+
+#+begin_src emacs-lisp
+;; (setq byte-compile-warnings
+;; '(not free-vars unresolved noruntime lexical make-local))
+#+end_src
+
+** Package management
+
+*** No =package.el=
+
+I can do all my package management things with Borg, and don't need
+Emacs' built-in =package.el=. Emacs 27 lets us disable =package.el= in
+the =early-init-file= (see [[https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=24acb31c04b4048b85311d794e600ecd7ce60d3b][here]]).
+
+#+begin_src emacs-lisp :tangle early-init.el
+(setq package-enable-at-startup nil)
+#+end_src
+
+But since Emacs 27 isn't out yet (Emacs 26 is just around the corner
+right now), and even when released it'll be long before most distros
+ship in their repos, I'll still put the old workaround with the
+commented call to ~package-initialize~ here anyway.
+
+#+begin_src emacs-lisp
+(setq package-enable-at-startup nil)
+;; (package-initialize)
+#+end_src
+
+*** Borg
+
+#+begin_quote
+Assimilate Emacs packages as Git submodules
+#+end_quote
+
+[[https://github.com/emacscollective/borg][Borg]] is at the heart of package management of my Emacs setup. In
+short, it creates a git submodule in =lib/= for each package, which
+can then be managed with the help of Magit or other tools.
+
+#+begin_src emacs-lisp
+(setq user-init-file (or load-file-name buffer-file-name)
+ user-emacs-directory (file-name-directory user-init-file))
+(add-to-list 'load-path
+ (expand-file-name "lib/borg" user-emacs-directory))
+(require 'borg)
+(borg-initialize)
+#+end_src
+
+*** =use-package=
+
+#+begin_quote
+A use-package declaration for simplifying your .emacs
+#+end_quote
+
+[[https://github.com/jwiegley/use-package][use-package]] is an awesome utility for managing and configuring
+packages (in our case especially the latter) in a neatly organized way
+and without compromising on performance.
+
+#+begin_src emacs-lisp
+(require 'use-package)
+(if nil ; set to t when need to debug init
+ (setq use-package-verbose t
+ use-package-expand-minimally nil
+ use-package-compute-statistics t
+ debug-on-error t)
+ (setq use-package-verbose nil
+ use-package-expand-minimally t))
+#+end_src
+
+*** Epkg
+
+#+begin_quote
+Browse the Emacsmirror package database
+#+end_quote
+
+Epkg provides access to a local copy of the [[https://emacsmirror.net][Emacsmirror]] package
+database, low-level functions for querying the database, and a
+=package.el=-like user interface for browsing the available packages.
+
+#+begin_src emacs-lisp
+(use-package epkg
+ :defer t)
+#+end_src
+
+** No littering in =~/.emacs.d=
+
+#+begin_quote
+Help keeping ~/.emacs.d clean
+#+end_quote
+
+By default, even for Emacs' built-in packages, the configuration files
+and persistent data are all over the place. Use =no-littering= to help
+contain the mess.
+
+#+begin_src emacs-lisp
+(use-package no-littering
+ :demand t
+ :config
+ (savehist-mode 1)
+ (add-to-list 'savehist-additional-variables 'kill-ring)
+ (save-place-mode 1)
+ (setq auto-save-file-name-transforms
+ `((".*" ,(no-littering-expand-var-file-name "auto-save/") t))))
+#+end_src
+
+
+** Custom file (=custom.el=)
+
+I'm not planning on using the custom file much, but even so, I
+definitely don't want it mixing with =init.el=. So, here; let's give
+it it's own file. While at it, treat themes as safe.
+
+#+begin_src emacs-lisp
+(use-package custom
+ :no-require t
+ :config
+ (setq custom-file (no-littering-expand-etc-file-name "custom.el"))
+ (when (file-exists-p custom-file)
+ (load custom-file))
+ (setf custom-safe-themes t))
+#+end_src
+
+** Better =$PATH= handling
+
+Let's use [[https://github.com/purcell/exec-path-from-shell][exec-path-from-shell]] to make Emacs use the =$PATH= as set up
+in my shell.
+
+#+begin_src emacs-lisp
+;; (use-package exec-path-from-shell
+;; :defer 1
+;; :init
+;; (setq exec-path-from-shell-check-startup-files nil)
+;; :config
+;; (exec-path-from-shell-initialize)
+;; ;; while we're at it, let's fix access to our running ssh-agent
+;; (exec-path-from-shell-copy-env "SSH_AGENT_PID")
+;; (exec-path-from-shell-copy-env "SSH_AUTH_SOCK"))
+#+end_src
+
+** Server
+
+Start server if not already running. Alternatively, can be done by
+issuing =emacs --daemon= in the terminal, which can be automated with
+a systemd service or using =brew services start emacs= on macOS. I use
+Emacs as my window manager (via EXWM), so I always start Emacs on
+login; so starting the server from inside Emacs is good enough for me.
+
+See [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Server.html#Emacs-Server][Using Emacs as a Server]].
+
+#+begin_src emacs-lisp
+(use-package server
+ :config (or (server-running-p) (server-mode)))
+#+end_src
+
+* Core
+:PROPERTIES:
+:CUSTOM_ID: core
+:END:
+
+** Defaults
+
+*** Smaller fringe
+
+Set fringe to a small value so we don't have big borders in EXWM, but
+can still see the =diff-hl= colors in the fringe.
+
+#+begin_src emacs-lisp
+(fringe-mode '(3 . 1))
+#+end_src
+
+*** Disable disabled commands
+
+Emacs disables some commands by default that could persumably be
+confusing for novice users. Let's disable that.
+
+#+begin_src emacs-lisp
+(setq disabled-command-function nil)
+#+end_src
+
+*** Kill-ring
+
+Save what I copy into clipboard from other applications into Emacs'
+kill-ring, which would allow me to still be able to easily access it
+in case I kill (cut or copy) something else inside Emacs before
+yanking (pasting) what I'd originally intended to.
+
+#+begin_src emacs-lisp
+(setq save-interprogram-paste-before-kill t)
+#+end_src
+
+*** Minibuffer
+
+#+begin_src emacs-lisp
+(setq enable-recursive-minibuffers t
+ resize-mini-windows t)
+#+end_src
+
+*** Lazy-person-friendly yes/no prompts
+
+Lazy people would prefer to type fewer keystrokes, especially for yes
+or no questions. I'm lazy.
+
+#+begin_src emacs-lisp
+(defalias 'yes-or-no-p #'y-or-n-p)
+#+end_src
+
+*** Startup screen and =*scratch*=
+
+Firstly, let Emacs know that I'd like to have =*scratch*= as my
+startup buffer.
+
+#+begin_src emacs-lisp
+(setq initial-buffer-choice t)
+#+end_src
+
+Now let's customize the =*scratch*= buffer a bit. First off, I don't
+need the default hint.
+
+#+begin_src emacs-lisp
+(setq initial-scratch-message nil)
+#+end_src
+
+Also, let's use Text mode as the major mode, in case I want to
+customize it (=*scratch*='s default major mode, Fundamental mode,
+can't really be customized).
+
+#+begin_src emacs-lisp
+(setq initial-major-mode 'text-mode)
+#+end_src
+
+Inhibit the buffer list when more than 2 files are loaded.
+
+#+begin_src emacs-lisp
+(setq inhibit-startup-buffer-menu t)
+#+end_src
+
+I don't really need to see the startup screen or echo area message
+either.
+
+#+begin_src emacs-lisp
+(advice-add #'display-startup-echo-area-message :override #'ignore)
+(setq inhibit-startup-screen t
+ inhibit-startup-echo-area-message user-login-name)
+#+end_src
+
+*** More useful frame titles
+
+Show either the file name or the buffer name (in case the buffer isn't
+visiting a file). Borrowed from Emacs Prelude.
+
+#+begin_src emacs-lisp
+(setq frame-title-format
+ '("" invocation-name " - "
+ (:eval (if (buffer-file-name)
+ (abbreviate-file-name (buffer-file-name))
+ "%b"))))
+#+end_src
+
+*** Backups
+
+Emacs' default backup settings aren't that great. Let's use more
+sensible options. See documentation for the ~make-backup-file~
+variable.
+
+#+begin_src emacs-lisp
+(setq backup-by-copying t
+ version-control t)
+#+end_src
+
+** Packages
+
+The packages in this section are absolutely essential to my everyday
+workflow, and they play key roles in how I do my computing. They
+immensely enhance the Emacs experience for me; both using Emacs, and
+customizing it.
+
+*** [[https://github.com/emacscollective/auto-compile][auto-compile]]
+
+#+begin_src emacs-lisp
+(use-package auto-compile
+ :demand t
+ :config
+ (auto-compile-on-load-mode)
+ (auto-compile-on-save-mode)
+ (setq auto-compile-display-buffer nil
+ auto-compile-mode-line-counter t
+ auto-compile-source-recreate-deletes-dest t
+ auto-compile-toggle-deletes-nonlib-dest t
+ auto-compile-update-autoloads t)
+ (add-hook 'auto-compile-inhibit-compile-hook
+ 'auto-compile-inhibit-compile-detached-git-head))
+#+end_src
+
+*** TODO [[https://github.com/Kungsgeten/ryo-modal][ryo-modal]]
+
+#+begin_quote
+Roll your own modal mode
+#+end_quote
+
+*** [[https://github.com/ch11ng/exwm][EXWM]] (window manager)
+
+#+begin_src emacs-lisp
+;; (use-package exwm
+;; :config
+;; (require 'exwm-config)
+;; (exwm-config-default)
+;; (require 'exwm-systemtray)
+;; (exwm-systemtray-enable)
+;; (require 'exwm-randr)
+;; (exwm-randr-enable))
+#+end_src
+
+*** [[https://orgmode.org/][Org mode]]
+
+#+begin_quote
+Org mode is for keeping notes, maintaining TODO lists, planning
+projects, and authoring documents with a fast and effective plain-text
+system.
+#+end_quote
+
+In short, my favourite way of life.
+
+#+begin_src emacs-lisp
+(setq org-src-tab-acts-natively t
+ org-src-preserve-indentation nil
+ org-edit-src-content-indentation 0)
+#+end_src
+
+*** [[https://magit.vc/][Magit]]
+
+#+begin_quote
+It's Magit! A Git porcelain inside Emacs.
+#+end_quote
+
+Not just how I do git, but /the/ way to do git.
+
+#+begin_src emacs-lisp
+(use-package magit
+ :defer t
+ :bind (("s-g" . magit-status)
+ ("C-x g" . magit-status)
+ ("C-x M-g" . magit-dispatch-popup))
+ :config
+ (magit-add-section-hook 'magit-status-sections-hook
+ 'magit-insert-modules
+ 'magit-insert-stashes
+ 'append))
+#+end_src
+
+*** [[https://github.com/abo-abo/swiper][Ivy]] (and friends)
+
+#+begin_quote
+Ivy - a generic completion frontend for Emacs, Swiper - isearch with
+an overview, and more. Oh, man!
+#+end_quote
+
+There's no way I could top that, so I won't attempt to.
+
+**** Ivy
+
+#+begin_src emacs-lisp
+;; (use-package ivy
+;; :bind
+;; (:map ivy-minibuffer-map
+;; ([escape] . keyboard-escape-quit)
+;; ("C-j" . ivy-next-line)
+;; ("C-k" . ivy-previous-line)
+;; ([S-up] . ivy-previous-history-element)
+;; ([S-down] . ivy-next-history-element)
+;; ("DEL" . ivy-backward-delete-char))
+;; :config
+;; (ivy-mode 1))
+#+end_src
+
+**** Swiper
+
+#+begin_src emacs-lisp
+;; (use-package swiper
+;; :bind (([remap isearch-forward] . swiper)
+;; ([remap isearch-backward] . swiper)))
+#+end_src
+
+**** Counsel
+
+#+begin_src emacs-lisp
+;; (use-package counsel
+;; :bind (([remap execute-extended-command] . counsel-M-x)
+;; ([remap find-file] . counsel-find-file)
+;; ("s-r" . counsel-recentf)
+;; :map minibuffer-local-map
+;; ("C-r" . counsel-minibuffer-history))
+;; :config
+;; (counsel-mode 1)
+;; (defalias 'locate #'counsel-locate))
+#+end_src
+
+* Borg's =layer/essentials=
+
+TODO: break this giant source block down into individual org sections.
+
+#+begin_src emacs-lisp
+(use-package dash
+ :config (dash-enable-font-lock))
+
+(use-package diff-hl
+ :config
+ (setq diff-hl-draw-borders nil)
+ (global-diff-hl-mode)
+ (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh t))
+
+(use-package dired
+ :defer t
+ :config (setq dired-listing-switches "-alh"))
+
+(use-package eldoc
+ :when (version< "25" emacs-version)
+ :config (global-eldoc-mode))
+
+(use-package help
+ :defer t
+ :config (temp-buffer-resize-mode))
+
+(progn ; `isearch'
+ (setq isearch-allow-scroll t))
+
+(use-package lisp-mode
+ :config
+ (add-hook 'emacs-lisp-mode-hook 'outline-minor-mode)
+ (add-hook 'emacs-lisp-mode-hook 'reveal-mode)
+ (defun indent-spaces-mode ()
+ (setq indent-tabs-mode nil))
+ (add-hook 'lisp-interaction-mode-hook #'indent-spaces-mode))
+
+(use-package man
+ :defer t
+ :config (setq Man-width 80))
+
+(use-package paren
+ :config (show-paren-mode))
+
+(use-package prog-mode
+ :config (global-prettify-symbols-mode)
+ (defun indicate-buffer-boundaries-left ()
+ (setq indicate-buffer-boundaries 'left))
+ (add-hook 'prog-mode-hook #'indicate-buffer-boundaries-left))
+
+(use-package recentf
+ :demand t
+ :config (add-to-list 'recentf-exclude "^/\\(?:ssh\\|su\\|sudo\\)?:"))
+
+(use-package savehist
+ :config (savehist-mode))
+
+(use-package saveplace
+ :when (version< "25" emacs-version)
+ :config (save-place-mode))
+
+(use-package simple
+ :config (column-number-mode))
+
+(progn ; `text-mode'
+ (add-hook 'text-mode-hook #'indicate-buffer-boundaries-left))
+
+(use-package tramp
+ :defer t
+ :config
+ (add-to-list 'tramp-default-proxies-alist '(nil "\\`root\\'" "/ssh:%h:"))
+ (add-to-list 'tramp-default-proxies-alist '("localhost" nil nil))
+ (add-to-list 'tramp-default-proxies-alist
+ (list (regexp-quote (system-name)) nil nil)))
+
+(use-package undo-tree
+ :config
+ (global-undo-tree-mode)
+ (setq undo-tree-mode-lighter ""))
+#+end_src
+
+* Post initialization
+:PROPERTIES:
+:CUSTOM_ID: post-init
+:END:
+
+Display how long it took to load the init file.
+
+#+begin_src emacs-lisp
+(message "Loading %s...done (%.3fs)" user-init-file
+ (float-time (time-subtract (current-time)
+ ab--before-user-init-time)))
+#+end_src
+
+* Footer
+:PROPERTIES:
+:CUSTOM_ID: footer
+:END:
+
+#+begin_src emacs-lisp :comments none
+;;; init.el ends here
+#+end_src