summaryrefslogtreecommitdiff
path: root/init.org
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--init.org2069
1 files changed, 2069 insertions, 0 deletions
diff --git a/init.org b/init.org
new file mode 100644
index 0000000..5479713
--- /dev/null
+++ b/init.org
@@ -0,0 +1,2069 @@
+#+TITLE: my Emacs configuration
+#+AUTHOR: Franck Cuny
+#+PROPERTY: header-args :tangle-mode o444 :results silent :tangle ~/.config/emacs/init.el
+#+STARTUP: overview indent
+#+AUTO_TANGLE: t
+
+To use this file, run =org-tangle= (or =C-c C-v C-t=).
+
+To exclude specific source blocks from being tangled add =:tangle no= to the header.
+
+* Startup
+** early initialization
+:PROPERTIES:
+:header-args: :tangle-mode o444 :results silent :tangle ~/.config/emacs/early-init.el
+:END:
+
+Using an =early-init.el= file can speedup loading emacs. This is only available after emacs version 27.
+
+#+begin_src emacs-lisp :lexical t
+;;; early-init.el --- Early initialization -*- lexical-binding: t -*-
+
+;;; Commentary:
+
+;;; Code:
+
+;; disable GUI elements
+(scroll-bar-mode -1) ; hide the scroll bar
+(tool-bar-mode -1) ; hide the tool bar
+(menu-bar-mode -1) ; hide the menu
+(blink-cursor-mode -1) ; don't blink the cursor
+
+(setq make-pointer-invisible t) ;; hide cursor while typing
+(setq use-dialog-box nil) ;; do not show GUI dialogs
+(setq use-file-dialog nil)
+(setq inhibit-startup-screen t) ;; hide the startup screen
+
+;; don't report warnings and errors related to native compilation
+(setq native-comp-async-report-warnings-errors nil)
+
+;; increase font size
+(set-face-attribute 'default nil :height 130)
+
+;;; early-init.el ends here
+#+end_src
+
+** set headers
+
+#+begin_src emacs-lisp :epilogue (format-time-string ";; Last generated on %c")
+;;; init.el --- This is where all emacs start. -*- lexical-binding: t -*-
+
+;;; Commentary:
+
+;;; Code:
+#+end_src
+
+** garbage collection
+Set the garbage collection threshold.
+
+#+begin_src emacs-lisp
+(setq gc-cons-percentage 0.5
+ gc-cons-threshold (* 128 1024 1024))
+#+end_src
+
+** report time
+
+While I don't restart emacs that often, it's still useful to know how much time was spend loading the modules.
+
+#+begin_src emacs-lisp
+(defconst emacs-start-time (current-time))
+
+(defun report-time-since-load (&optional suffix)
+ (message "Loading init...done (%.3fs)%s"
+ (float-time (time-subtract (current-time) emacs-start-time))
+ suffix))
+
+(add-hook 'after-init-hook
+ #'(lambda () (report-time-since-load " [after-init]"))
+ t)
+#+end_src
+
+** generic configuration
+
+#+begin_src emacs-lisp
+(fset 'yes-or-no-p 'y-or-n-p) ; replace yes/no prompts with y/n
+
+;; set utf-8 as the default encoding
+(prefer-coding-system 'utf-8-unix)
+(setq locale-coding-system 'utf-8)
+(set-language-environment 'utf-8)
+(set-terminal-coding-system 'utf-8)
+(set-keyboard-coding-system 'utf-8)
+#+end_src
+
+** load =use-package=
+
+Add MELPA and NonGNU ELPA repositories.
+
+#+begin_src emacs-lisp
+(setq load-prefer-newer t)
+(setq init-file-debug t)
+
+(package-initialize)
+
+(setq package-archives (append
+ package-archives
+ '(("melpa" . "https://melpa.org/packages/")
+ ("elpa" . "https://elpa.nongnu.org/nongnu/"))))
+#+end_src
+
+Then we load =use-package=.
+
+#+begin_src emacs-lisp
+(eval-and-compile
+ (defsubst emacs-path (path)
+ (expand-file-name path user-emacs-directory))
+
+ (setq package-enable-at-startup nil
+ load-path
+ (append (list (emacs-path "use-package"))
+ (delete-dups load-path)
+ (list (emacs-path "lisp")))))
+
+(require 'use-package)
+
+(setq use-package-verbose init-file-debug
+ use-package-expand-minimally (not init-file-debug)
+ use-package-compute-statistics nil
+ debug-on-error init-file-debug)
+#+end_src
+
+*** diminish
+
+Since =use-package= no longer requires =diminish= as a dependency (see [[https://github.com/jwiegley/use-package/blob/a6e856418d2ebd053b34e0ab2fda328abeba731c/NEWS.md?plain=1#LL103C3-L103C62][changelog]]), we need to require it before other packages.
+
+#+begin_src emacs-lisp
+(use-package diminish :ensure t)
+#+end_src
+
+** define data environment
+
+#+begin_src emacs-lisp
+(defconst user-data-directory
+ (emacs-path "data"))
+
+(defun user-data (dir)
+ (expand-file-name dir user-data-directory))
+
+(setq custom-file (user-data "customizations.el"))
+(load custom-file 'noerror)
+#+end_src
+
+* Helper functions
+
+** Push and pop window configurations
+
+#+begin_src emacs-lisp
+(defvar saved-window-configuration nil)
+
+(defun push-window-configuration ()
+ (interactive)
+ (push (current-window-configuration) saved-window-configuration))
+
+(defun pop-window-configuration ()
+ (interactive)
+ (let ((config (pop saved-window-configuration)))
+ (if config
+ (set-window-configuration config)
+ (if (> (length (window-list)) 1)
+ (delete-window)
+ (bury-buffer)))))
+#+end_src
+
+** Rename the current buffer
+
+#+begin_src emacs-lisp
+(defun my/rename-this-buffer-and-file ()
+ "Renames current buffer and file it is visiting."
+ (interactive)
+ (let ((name (buffer-name))
+ (filename (buffer-file-name))
+ (read-file-name-function 'read-file-name-default))
+ (if (not (and filename (file-exists-p filename)))
+ (error "Buffer '%s' is not visiting a file!" name)
+ (let ((new-name (read-file-name "New name: " filename)))
+ (cond ((get-buffer new-name)
+ (error "A buffer named '%s' already exists!" new-name))
+ (t
+ (rename-file filename new-name 1)
+ (rename-buffer new-name)
+ (set-visited-file-name new-name)
+ (set-buffer-modified-p nil)
+ (message "File '%s' successfully renamed to '%s'" name (file-name-nondirectory new-name))))))))
+#+end_src
+
+** Is this my work issued machine
+
+#+begin_src emacs-lisp
+(defun my/check-work-machine-p ()
+ "Return t if this is a work machine."
+ (string-match "HQ\\.*" (system-name)))
+#+end_src
+
+** GitHub's code search
+
+#+begin_src emacs-lisp
+(defun my/github-code-search ()
+ "Search code on github for a given language."
+ (interactive)
+ (let ((language (completing-read
+ "Language: "
+ '("Emacs Lisp" "Python" "Go" "Nix")))
+ (code (read-string "Code: ")))
+ (browse-url
+ (concat "https://github.com/search?lang=" language
+ "&type=code&q=" code))))
+#+end_src
+
+** Search work's repositories
+
+#+begin_src emacs-lisp
+(defun my/work-code-search ()
+ "Search code on sourcegraph for a given language."
+ (interactive)
+ (let ((language (completing-read
+ "Language: "
+ '("Ruby" "Python" "Go")))
+ (code (read-string "Code: ")))
+ (browse-url
+ (concat "https://sourcegraph.rbx.com/search?q=context:global+lang:" language
+ "+" code))))
+#+end_src
+
+* Packages
+** abbrev
+
+If you want 'btw' to expand to 'by the way', type 'btw' followed by =C-x a i g=. The =g= is flow global, and you can define abbreviations per mode.
+
+| keys | action |
+|-----------+--------------------------------------------------|
+| =C-x a i g= | add a new abbreviation for all modes |
+| =C-x a i l= | add a new abbreviation local to the current mode |
+
+#+begin_src emacs-lisp
+(use-package abbrev
+ :diminish
+ :hook
+ ((text-mode prog-mode) . abbrev-mode)
+ :custom
+ (abbrev-file-name (emacs-path "abbrevs.el"))
+ ;; save abbrevs when files are saved
+ (save-abbrevs 'silently)
+ :config
+ (if (file-exists-p abbrev-file-name)
+ (quietly-read-abbrev-file)))
+#+end_src
+
+** autorevert
+
+Automatically revert buffers if the file has changed on disk.
+
+#+begin_src emacs-lisp
+(use-package autorevert
+ :custom
+ (auto-revert-use-notify nil)
+ :config
+ (global-auto-revert-mode t))
+#+end_src
+
+** bookmark
+
+| keys | action |
+|---------+------------------------------------|
+| =C-x r m= | add a new bookmark |
+| =C-x r l= | list all the bookmarks in a buffer |
+| =C-x r b= | list the bookmarks with consul |
+
+#+begin_src emacs-lisp
+(use-package bookmark
+ :defer t
+ :custom
+ (bookmark-default-file (user-data "bookmarks")))
+#+end_src
+
+** compile
+
+#+begin_src emacs-lisp
+(use-package compile
+ :bind (:map compilation-mode-map
+ ("z" . delete-window))
+ :custom
+ (compilation-always-kill t)
+ ;; Don't freeze when process reads from stdin
+ (compilation-disable-input t)
+ (compilation-ask-about-save nil)
+ (compilation-context-lines 10)
+ (compilation-scroll-output 'first-error)
+ (compilation-skip-threshold 2)
+ (compilation-window-height 100))
+#+end_src
+
+** completions
+*** consult
+
+#+begin_src emacs-lisp
+(use-package consult
+ :ensure t
+ :bind (("C-c m" . consult-mode-command)
+ ("M-g o" . consult-org-heading)
+ ("C-x b" . consult-buffer)
+ ("C-x 5 b" . consult-buffer-other-frame)
+ ("C-x r b" . consult-bookmark)
+ ("C-x p b" . consult-project-buffer)
+ ("C-c i" . consult-imenu)
+ ("M-g e" . consult-compile-error)
+ ("M-g g" . consult-goto-line)
+ ("M-g M-g" . consult-goto-line)
+ ("M-g l" . consult-goto-line)
+ ("M-g m" . consult-mark)
+ ("M-g k" . consult-global-mark))
+
+ ;; Enable automatic preview at point in the *Completions* buffer. This is
+ ;; relevant when you use the default completion UI.
+ :hook (completion-list-mode . consult-preview-at-point-mode)
+
+ :custom
+ (consult-narrow-key "<")
+
+ :functions
+ (consult-register-format
+ consult-register-window
+ consult-xref)
+
+ :init
+ ;; Optionally configure the register formatting. This improves the register
+ ;; preview for `consult-register', `consult-register-load',
+ ;; `consult-register-store' and the Emacs built-ins.
+ (setq register-preview-delay 0.5
+ register-preview-function #'consult-register-format)
+
+ ;; Optionally tweak the register preview window.
+ ;; This adds thin lines, sorting and hides the mode line of the window.
+ (advice-add #'register-preview :override #'consult-register-window)
+
+ ;; Use Consult to select xref locations with preview
+ (setq xref-show-xrefs-function #'consult-xref
+ xref-show-definitions-function #'consult-xref)
+
+ ;; Configure other variables and modes in the :config section,
+ ;; after lazily loading the package.
+ :config
+ (use-package consult-xref)
+
+ (consult-customize
+ consult-theme
+ :preview-key '(:debounce 0.2 any)
+ consult-ripgrep
+ consult-git-grep
+ consult-grep
+ consult-bookmark
+ consult-recent-file
+ consult-xref
+ consult--source-bookmark
+ consult--source-file-register
+ consult--source-recent-file
+ consult--source-project-recent-file
+ :preview-key '(:debounce 0.4 any)))
+#+end_src
+
+*** corfu
+
+#+begin_src emacs-lisp
+(use-package corfu
+ :ensure t
+ :demand t
+ :bind (("M-/" . completion-at-point)
+ :map corfu-map
+ ("C-n" . corfu-next)
+ ("C-p" . corfu-previous)
+ ("<escape>" . corfu-quit)
+ ("<return>" . corfu-insert)
+ ("M-d" . corfu-info-documentation)
+ ("M-l" . corfu-info-location)
+ ("M-." . corfu-move-to-minibuffer))
+ :custom
+ ;; Works with `indent-for-tab-command'. Make sure tab doesn't indent when you
+ ;; want to perform completion
+ (tab-always-indent 'complete)
+ (completion-cycle-threshold t) ; Always show candidates in menu
+
+ (corfu-auto t)
+ (corfu-auto-prefix 2)
+ (corfu-auto-delay 0.25)
+
+ (corfu-min-width 80)
+ (corfu-max-width corfu-min-width) ; Always have the same width
+ (corfu-count 14)
+ (corfu-scroll-margin 4)
+ (corfu-cycle t)
+
+ ;; `nil' means to ignore `corfu-separator' behavior, that is, use the older
+ ;; `corfu-quit-at-boundary' = nil behavior. Set this to separator if using
+ ;; `corfu-auto' = `t' workflow (in that case, make sure you also set up
+ ;; `corfu-separator' and a keybind for `corfu-insert-separator', which my
+ ;; configuration already has pre-prepared). Necessary for manual corfu usage with
+ ;; orderless, otherwise first component is ignored, unless `corfu-separator'
+ ;; is inserted.
+ (corfu-quit-at-boundary nil)
+ (corfu-separator ?\s) ; Use space
+ (corfu-quit-no-match 'separator) ; Don't quit if there is `corfu-separator' inserted
+ (corfu-preview-current 'insert) ; Preview first candidate. Insert on input if only one
+ (corfu-preselect-first t) ; Preselect first candidate?
+
+ ;; Other
+ (corfu-echo-documentation nil) ; Already use corfu-popupinfo
+
+ :config
+ (global-corfu-mode))
+#+end_src
+
+**** corfu-popupinfo
+
+#+begin_src emacs-lisp
+(use-package corfu-popupinfo
+ :after corfu
+ :hook (corfu-mode . corfu-popupinfo-mode)
+ :bind (:map corfu-map
+ ("M-n" . corfu-popupinfo-scroll-up)
+ ("M-p" . corfu-popupinfo-scroll-down)
+ ([remap corfu-show-documentation] . corfu-popupinfo-toggle))
+ :custom
+ (corfu-popupinfo-delay 0.5)
+ (corfu-popupinfo-max-width 70)
+ (corfu-popupinfo-max-height 20)
+ ;; Also here to be extra-safe that this is set when `corfu-popupinfo' is
+ ;; loaded. I do not want documentation shown in both the echo area and in
+ ;; the `corfu-popupinfo' popup.
+ (corfu-echo-documentation nil))
+#+end_src
+
+*** cape
+
+#+begin_src emacs-lisp
+(use-package cape
+ :demand t
+ :ensure t
+ :bind (("C-c . p" . completion-at-point)
+ ("C-c . t" . complete-tag)
+ ("C-c . d" . cape-dabbrev)
+ ("C-c . h" . cape-history)
+ ("C-c . f" . cape-file)
+ ("C-c . k" . cape-keyword)
+ ("C-c . s" . cape-symbol)
+ ("C-c . a" . cape-abbrev)
+ ("C-c . l" . cape-line)
+ ("C-c . w" . cape-dict)
+ ("C-c . \\" . cape-tex)
+ ("C-c . _" . cape-tex)
+ ("C-c . ^" . cape-tex)
+ ("C-c . &" . cape-sgml)
+ ("C-c . r" . cape-rfc1345))
+ :init
+ ;; Add `completion-at-point-functions', used by `completion-at-point'.
+ (add-to-list 'completion-at-point-functions #'cape-dabbrev)
+ (add-to-list 'completion-at-point-functions #'cape-ispell)
+ (add-to-list 'completion-at-point-functions #'cape-file)
+ (add-to-list 'completion-at-point-functions #'cape-abbrev))
+#+end_src
+
+*** elisp-mode-cape
+
+#+begin_src emacs-lisp
+(use-package elisp-mode-cape
+ :after (cape elisp-mode)
+ :hook (emacs-lisp-mode . my/setup-elisp)
+ :preface
+ (defun my/setup-elisp ()
+ (setq-local completion-at-point-functions
+ `(,(cape-super-capf
+ #'elisp-completion-at-point
+ #'cape-dabbrev)
+ cape-file)
+ cape-dabbrev-min-length 5)))
+#+end_src
+
+*** embark
+
+#+begin_src emacs-lisp
+ (use-package embark
+ :ensure t
+ :bind (("M-." . embark-act)
+ ("C-h b" . embark-bindings) ;; alternative for `describe-bindings'
+
+ :map embark-collect-mode-map
+ ("C-c C-a" . embark-collect-direct-action-minor-mode))
+ :init
+ ;; Optionally replace the key help with a completing-read interface
+ (setq prefix-help-command #'embark-prefix-help-command)
+
+ ;; Show the Embark target at point via Eldoc. You may adjust the Eldoc
+ ;; strategy, if you want to see the documentation from multiple providers.
+ (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target)
+
+ :config
+ ;; Hide the mode line of the Embark live/completions buffers
+ (add-to-list 'display-buffer-alist
+ '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
+ nil
+ (window-parameters (mode-line-format . none)))))
+#+end_src
+
+**** embark-consult
+
+#+begin_src emacs-lisp
+ (use-package embark-consult
+ :ensure t
+ :hook
+ (embark-collect-mode . consult-preview-at-point-mode))
+#+end_src
+
+*** marginalia
+
+
+#+begin_src emacs-lisp
+ (use-package marginalia
+ :ensure t
+ ;; Either bind `marginalia-cycle' globally or only in the minibuffer
+ :bind (("M-A" . marginalia-cycle)
+ :map minibuffer-local-map
+ ("M-A" . marginalia-cycle))
+
+ ;; The :init configuration is always executed (Not lazy!)
+ :config
+ ;; Must be in the :init section of use-package such that the mode gets
+ ;; enabled right away. Note that this forces loading the package.
+ (marginalia-mode))
+#+end_src
+
+*** orderless
+
+#+begin_src emacs-lisp
+ (use-package orderless
+ :demand t
+ :ensure t
+ :custom
+ (completion-styles '(orderless basic))
+ (completion-category-defaults nil))
+#+end_src
+
+*** vertico
+
+
+#+begin_src emacs-lisp
+ (use-package vertico
+ :demand t
+ :ensure t
+ :bind (("C-c . ." . vertico-repeat)
+ :map vertico-map
+ ("C-j" . vertico-exit-input)
+ ("C-M-n" . vertico-next-group)
+ ("C-M-p" . vertico-previous-group))
+ :hook
+ (minibuffer-setup . vertico-repeat-save)
+ (rfn-eshadow-update-overlay . vertico-directory-tidy)
+ :custom
+ (vertico-count 10)
+ (vertico-resize nil)
+ (vertico-cycle t)
+ :preface
+ (defun crm-indicator (args)
+ (cons (format "[CRM%s] %s"
+ (replace-regexp-in-string
+ "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
+ crm-separator)
+ (car args))
+ (cdr args)))
+ :config
+ (vertico-mode)
+
+ ;; Add prompt indicator to `completing-read-multiple'.
+ ;; We display [CRM<separator>], e.g., [CRM,] if the separator is a comma.
+ (advice-add #'completing-read-multiple :filter-args #'crm-indicator)
+
+ ;; Do not allow the cursor in the minibuffer prompt
+ (setq minibuffer-prompt-properties
+ '(read-only t cursor-intangible t face minibuffer-prompt))
+
+ (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)
+
+ ;; Hide commands in M-x which do not work in the current mode. Vertico
+ ;; commands are hidden in normal buffers.
+ (setq read-extended-command-predicate
+ #'command-completion-default-include-p))
+#+end_src
+
+*** yasnippet
+
+#+begin_src emacs-lisp
+ (use-package yasnippet
+ :ensure t
+ :demand t
+ :diminish
+ :commands yas-minor-mode-on
+ :bind (("C-c y d" . yas-load-directory)
+ ("C-c y i" . yas-insert-snippet)
+ ("C-c y f" . yas-visit-snippet-file)
+ ("C-c y n" . yas-new-snippet)
+ ("C-c y t" . yas-tryout-snippet)
+ ("C-c y l" . yas-describe-tables)
+ ("C-c y g" . yas-global-mode)
+ ("C-c y m" . yas-minor-mode)
+ ("C-c y r" . yas-reload-all)
+ ("C-c y x" . yas-expand)
+ :map yas-keymap
+ ("C-i" . yas-next-field-or-maybe-expand))
+ :mode ("/emacs\\.d/snippets/" . snippet-mode)
+ :hook (prog-mode . yas-minor-mode-on)
+ :custom
+ (yas-prompt-functions '(yas-completing-prompt yas-no-prompt))
+ (yas-snippet-dirs (list (emacs-path "snippets")))
+ (yas-triggers-in-field t)
+ (yas-wrap-around-region t)
+ :config
+ (yas-load-directory (emacs-path "snippets")))
+#+end_src
+
+** dired
+
+#+begin_src emacs-lisp
+(use-package dired
+ :diminish
+ :bind (:map dired-mode-map
+ ("j" . dired)
+ ("z" . pop-window-configuration)
+ ("^" . dired-up-directory)
+ ("q" . pop-window-configuration)
+ ("M-!" . shell-command)
+ ("<tab>" . dired-next-window))
+ :custom
+ (dired-clean-up-buffers-too nil)
+ (dired-dwim-target t)
+ (dired-hide-details-hide-information-lines nil)
+ (dired-hide-details-hide-symlink-targets nil)
+ (dired-listing-switches "-lah")
+ (dired-no-confirm
+ '(byte-compile chgrp chmod chown copy hardlink symlink touch))
+ (dired-omit-mode nil t)
+ (dired-omit-size-limit 60000)
+ (dired-recursive-copies 'always)
+ (dired-recursive-deletes 'always)
+ :functions (dired-dwim-target-directory))
+#+end_src
+
+** direnv
+Invokes [[https://direnv.net/][direnv]] to obtain environment for the current file.
+
+#+begin_src emacs-lisp
+ (use-package direnv
+ :ensure t
+ :custom
+ (direnv-always-show-summary nil)
+ :config
+ (direnv-mode))
+#+end_src
+
+** docker
+
+#+begin_src emacs-lisp
+(use-package docker
+ :bind ("C-c d" . docker)
+ :diminish
+ :init
+ (use-package docker-image :commands docker-images)
+ (use-package docker-volume :commands docker-volumes)
+ (use-package docker-network :commands docker-containers)
+ (use-package docker-compose :commands docker-compose)
+
+ (use-package docker-container
+ :commands docker-containers
+ :custom
+ (docker-containers-shell-file-name "/bin/bash")
+ (docker-containers-show-all nil)))
+#+end_src
+
+*** docker-compose-mode
+
+#+begin_src emacs-lisp
+(use-package docker-compose-mode
+ :mode "docker-compose.*\.yml\\'")
+#+end_src
+
+*** dockerfile-mode
+
+#+begin_src emacs-lisp
+(use-package dockerfile-mode
+ :mode "Dockerfile[a-zA-Z.-]*\\'")
+#+end_src
+
+** elfeed
+
+#+begin_src emacs-lisp
+(use-package elfeed
+ :ensure t
+ :custom
+ (elfeed-db-directory (user-data "elfeed/"))
+ (elfeed-search-filter "@2-weeks-ago +unread")
+ (elfeed-show-entry-switch 'pop-to-buffer)
+ (elfeed-show-entry-delete 'delete-window))
+
+(defface elfeed-face-tag-mustread
+ '((t :foreground "#f00"))
+ "This is a custom font face for the `mustread' tag in Elfeed."
+ :group 'elfeed)
+
+(push '(mustread elfeed-face-tag-mustread) elfeed-search-face-alist)
+#+end_src
+
+*** elfeed-org
+
+#+begin_src emacs-lisp
+(use-package elfeed-org
+ :ensure t
+ :init
+ (setq rmh-elfeed-org-files (list (expand-file-name "etc/elfeed.org" user-emacs-directory)))
+ :config (elfeed-org))
+#+end_src
+
+** emacs
+
+#+begin_src emacs-lisp
+ (use-package emacs
+ :bind* ("M-j" . join-line)
+
+ :custom-face
+ (cursor ((t (:background "hotpink"))))
+ ;; (highlight ((t (:background "blue4"))))
+ ;; (minibuffer-prompt ((t (:foreground "grey80"))))
+ ;; (mode-line-inactive ((t (:background "grey50"))))
+ (nobreak-space ((t nil)))
+
+ :custom
+ (auto-save-default nil) ;; don't auto save files
+ (auto-save-list-file-prefix nil) ;; no backups
+ (create-lockfiles nil) ;; don't use a lock file
+ (confirm-kill-emacs #'yes-or-no-p) ;; ask before killing emacs
+ (make-backup-files nil) ;; really no backups
+ (minibuffer-message-timeout 0.5) ;; How long to display an echo-area message
+ (next-screen-context-lines 5) ;; scroll 5 lines at a time
+ (require-final-newline t) ;; ensure newline exists at the end of the file
+ (ring-bell-function 'ignore) ;; really no bell
+ (tab-always-indent 'complete) ;; when using TAB, always indent
+ (visible-bell nil) ;; no bell
+ (column-number-mode t) ;; show column number in the mode line
+ (electric-pair-mode 1) ;; matches parens and brackets
+ (indent-tabs-mode nil) ;; turn off tab indentation
+ (cursor-type 'box) ;; cursor is a horizontal bar
+ (delete-by-moving-to-trash t) ;; delete files by moving them to the trash
+ (initial-scratch-message "") ;; empty scratch buffer
+ (garbage-collection-messages t) ;; log when the gc kicks in
+
+ ;; bytecomp.el
+ (byte-compile-verbose nil)
+
+ ;; prog-mode.el
+ (prettify-symbols-unprettify-at-point 'right-edge)
+
+ ;; startup.el
+ (auto-save-list-file-prefix (user-data "auto-save-list/.saves-"))
+ (initial-buffer-choice t)
+ (initial-major-mode 'fundamental-mode)
+ (initial-scratch-message "")
+
+ (user-full-name "Franck Cuny")
+ (user-mail-address "franck@fcuny.net")
+ (add-log-mailing-address "franck@fcuny.net")
+
+ (history-length 200)
+ (history-delete-duplicates t)
+ (completion-ignored-extensions
+ '(".class"
+ ".cp"
+ ".elc"
+ ".fmt"
+ ".git/"
+ ".pyc"
+ ".so"
+ "~"))
+
+ ;; paragraphs.el
+ (sentence-end-double-space nil)
+
+ ;; switch to view-mode whenever you are in a read-only buffer (e.g.
+ ;; switched to it using C-x C-q).
+ (view-read-only t)
+
+ ;; window.el
+ (switch-to-buffer-preserve-window-point t)
+
+ ;; warnings.el
+ (warning-minimum-log-level :error)
+
+ ;; frame.el
+ (window-divider-default-bottom-width 1)
+ (window-divider-default-places 'bottom-only)
+
+ ;; paren.el
+ (show-paren-delay 0)
+ (show-paren-highlight-openparen t)
+ (show-paren-when-point-inside-paren t)
+ (show-paren-when-point-in-periphery t)
+
+ :config
+ (add-hook 'after-save-hook
+ #'executable-make-buffer-file-executable-if-script-p))
+#+end_src
+
+** eshell
+#+begin_src emacs-lisp
+(use-package eshell
+ :commands (eshell eshell-command)
+ :custom
+ (eshell-directory-name (emacs-path "eshell"))
+ (eshell-hist-ignoredups t)
+ (eshell-history-size 50000)
+ (eshell-ls-dired-initial-args '("-h"))
+ (eshell-ls-exclude-regexp "~\\'")
+ (eshell-ls-initial-args "-h")
+ (eshell-modules-list
+ '(eshell-alias
+ eshell-basic
+ eshell-cmpl
+ eshell-dirs
+ eshell-glob
+ eshell-hist
+ eshell-ls
+ eshell-pred
+ eshell-prompt
+ eshell-rebind
+ eshell-script
+ eshell-term
+ eshell-unix
+ eshell-xtra))
+ (eshell-prompt-function
+ (lambda nil
+ (concat (abbreviate-file-name (eshell/pwd))
+ (if (= (user-uid) 0)
+ " # " " $ "))))
+ (eshell-save-history-on-exit t)
+ (eshell-stringify-t nil)
+ (eshell-term-name "ansi")
+
+ :preface
+ (defun eshell-initialize ()
+ (add-hook 'eshell-expand-input-functions #'eshell-spawn-external-command)
+
+ :init
+ (add-hook 'eshell-first-time-mode-hook #'eshell-initialize)))
+#+end_src
+
+** flymake
+
+#+begin_src emacs-lisp
+(use-package flymake
+ :bind (("C-c ! n" . flymake-goto-next-error)
+ ("C-c ! p" . flymake-goto-next-error))
+ :custom
+ (flymake-start-on-save-buffer t)
+ (flymake-fringe-indicator-position 'left-fringe)
+ (flymake-suppress-zero-counters t)
+ (flymake-start-on-flymake-mode t)
+ (flymake-no-changes-timeout nil)
+ (flymake-proc-compilation-prevents-syntax-check t)
+ (flymake-wrap-around nil)
+ (elisp-flymake-byte-compile-load-path load-path))
+#+end_src
+
+** font
+Somehow I need to set the font to a higher height on MacOS.
+
+#+BEGIN_SRC emacs-lisp
+(set-face-attribute 'default nil
+ :family "JetBrains Mono"
+ :height 130)
+
+(set-face-attribute 'fixed-pitch nil
+ :family "JetBrain Mono"
+ :height 130)
+
+(set-face-attribute 'variable-pitch nil
+ :family "Roboto"
+ :height 130)
+
+(use-package default-text-scale
+ :bind
+ (("C-)" . default-text-scale-reset)
+ ("C-=" . default-text-scale-increase)
+ ("C--" . default-text-scale-decrease)))
+#+END_SRC
+
+** fringe
+
+#+begin_src emacs-lisp
+(use-package fringe
+ :config
+ ;;; no fringe on the right side
+ (set-fringe-mode '(8 . 0)))
+#+end_src
+
+** git-link
+
+A convenient package to create link to a code location in GitHub, or other forges. This is very useful to get a link and share with others when looking at some code.
+
+#+begin_src emacs-lisp
+ (use-package git-link
+ :defines git-link-remote-alist
+ :ensure t
+ :bind ("C-c Y" . git-link)
+ :commands (git-link git-link-commit git-link-homepage)
+
+ :custom
+ (git-link-open-in-browser t)
+
+ :preface
+ (defun git-link-fcuny-net (hostname dirname filename branch commit start end)
+ (format "http://git.fcuny.net/%s/tree/%s?id=%s#n%s"
+ (replace-regexp-in-string "^r/\\(.*\\)" "\\1.git" dirname)
+ filename
+ commit
+ start))
+
+ (defun git-link-commit-fcuny-net (hostname dirname commit)
+ (format "http://git.fcuny.net/%s/commit/?id=%s"
+ (replace-regexp-in-string "^r/\\(.*\\)" "\\1.git" dirname)
+ commit))
+
+ :config
+ (add-to-list 'git-link-remote-alist '("git\\.fcuny\\.net" git-link-fcuny-net))
+ (add-to-list 'git-link-commit-remote-alist '("git\\.fcuny\\.net" git-link-commit-fcuny-net))
+
+ ;; sets up roblox git enterprise as a git-link handler
+ (add-to-list 'git-link-remote-alist '("github\\.rblx\\.com" git-link-github))
+ (add-to-list 'git-link-commit-remote-alist '("github\\.rblx\\.com" git-link-commit-github)))
+#+end_src
+
+** ibuffer
+
+#+begin_src emacs-lisp
+(use-package ibuffer
+ :bind ("C-x C-b" . ibuffer)
+ :custom
+ (ibuffer-default-display-maybe-show-predicates t)
+ (ibuffer-expert t)
+ (ibuffer-formats
+ '((mark modified read-only " "
+ (name 16 -1)
+ " "
+ (size 6 -1 :right)
+ " "
+ (mode 16 16)
+ " " filename)
+ (mark " "
+ (name 16 -1)
+ " " filename)))
+ (ibuffer-maybe-show-regexps nil)
+ (ibuffer-saved-filter-groups
+ '(("default"
+ ("Magit"
+ (or
+ (mode . magit-status-mode)
+ (mode . magit-log-mode)
+ (name . "\\*magit")
+ (name . "magit-")
+ (name . "git-monitor")))
+ ("Commands"
+ (or
+ (mode . shell-mode)
+ (mode . eshell-mode)
+ (mode . term-mode)
+ (mode . compilation-mode)))
+ ("Rust"
+ (or
+ (mode . rust-mode)
+ (mode . cargo-mode)
+ (name . "\\*Cargo")
+ (name . "^\\*rls\\(::stderr\\)?\\*")
+ (name . "eglot")))
+ ("Nix"
+ (mode . nix-mode))
+ ("Lisp"
+ (mode . emacs-lisp-mode))
+ ("Dired"
+ (mode . dired-mode))
+ ("Org"
+ (or
+ (name . "^\\*Calendar\\*$")
+ (name . "^\\*Org Agenda")
+ (name . "^ \\*Agenda")
+ (name . "^diary$")
+ (mode . org-mode)))
+ ("Emacs"
+ (or
+ (name . "^\\*scratch\\*$")
+ (name . "^\\*Messages\\*$")
+ (name . "^\\*\\(Customize\\|Help\\)")
+ (name . "\\*\\(Echo\\|Minibuf\\)"))))))
+ (ibuffer-show-empty-filter-groups nil)
+ (ibuffer-shrink-to-minimum-size t t)
+ (ibuffer-use-other-window t)
+ :init
+ (add-hook 'ibuffer-mode-hook
+ #'(lambda ()
+ (ibuffer-switch-to-saved-filter-groups "default"))))
+#+end_src
+
+** indent
+
+#+begin_src emacs-lisp
+(use-package indent
+ :commands indent-according-to-mode
+ :custom
+ (tab-always-indent 'complete))
+#+end_src
+
+** ispell
+
+#+begin_src emacs-lisp
+ (use-package ispell
+ :custom
+ (ispell-program-name (executable-find "aspell"))
+ (ispell-dictionary "en_US")
+ (ispell-extra-args '("--camel-case")))
+#+end_src
+
+** jq-mode
+
+#+begin_src emacs-lisp
+ (use-package jq-mode
+ :ensure t
+ :mode "\\.jq\\'")
+#+end_src
+
+** js2-mode
+** languages
+*** eglot
+
+After experimenting with [[https://emacs-lsp.github.io/lsp-mode/][lsp-mode]] and [[https://github.com/joaotavora/eglot][eglot]] I decided to go with eglot for LSP integration.
+
+For languages where I want to use LSP, I need to add ~:hook (nix-mode . englot-ensure)~ in the ~use-package~ definition for the language.
+
+#+begin_src emacs-lisp
+ (use-package eglot)
+#+end_src
+
+*** tree-sitter
+
+#+begin_src emacs-lisp
+(use-package tree-sitter
+ :ensure t
+ :config
+ (global-tree-sitter-mode))
+#+end_src
+
+**** tree-sitter-langs
+
+#+begin_src emacs-lisp
+(use-package tree-sitter-langs
+ :after tree-sitter
+ :ensure t)
+#+end_src
+
+*** lisp-mode
+
+**** elisp-mode
+
+#+begin_src emacs-lisp
+(use-package emacs-lisp-mode
+ :bind (:map emacs-lisp-mode-map
+ ("C-c C-r" . eval-region)
+ ("C-c C-d" . eval-defun)
+ ("C-c C-b" . eval-buffer))
+ :hook ((emacs-lisp-mode . flymake-mode)))
+#+end_src
+
+**** eldoc
+
+#+begin_src emacs-lisp
+(use-package eldoc
+ :diminish
+ :hook ((c-mode-common emacs-lisp-mode) . eldoc-mode)
+ :custom
+ (eldoc-idle-delay 1)
+ (eldoc-documentation-strategy #'eldoc-documentation-default)
+ (eldoc-echo-area-use-multiline-p 3)
+ (eldoc-echo-area-prefer-doc-buffer 'maybe)
+ (eldoc-echo-area-display-truncation-message nil))
+#+end_src
+
+*** hcl-mode
+
+#+begin_src emacs-lisp
+ (use-package hcl-mode
+ :ensure t
+ :mode "\.nomad\\'")
+#+end_src
+
+*** json-mode
+
+#+begin_src emacs-lisp
+(use-package json-mode
+ :mode "\\.json\\'")
+#+end_src
+
+**** json-reformat
+
+#+begin_src emacs-lisp
+(use-package json-reformat
+ :ensure t
+ :after json-mode)
+#+end_src
+
+*** go-mode
+
+#+begin_src emacs-lisp
+ (use-package go-mode
+ :ensure t
+ :hook ((go-mode . tree-sitter-hl-mode)
+ (go-mode . eglot-ensure)
+ (go-mode . (lambda () (add-hook 'before-save-hook 'eglot-format-buffer nil t))))
+ :bind (:map go-mode-map
+ ("C-c C-c" . compile)))
+#+end_src
+
+**** gotest
+
+#+begin_src emacs-lisp
+(use-package gotest
+ :ensure t
+ :after go-mode
+ :custom
+ (go-test-verbose t))
+#+end_src
+
+*** nix-mode
+
+#+begin_src emacs-lisp
+ (use-package nix-mode
+ :ensure t
+ :hook ((nix-mode . eglot-ensure)
+ (nix-mode . (lambda () (add-hook 'before-save-hook 'eglot-format-buffer nil t))))
+ :custom
+ (nix-indent-function 'nix-indent-line))
+#+end_src
+
+*** protobuf-mode
+
+#+begin_src emacs-lisp
+ (use-package protobuf-mode
+ :ensure t
+ :mode "\\.proto\\'")
+#+end_src
+
+*** python-mode
+
+Enable eglot and tree-sitter when working with python.
+
+#+begin_src emacs-lisp
+(use-package python-mode
+ :hook ((python-mode . tree-sitter-hl-mode)
+ (python-mode . eglot-ensure)
+ (python-mode . (lambda () (add-hook 'before-save-hook 'eglot-format-buffer nil t))))
+ :interpreter "python"
+ :bind (:map python-mode-map
+ ("C-c c")
+ ("C-c C-z" . python-shell)))
+#+end_src
+
+**** blacken
+
+[[https://pypi.org/project/black/][black]] is a code formatter for python.
+
+#+begin_src emacs-lisp
+(use-package blacken
+ :ensure t
+ :hook (python-mode . blacken-mode))
+#+end_src
+
+**** isort
+
+[[https://pypi.org/project/isort/][Sort imports]].
+
+#+begin_src emacs-lisp
+ (use-package py-isort
+ :ensure t
+ :commands (py-isort-buffer py-isort-region))
+#+end_src
+
+*** ruby-mode
+
+#+begin_src emacs-lisp
+ (use-package ruby-mode
+ :mode "\\.rb\\'"
+ :interpreter "ruby"
+ :bind (:map ruby-mode-map
+ ("<return>" . my-ruby-smart-return))
+ :preface
+ (defun my-ruby-smart-return ()
+ (interactive)
+ (when (memq (char-after) '(?\| ?\" ?\'))
+ (forward-char))
+ (call-interactively 'newline-and-indent)))
+#+end_src
+
+*** rustic-mode
+
+#+begin_src emacs-lisp
+(use-package rustic
+ :ensure t
+ :custom
+ (rustic-format-on-save t)
+ (rustic-format-trigger 'on-save)
+ (rustic-lsp-server 'rust-analyzer)
+ (rustic-lsp-client 'eglot))
+#+end_src
+
+*** sh-script
+
+#+begin_src emacs-lisp
+(use-package sh-script
+ :defer t
+ :preface
+ (defvar sh-script-initialized nil)
+
+ (defun initialize-sh-script ()
+ (unless sh-script-initialized
+ (setq sh-script-initialized t)
+ (info-lookup-add-help :mode 'shell-script-mode
+ :regexp ".*"
+ :doc-spec '(("(bash)Index")))))
+ :init
+ (add-hook 'shell-mode-hook #'initialize-sh-script))
+#+end_src
+
+*** terraform-mode
+
+#+begin_src emacs-lisp
+(use-package terraform-mode
+ :ensure t
+ :mode "\.tf\\'")
+#+end_src
+
+*** toml-mode
+
+#+begin_src emacs-lisp
+(use-package toml-mode
+ :ensure t)
+#+end_src
+
+*** yaml-mode
+
+#+begin_src emacs-lisp
+(use-package yaml-mode
+ :mode "\\.ya?ml\\'")
+#+end_src
+
+** MacOS specific
+
+#+begin_src emacs-lisp
+(when (memq window-system '(mac ns))
+ (add-to-list 'default-frame-alist '(fullscreen . maximized))
+ (add-to-list 'default-frame-alist '(ns-appearance . nil))
+ (add-to-list 'default-frame-alist '(ns-transparent-titlebar . nil))
+ (when (boundp 'ns-use-native-fullscreen)
+ (setq ns-use-native-fullscreen nil))
+ (when (boundp 'mac-allow-anti-aliasing)
+ (setq mac-allow-anti-aliasing t)))
+#+end_src
+
+** magit
+
+#+begin_src emacs-lisp
+ (use-package magit
+ :ensure t
+ :bind (("C-x g" . magit-status)
+ ("C-x G" . magit-status-with-prefix))
+ :custom
+ (magit-diff-options nil)
+ (magit-diff-refine-hunk t)
+ (magit-fetch-arguments nil)
+ (magit-log-section-commit-count 10)
+ (magit-pre-refresh-hook nil)
+ (magit-process-popup-time 15)
+ (magit-clone-default-directory "~/workspace/")
+ (magit-section-initial-visibility-alist '((untracked . hide)))
+ :hook (magit-mode . hl-line-mode)
+ :config
+ (use-package magit-commit
+ :defer t
+ :config
+ (use-package git-commit
+ :custom
+ (git-commit-major-mode 'markdown-mode)
+ (git-commit-setup-hook
+ '(git-commit-save-message
+ git-commit-turn-on-auto-fill
+ git-commit-turn-on-flyspell
+ bug-reference-mode))))
+
+ (use-package magit-status
+ :defer t
+ :config
+ (dolist (func '(magit-insert-unpushed-to-upstream-or-recent
+ magit-insert-unpulled-from-pushremote
+ magit-insert-unpulled-from-upstream
+ ))
+ (remove-hook 'magit-status-sections-hook func))
+
+ (dolist (func '(magit-insert-diff-filter-header
+ magit-insert-tags-header))
+ (remove-hook 'magit-status-headers-hook func))))
+#+end_src
+
+** markdown-mode
+
+#+begin_src emacs-lisp
+(use-package markdown-mode
+ :mode (("\\`README\\.md\\'" . gfm-mode)
+ ("\\.md\\'" . markdown-mode)
+ ("\\.markdown\\'" . markdown-mode))
+ :custom
+ (markdown-command "pandoc -f markdown_github+smart")
+ (markdown-command-needs-filename t)
+ (markdown-enable-math t)
+ (markdown-open-command "marked")
+ :init
+ (setq markdown-command "multimarkdown"))
+#+end_src
+
+*** markdown-preview-mode
+
+#+begin_src emacs-lisp
+ (use-package markdown-preview-mode
+ :ensure t
+ :after markdown-mode
+ :config
+ (setq markdown-preview-stylesheets
+ (list (concat "https://github.com/dmarcotte/github-markdown-preview/"
+ "blob/master/data/css/github.css"))))
+#+end_src
+
+** midnight
+
+#+begin_src emacs-lisp
+(use-package midnight
+ :demand t
+ :bind ("C-c z" . clean-buffer-list)
+ :custom
+ (midnight-delay 18000)
+ (clean-buffer-list-kill-never-buffer-names
+ '("*scratch*"
+ "*Messages*"
+ "*server*"
+ "*Group*"
+ "*Org Agenda*"
+ "todo.org"))
+ (clean-buffer-list-kill-never-regexps
+ '("^ \\*Minibuf-.*\\*$"
+ "^\\*Summary"
+ "^\\*Article" "^#"))
+ (clean-buffer-list-kill-regexps '(".*"))
+ :config
+ (midnight-mode t))
+#+end_src
+
+** my-cheeseboard
+
+get the pizzas for this week at cheeseboard.
+
+#+begin_src emacs-lisp
+(use-package my-cheeseboard)
+#+end_src
+
+** my-uptime
+
+an uptime / SLO calculator.
+
+#+begin_src emacs-lisp
+(use-package my-uptime
+ :init
+ (add-to-list 'display-buffer-alist '("\\*slo-calculator\\*"
+ (display-buffer-in-side-window)
+ (side . left)
+ (slot . 0)
+ (window-width . 0.35))))
+#+end_src
+
+** org-mode
+
+#+begin_src emacs-lisp
+ (use-package org
+ :commands org-resolve-clocks
+ :bind* (("C-c c" . org-capture)
+ ("C-c a" . org-agenda))
+ :bind (:map
+ org-mode-map
+ ("C-,")
+ ("C-c #" . org-priority)
+ ("C-c .")
+ ("C-c ," . org-priority)
+ ("C-c !" . org-time-stamp-inactive)
+ ("C-c <" . org-time-stamp)
+ ("C-c x" . org-todo)
+ ("C-c C-x @" . visible-mode)
+ ("C-c C-x U" . org-insert-url-link)
+ ("C-c C-x o" . my-org-todoize)
+ ([(control meta return)] . org-insert-heading-after-current))
+ :hook
+ (org-mode . abbrev-mode)
+ (org-mode . turn-on-flyspell)
+ (org-mode . visual-line-mode)
+ (org-mode . org-indent-mode)
+ (org-log-buffer-setup . abbrev-mode)
+ (org-log-buffer-setup . visual-line-mode)
+ (org-log-buffer-setup . turn-on-flyspell)
+ (org-capture-after-finalize . org-save-all-org-buffers)
+ (org-capture-prepare-finalize-hook . org-save-all-org-buffers)
+ :custom
+ ;;; general settings
+ (org-startup-folded t)
+ (org-startup-indented t)
+ (org-hide-emphasis-markers t)
+ (org-hide-leading-stars t)
+ (org-pretty-entities t)
+ (org-return-follows-link t)
+ (org-startup-with-inline-images t)
+ (org-startup-indented t)
+ (org-clone-delete-id t)
+ (org-deadline-warning-days 14)
+ (org-default-notes-file "~/documents/notes/inbox.org")
+ (org-directory "~/documents/notes/")
+ (org-export-backends '(html md))
+ (org-export-use-babel nil)
+ (org-footnote-section nil)
+ (org-icalendar-timezone "America/Los_Angeles")
+ (org-image-actual-width 800)
+ (org-imenu-depth 4)
+ (org-insert-heading-respect-content t)
+ (org-outline-path-complete-in-steps nil)
+ (org-src-fontify-natively t)
+ (org-src-preserve-indentation t)
+ (org-src-tab-acts-natively t)
+ (org-src-window-setup 'current-window)
+ (org-yank-adjusted-subtrees t)
+ (org-structure-template-alist
+ '(("s" . "src")
+ ("E" . "src emacs-lisp")
+ ("p" . "src python")
+ ("e" . "example")
+ ("q" . "quote")
+ ("V" . "verbatim")))
+
+ ;;; refile
+ (org-refile-targets '(("~/documents/notes/tasks.org" :maxlevel . 1)
+ ("~/documents/notes/notes.org" :maxlevel . 1)))
+ (org-refile-use-cache nil)
+ (org-refile-use-outline-path t)
+ (org-reverse-note-order t)
+ (org-todo-keywords
+ '((sequence "TODO(t)" "STARTED(s)" "|" "DONE(d@)" "CANCELED(x@)")))
+ (org-todo-repeat-to-state "TODO")
+ (org-fontify-done-headline t)
+ (org-fontify-quote-and-verse-blocks t)
+ (org-fontify-todo-headline nil)
+ (org-fontify-whole-block-delimiter-line t)
+ (org-fontify-whole-heading-line nil)
+ (org-enforce-todo-dependencies t)
+ (org-track-ordered-property-with-tag t)
+ (org-highest-priority ?A)
+ (org-lowest-priority ?C)
+ (org-default-priority ?A)
+ (org-confirm-babel-evaluate nil)
+ (org-tags-column -97)
+
+ ;;; log
+ (org-log-done 'time)
+ (org-log-into-drawer t)
+ (org-log-note-clock-out nil)
+ (org-log-redeadline 'time)
+ (org-log-reschedule 'time)
+ (org-read-date-prefer-future 'time)
+
+ ;;; capture
+ (org-capture-templates
+ '(("t" "tasks" entry (file "inbox.org")
+ "* TODO [#D] %?\n:PROPERTIES:\n:CREATED: %U\n:END:\n")
+
+ ("T" "TIL" entry (file+headline "til.org" "Today I learned")
+ "* %^{title} :%^{tag}:\n:PROPERTIES:\n:CREATED: %U\n:END:\n%?\nSource: %^C")
+
+ ("n" "note" entry (file "notes.org")
+ "* %?\n:PROPERTIES:\n:CREATED: %T\n:END:\n")
+
+ ("f" "feed" entry (file "inbox.org")
+ ,(concat "* TODO [#D] %:elfeed-entry-title :feed:\n"
+ ":PROPERTIES:\n:CREATED: %T\n:END:\n"
+ "%a\n"))
+
+ ("b" "bookmark" entry (file "bookmarks.org")
+ ,(concat "* %(org-cliplink-capture) :%^{tag}:\n"
+ ":PROPERTIES:\n:CREATED: %T\n:END:%?\n") :prepend t :empty-lines 1)
+
+ ("j" "journal" entry (file+olp+datetree "journal.org")
+ "* %?\n:PROPERTIES:\n:CREATED: %T\n:END:\n" :tree-type day)))
+
+ :config
+ (font-lock-add-keywords 'org-mode
+ '(("^ *\\(-\\) "
+ (0 (ignore (compose-region (match-beginning 1) (match-end 1) "•")))))))
+#+end_src
+
+*** org-agenda
+
+#+begin_src emacs-lisp
+ (use-package org-agenda
+ :commands org-agenda-list
+ :bind* (("C-c a" . org-agenda))
+ :bind (:map
+ org-agenda-mode-map
+ (" " . org-agenda-tree-to-indirect-buffer)
+ (">" . org-agenda-filter-by-top-headline)
+ ("C-n" . next-line)
+ ("C-p" . previous-line)
+ ("F" . org-agenda-follow-mode)
+ ("M-m")
+ ("M-n" . org-agenda-later)
+ ("M-p" . org-agenda-earlier)
+ ("b" . org-agenda-date-earlier)
+ ("f" . org-agenda-date-later)
+ ("g" . org-agenda-redo)
+ ("q" . delete-window)
+ ("r" . org-agenda-refile)
+ ("x" . org-agenda-todo)
+ ("z" . pop-window-configuration))
+ :custom
+ (org-agenda-auto-exclude-function 'org-my-auto-exclude-function)
+ (org-agenda-cmp-user-defined 'org-compare-todo-age)
+ (org-agenda-compact-blocks t)
+ (org-agenda-deadline-leaders '("!D!: " "D%02d: " "D-%02d:"))
+ (org-agenda-default-appointment-duration 60)
+ (org-agenda-files
+ '("~/documents/notes/inbox.org"
+ "~/documents/notes/notes.org"
+ "~/documents/notes/tasks.org"
+ "~/documents/notes/bookmarks.org"
+ "~/documents/notes/journal.org"))
+ (org-agenda-fontify-priorities t)
+ (org-agenda-include-diary t)
+ (org-agenda-inhibit-startup t)
+ (org-agenda-log-mode-items '(closed clock state))
+ (org-agenda-ndays 1)
+ (org-agenda-persistent-filter t)
+ (org-agenda-prefix-format
+ '((agenda . " %-11c%?-12t% s")
+ (timeline . " % s")
+ (todo . " %-11c")
+ (tags . " %-11c")))
+ (org-agenda-show-all-dates t)
+ (org-agenda-show-outline-path nil)
+ (org-agenda-skip-deadline-if-done t)
+ (org-agenda-skip-scheduled-if-deadline-is-shown t)
+ (org-agenda-skip-scheduled-if-done t)
+ (org-agenda-skip-unavailable-files t)
+ (org-agenda-sorting-strategy
+ '((agenda habit-down time-up todo-state-up priority-down)
+ (todo priority-down category-keep)
+ (tags priority-down category-keep)
+ (search category-keep)))
+ (org-agenda-span 'day)
+ (org-agenda-start-on-weekday 1)
+ (org-agenda-tags-column -100)
+ (org-agenda-tags-todo-honor-ignore-options t)
+ (org-agenda-text-search-extra-files '(agenda-archives))
+ (org-agenda-todo-ignore-scheduled 'past)
+ (org-agenda-use-time-grid nil)
+ (org-agenda-window-frame-fractions '(0.5 . 0.75)))
+#+end_src
+
+*** org-bullet
+
+This is to make =org-mode= document looks a bit nicer.
+
+#+begin_src emacs-lisp
+(use-package org-bullets
+ :ensure t
+ :hook (org-mode . org-bullets-mode))
+#+end_src
+
+*** org-auto-tangle
+
+#+begin_src emacs-lisp
+(use-package org-auto-tangle
+ :ensure t
+ :hook (org-mode . org-auto-tangle-mode))
+#+end_src
+
+*** org-habit
+
+#+begin_src emacs-lisp
+ (use-package org-habit
+ :after org-agenda
+ :custom
+ (org-habit-preceding-days 42)
+ (org-habit-today-glyph 45))
+#+end_src
+
+*** org-attach
+
+#+begin_src emacs-lisp
+(use-package org-attach
+ :after org
+ :init
+ (defun my-org-attach-visit-headline-from-dired ()
+ "Go to the headline corresponding to this org-attach directory."
+ (interactive)
+ (let* ((id-parts (last (split-string default-directory "/" t) 2))
+ (id (apply #'concat id-parts)))
+ (let ((m (org-id-find id 'marker)))
+ (unless m (user-error "Cannot find entry with ID \"%s\"" id))
+ (pop-to-buffer (marker-buffer m))
+ (goto-char m)
+ (move-marker m nil)))))
+#+end_src
+
+*** org-babel
+
+#+begin_src emacs-lisp
+(use-package org-babel
+ :no-require t
+ :after (org ob-emamux)
+ :config
+ (org-babel-do-load-languages
+ 'org-babel-load-languages
+ '((python . t)
+ (emacs-lisp . t)
+ (calc . t)
+ (shell . t)
+ (sql . t)
+ (dot . t)))
+
+ (remove-hook 'kill-emacs-hook 'org-babel-remove-temporary-directory)
+
+ (advice-add 'org-babel-edit-prep:emacs-lisp :after
+ #'(lambda (_info) (run-hooks 'emacs-lisp-mode-hook))))
+#+end_src
+
+*** org-bookmark-heading
+
+#+begin_src emacs-lisp
+(use-package org-bookmark-heading
+ :ensure t
+ :after org)
+#+end_src
+
+*** org-download
+
+#+begin_src emacs-lisp
+(use-package org-download
+ :after org
+ :ensure t
+ :bind (:map org-mode-map
+ ("C-c C-x C" . org-download-clipboard)
+ ("C-c C-x Y" . org-download-yank))
+ :custom
+ (org-download-method 'attach))
+#+end_src
+
+*** org-id
+
+#+begin_src emacs-lisp
+(use-package org-id
+ :after org
+ :bind (:map org-mode-map
+ ("C-c C-x i" . org-id-get-create))
+ :custom
+ (org-id-locations-file (user-data "org-id-locations")))
+#+end_src
+
+*** org-protocol
+
+#+begin_src emacs-lisp
+(use-package org-protocol
+ :after org)
+#+end_src
+
+*** org-ql
+
+#+begin_src emacs-lisp
+(use-package org-ql
+ :ensure t
+ :commands org-ql-search)
+#+end_src
+
+*** org-rich-yank
+
+#+begin_src emacs-lisp
+(use-package org-rich-yank
+ :ensure t
+ :bind (:map org-mode-map
+ ("C-M-y" . org-rich-yank)))
+#+end_src
+
+*** org-sticky-header
+
+#+begin_src emacs-lisp
+(use-package org-sticky-header
+ :ensure t
+ :commands org-sticky-header-mode)
+#+end_src
+
+*** org-super-agenda
+
+#+begin_src emacs-lisp
+(use-package org-super-agenda
+ :after org
+ :ensure t
+ :custom
+ (org-super-agenda-groups
+ '((:name "Important" :priority "A")
+ (:name "Needs review" :todo "WAITING" :todo "DELEGATED")
+ (:name "Optional" :priority "C" :order 9)
+ (:name "Tasks" :not (:priority "A" :priority "C"))))
+ (org-super-agenda-header-separator "")
+ :config
+ (org-super-agenda-mode))
+#+end_src
+
+*** org-web-tools
+
+#+begin_src emacs-lisp
+(use-package org-web-tools
+ :ensure t
+ :bind (("C-c w C-y" . my-org-insert-url)
+ ("C-c w C-M-y" . org-web-tools-insert-web-page-as-entry))
+ :functions (org-web-tools--org-link-for-url
+ org-web-tools--get-first-url)
+ :preface
+ (defun my-org-insert-url (&optional arg)
+ (interactive "P")
+ (require' org-web-tools)
+ (let ((link (org-web-tools--org-link-for-url
+ (org-web-tools--get-first-url))))
+ (if arg
+ (progn
+ (org-set-property "URL" link)
+ (message "Added pasteboard link to URL property"))
+ (insert link)))))
+#+end_src
+
+*** orgit
+
+#+begin_src emacs-lisp
+(use-package orgit
+ :ensure t
+ :after org)
+#+end_src
+
+*** ox
+
+**** ox-gfm
+
+#+begin_src emacs-lisp
+(use-package ox-gfm
+ :ensure t
+ :after org)
+#+end_src
+
+**** ox-md
+
+#+begin_src emacs-lisp
+(use-package ox-md
+ :after org)
+#+end_src
+
+**** ox-pandoc
+
+#+begin_src emacs-lisp
+(use-package ox-pandoc
+ :ensure t
+ :after org
+ :preface
+ (defun markdown-to-org-region (start end)
+ "Convert region from markdown to org, replacing selection"
+ (interactive "r")
+ (shell-command-on-region start end "pandoc -f markdown -t org" t t)))
+#+end_src
+
+** path setup
+
+#+begin_src emacs-lisp
+(use-package exec-path-from-shell
+ :ensure t
+ :demand t
+ :if (memq window-system '(mac ns))
+ :config
+ (exec-path-from-shell-initialize))
+#+end_src
+
+** project
+#+begin_src emacs-lisp
+ (use-package project
+ :ensure nil
+ :custom
+ (project-switch-commands
+ '((project-dired "Root" ?d)
+ (project-find-file "File" ?f)
+ (project-switch-to-buffer "Buffer" ?b)
+ (project-eshell "Shell" ?e)
+ (magit-project-status "Git" ?g)))
+ :init
+ (setq-default project-list-file (user-data "projects.eld")))
+#+end_src
+
+** recentf
+
+#+begin_src emacs-lisp
+(use-package recentf
+ :demand t
+ :commands (recentf-mode
+ recentf-add-file
+ recentf-apply-filename-handlers)
+ :custom
+ (recentf-auto-cleanup 60)
+ (recentf-exclude
+ '("~\\'" "\\`out\\'" "\\.log\\'" "^/[^/]*:" "\\.el\\.gz\\'" "\\.gz\\'"))
+ (recentf-max-saved-items 2000)
+ (recentf-save-file (user-data "recentf"))
+ :preface
+ :config
+ (recentf-mode 1))
+#+end_src
+
+** rg
+
+#+begin_src emacs-lisp
+ (use-package rg
+ :ensure t
+ :custom ((rg-group-result t)
+ (rg-show-columns t)
+ (rg-align-line-number-field-length 3)
+ (rg-align-column-number-field-length 3)
+ (rg-align-line-column-separator "#")
+ (rg-align-position-content-separator "|")
+ (rg-hide-command nil)
+ (rg-align-position-numbers t)
+ (rg-command-line-flags '("--follow"))))
+#+end_src
+
+** savehist
+
+#+begin_src emacs-lisp
+(use-package savehist
+ :unless noninteractive
+ :custom
+ (savehist-additional-variables
+ '(file-name-history
+ kmacro-ring
+ compile-history
+ compile-command))
+ (savehist-autosave-interval 60)
+ (savehist-file (user-data "history"))
+ (savehist-ignored-variables
+ '(load-history
+ flyspell-auto-correct-ring
+ org-roam-node-history
+ magit-revision-history
+ org-read-date-history
+ query-replace-history
+ yes-or-no-p-history
+ kill-ring))
+ (savehist-mode t)
+ :config
+ (savehist-mode 1))
+#+end_src
+
+** saveplace
+
+#+begin_src emacs-lisp
+(use-package saveplace
+ :unless noninteractive
+ :custom
+ (save-place-file (user-data "places"))
+ :config
+ (save-place-mode 1))
+#+end_src
+
+** theme
+
+*** modus-themes
+
+This theme is clean and readable. The [[https://protesilaos.com/emacs/modus-themes][online documentation]] is pretty detailed too.
+
+#+begin_src emacs-lisp
+(use-package emacs
+ :custom
+ ;; Syntax Highlighting
+ ;; Increase the number of bolded syntax elements
+ (modus-themes-bold-constructs t)
+ ;; Increase the number of italicized syntax elements
+ (modus-themes-italic-constructs t)
+
+ ;; Org Mode
+ ;; Make headings in org files more distinct
+ (modus-themes-headings '((t . (background bold rainbow 1))))
+ ;; Tint the background of code blocks in org files
+ (modus-themes-org-blocks 'tinted-background)
+ ;; Make tags less colorful and tables look the same as
+ ;; the default foreground.
+ (prose-done cyan-cooler)
+ (prose-tag fg-dim)
+ (prose-table fg-main)
+
+ ;; Other
+ ;; Make the fringe more intense
+ (modus-themes-common-palette-overrides '((fringe bg-active)))
+ ;; Enhance minibuffer completions
+ (modus-themes-completions 'minimal)
+ ;; Make the current line of `hl-line-mode' a fine shade of gray
+ (bg-hl-line bg-dim)
+ ;; Make matching parentheses a shade of magenta
+ (bg-paren-match bg-magenta-intense)
+ (bg-mode-line-inactive bg-dim)
+
+ :config
+ (load-theme 'modus-operandi t))
+#+end_src
+
+*** COMMENT standard-themes
+
+I like the default theme, but there's not enough contrast. The package =standard-themes= fix this problem. The full documentation can be [[https://protesilaos.com/emacs/standard-themes][read online]].
+
+#+begin_src emacs-lisp
+(use-package standard-themes
+ :ensure t
+ :init
+ (setq standard-themes-bold-constructs t
+ standard-themes-italic-constructs t
+ standard-themes-mixed-fonts t
+ standard-themes-variable-pitch-ui nil
+ standard-themes-mode-line-accented t
+ ;; Accepts a symbol value:
+ standard-themes-fringes 'intense
+ ;; The following accept lists of properties
+ standard-themes-links '(italic neutral-underline)
+ standard-themes-region '(no-extend neutral intense)
+ standard-themes-prompts '(bold italic)
+ standard-themes-headings (quote ((1 . (bold 1.2))
+ (2 . (regular 1.1))
+ (agenda-date (1.1))
+ (agenda-structure (regular 1.1)))))
+ (load-theme 'standard-light :no-confirm))
+#+end_src
+
+** time
+
+#+begin_src emacs-lisp
+(use-package time
+ :custom
+ (display-time-interval 60)
+ (display-time-mode t)
+ (display-time-24hr-format t)
+ (display-time-day-and-date t)
+ (display-time-default-load-average nil)
+ (world-clock-list t)
+ (world-clock-timer-enable t)
+ (world-clock-timer-second 60)
+ ;; UTC => 02:42 +0000 Wednesday 20 April
+ ;; Berkeley => 19:42 -0700 Tuesday 19 April
+ (world-clock-time-format "%R %z %A %d %B")
+ (zoneinfo-style-world-list '(("UTC" "UTC")
+ ("America/Los_Angeles" "Berkeley")
+ ("America/Denver" "Mountain Time")
+ ("America/Chicago" "Central Time")
+ ("America/New_York" "New York")
+ ("Europe/London" "London")
+ ("Europe/Paris" "Paris")))
+ :init
+ (add-to-list 'display-buffer-alist '("\\*wclock\\*"
+ (display-buffer-in-side-window)
+ (side . left)
+ (slot . 0)
+ (window-width . 0.35))))
+#+end_src
+
+** tramp
+
+#+begin_src emacs-lisp
+ (use-package tramp
+ :defer t
+ :custom
+ (tramp-default-method "ssh")
+ (tramp-auto-save-directory "~/.cache/emacs/backups")
+ (tramp-ssh-controlmaster-options "-o ControlMaster=auto -o ControlPath='tramp.%%C'")
+ :config
+ ;; Setting this with `:custom' does not take effect.
+ (setq tramp-persistency-file-name (user-data "tramp")))
+#+end_src
+
+** transient
+
+#+begin_src emacs-lisp
+(use-package transient
+ :defer t
+ :custom
+ (transient-history-file (user-data "transient/history.el"))
+ (transient-values-file (user-data "transient/values.el")))
+#+end_src
+
+** vc
+
+#+begin_src emacs-lisp
+(use-package vc
+ :defer t
+ :custom
+ (vc-command-messages t)
+ (vc-follow-symlinks t))
+#+end_src
+
+** which-key
+
+#+begin_src emacs-lisp
+(use-package which-key
+ :demand t
+ :diminish
+ :ensure t
+ :config
+ (which-key-mode))
+#+end_src
+
+** whitespace
+
+#+begin_src emacs-lisp
+(use-package whitespace
+ :diminish (global-whitespace-mode
+ whitespace-mode
+ whitespace-newline-mode)
+ :commands (whitespace-buffer
+ whitespace-cleanup
+ whitespace-mode
+ whitespace-turn-off)
+ :init
+ (dolist (hook '(prog-mode-hook text-mode-hook))
+ (add-hook hook #'whitespace-mode))
+ :custom
+ (whitespace-auto-cleanup t t)
+ (whitespace-rescan-timer-time nil t)
+ (whitespace-silent t t)
+ (whitespace-style '(face trailing space-before-tab))
+ :defines
+ (whitespace-auto-cleanup
+ whitespace-rescan-timer-time
+ whitespace-silent))
+#+end_src
+
+* Finalization
+#+begin_src emacs-lisp
+(report-time-since-load)
+
+;; Local Variables:
+;; byte-compile-warnings: (not docstrings lexical noruntime)
+;; End:
+#+end_src