#+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))
:preface
;; see https://github.com/minad/corfu#completing-in-the-eshell-or-shell
(defun corfu-send-shell (&rest _)
"Send completion candidate when inside comint/eshell."
(cond
((and (derived-mode-p 'eshell-mode) (fboundp 'eshell-send-input))
(eshell-send-input))
((and (derived-mode-p 'comint-mode) (fboundp 'comint-send-input))
(comint-send-input))))
: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
:init
;; see https://github.com/minad/corfu#completing-in-the-eshell-or-shell
(add-hook 'eshell-mode-hook
(lambda ()
(setq-local corfu-auto nil)
(corfu-mode)))
(advice-add #'corfu-insert :after #'corfu-send-shell)
: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)
#+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 () (setq tab-width 4)))
(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
:ensure t
: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