;; config.el -*- lexical-binding: t; -*-
;; In interactive sessions, save a little IO time by skipping the
;; mtime checks on every *.elc file.
(setq load-prefer-newer noninteractive)
;; Optimise start-up time
(setq straight-check-for-modifications '(find-when-checking)
straight-enable-package-integration nil)
(defvar bootstrap-version)
(let ((bootstrap-file
(expand-file-name
"straight/repos/straight.el/bootstrap.el"
(or (bound-and-true-p straight-base-dir)
user-emacs-directory)))
(bootstrap-version 7))
(unless (file-exists-p bootstrap-file)
(with-current-buffer
(url-retrieve-synchronously
"https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el"
'silent 'inhibit-cookies)
(goto-char (point-max))
(eval-print-last-sexp)))
(load bootstrap-file nil 'nomessage))
;; Get use-package and integrate it with straight.el
(straight-use-package 'use-package)
(setq straight-use-package-by-default t)
;; Enable the :chords keyword in use-package
(use-package use-package-chords
:config (key-chord-mode t))
;; Get delight for the :delight keyword in use-package
(use-package delight)
(unless (executable-find "trash")
(warn "Command-line utility `trash' not found in PATH!"))
(defvar macos-trash-use-finder t
"Whether `trash' should use Finder to move files to Trash.")
(defun system-move-file-to-trash (filename)
"Move the file (or directory) named FILENAME to Trash."
(if macos-trash-use-finder
(call-process "trash" nil nil nil "-F" "--" filename)
(call-process "trash" nil nil nil "--" filename)))
(defconst have-gnu-ls
(let ((gls-path (executable-find "gls")))
(cond (gls-path
(setq insert-directory-program gls-path)
t)
(t
(warn "Failure to find GNU `ls' on the host system.")
nil)))
"Whether GNU `ls' is available on the system.")
(let ((personal-file (concat user-emacs-directory "personal.el")))
(when (file-exists-p personal-file)
(load personal-file nil nil 'nosuffix)))
(setq inhibit-startup-screen t
confirm-nonexistent-file-or-buffer t
ediff-window-setup-function 'ediff-setup-windows-plain)
(global-set-key (kbd "s-b") #'bury-buffer)
;; Disable transient history
(set 'transient-save-history nil)
(unless (eq window-system 'ns)
(menu-bar-mode -1))
(when (fboundp 'scroll-bar-mode)
(scroll-bar-mode -1))
(when (fboundp 'tool-bar-mode)
(tool-bar-mode -1))
(windmove-default-keybindings 'super)
(windmove-swap-states-default-keybindings)
(when (boundp 'ns-right-alternate-modifier)
(setq ns-right-alternate-modifier 'none))
(when (boundp 'ns-system-appearance-change-functions)
(add-hook
'ns-system-appearance-change-functions
(lambda (appearance)
(mapc #'disable-theme custom-enabled-themes)
(pcase appearance
('light
(load-theme 'tango t)
(load-theme 'tango-patch t))
('dark
(load-theme 'tango-dark t))))))
(use-package osx-location
:commands osx-location-watch
:config
(add-hook
'osx-location-changed-hook
(lambda ()
(setq calendar-latitude osx-location-latitude
calendar-longitude osx-location-longitude
calendar-location-name "current location"))))
Setting auto-save-default
and create-lockfiles
to nil
disables
them.
(setq backup-by-copying-when-linked t
backup-by-copying-when-mismatch t
backup-directory-alist `(("." . ,(concat user-emacs-directory "backups")))
delete-old-versions t
kept-new-versions 8
kept-old-versions 1
version-control t)
(setq auto-revert-use-notify t)
(global-auto-revert-mode t)
(setq save-place-version-control 'never)
(save-place-mode t)
(use-package recentf
:straight nil
:config
(dolist (path `(,(expand-file-name "../lisp" data-directory)
,(expand-file-name "straight" straight-base-dir)))
(add-to-list 'recentf-exclude (concat "^" (regexp-quote path))))
(recentf-mode t))
(setq completion-styles '(basic partial-completion)
completion-category-overrides '((file (styles basic substring))))
(setq read-buffer-completion-ignore-case
read-file-name-completion-ignore-case)
(use-package vertico
:config
(define-key vertico-map (kbd "C-M-i") #'vertico-insert)
(define-key vertico-map (kbd "TAB") #'minibuffer-complete)
(vertico-mode t))
(use-package marginalia
:init
(marginalia-mode))
(use-package embark
:bind (("C-." . embark-act)))
(use-package which-key
:delight which-key-mode
:config (which-key-mode t))
(global-set-key [remap list-buffers] #'ibuffer)
(use-package consult
:bind (("C-c f r" . consult-recent-file)
("C-c f d" . consult-find)
("C-c s g" . consult-git-grep)
("C-c s d" . consult-ripgrep)))
(use-package embark-consult
:after consult)
;; More often than not, files should end with a final newline
(setq require-final-newline t)
;; Maximum decoration unless otherwise specified later
(setq font-lock-maximum-decoration '((t . t)))
;; Highlight matching parenthesis
(show-paren-mode t)
;; Highlight some keywords
(use-package hl-todo
:config
(dolist (assoc hl-todo-keyword-faces)
(rplacd assoc (list :inherit 'hl-todo)))
(global-hl-todo-mode t)
:custom-face
(hl-todo ((t (:weight bold)))))
;; Prettify page break characters in Help buffers
(use-package page-break-lines
:hook (help-mode . page-break-lines-mode))
(setq kill-do-not-save-duplicates t
mouse-drag-and-drop-region 'meta
mouse-yank-at-point t
save-interprogram-paste-before-kill t)
;; Do not insert tabs by default
(setq-default indent-tabs-mode nil)
;; Do not hide tabs when deleting
(setq backward-delete-char-untabify-method 'hungry)
(use-package whitespace
:straight nil
:delight global-whitespace-mode
:config
(setq whitespace-global-modes '())
(global-whitespace-mode t))
(defconst highlight-tabs-style '(face tabs tab-mark)
"Whitespace highlighting when indenting with spaces.")
(defconst highlight-spaces-style
'(face indentation space-before-tab space-after-tab)
"Whitespace highlighting when indenting with tabs.")
(defun set-whitespace-style (style)
"Configure whitespace highlighting with STYLE."
(setq-local whitespace-style style)
(whitespace-turn-off)
(whitespace-turn-on-if-enabled))
(defun reset-whitespace-style ()
"Reset whitespace highlighting to highlight everything."
(interactive)
(set-whitespace-style (default-value 'whitespace-style)))
(defun indent-tabs-mode (&optional arg)
"Toggle or set `indent-tabs-mode'."
(interactive (list (or current-prefix-arg 'toggle)))
(let ((value
(if (eq arg 'toggle)
(not indent-tabs-mode)
(> (prefix-numeric-value arg) 0))))
(setq-local indent-tabs-mode value)
(set-whitespace-style
(if value highlight-spaces-style highlight-tabs-style))))
(defun indent-with-tabs ()
"Configure indentation with tabs."
(interactive)
(indent-tabs-mode 1))
(defun indent-with-spaces ()
"Configure indentation with spaces."
(interactive)
(indent-tabs-mode -1))
(use-package ws-butler
:delight ws-butler-mode
:config (ws-butler-global-mode t))
(use-package hippie-exp
:straight nil
:bind ("M-/" . hippie-expand)
:config
(setq hippie-expand-try-functions-list
(cl-set-difference hippie-expand-try-functions-list
'(try-expand-line try-expand-list))))
(use-package company
:defer 1
:delight company-mode
:config (global-company-mode))
(use-package avy
:chords (",." . avy-goto-symbol-1)
:bind (:map isearch-mode-map
("<C-return>" . avy-isearch)))
(use-package outline
:straight nil
:custom
(outline-minor-mode-cycle t))
(use-package paredit
:straight (paredit :depth full)
:demand t
:hook ((emacs-lisp-mode
eval-expression-minibuffer-setup
ielm-mode
lisp-mode
sly-mrepl-mode) . enable-paredit-mode))
(defun avy-kill-sexp (char &optional arg)
(interactive (list (read-char "char: " t)
current-prefix-arg))
(save-excursion
(avy-goto-char-2 ?\( char arg)
(kill-sexp)
(let ((col (current-column))
end)
(if (and (looking-at "[ \t]*$")
(<= col
(save-excursion
;; Move
(forward-line)
(back-to-indentation)
;; Save point
(setq end (point))
;; Return column
(current-column))))
(delete-region (point) end)
(just-one-space)))
(paredit-close-round)))
(defun avy-yank-sexp (char &optional arg)
(interactive (list (read-char "char: " t)
current-prefix-arg))
(save-excursion
(avy-goto-char-2 ?\( char arg)
(kill-ring-save (point)
(save-excursion
(forward-sexp)
(point)))))
;; Flycheck
(use-package flycheck
:hook (emacs-startup . global-flycheck-mode)
:config
(setq flycheck-global-modes '()
flycheck-check-syntax-automatically '(mode-enabled save)))
(use-package guess-language
:defer t
:commands guess-language
:hook (flyspell-mode . (lambda ()
(guess-language-mode (if flyspell-mode 1 -1))))
:config
(setq guess-language-languages '(en de hu la)
guess-language-langcodes
'((en "en_GB" "English")
(de "de_DE" "German")
(hu "hu_HU" nil)
(la "la" nil))))
;; Turn on Flyspell mode automatically in text modes when manually
;; asking to spell check a word. In addition, guess language before
;; checking the word.
(define-advice ispell-word (:before (&rest ignored) "flyspell-mode")
ignored
(when (and (not flyspell-mode) (derived-mode-p 'text-mode))
(flyspell-mode)
(guess-language)))
(defun set-ispell-dictionary ()
(interactive)
;; Disable guess language mode
(guess-language-mode -1)
;; Change dictionary interactively
(call-interactively #'ispell-change-dictionary))
(use-package langtool
:defer t
:bind (("s-;" . langtool-check)
("s-:" . langtool-check-done))
:config
(setq langtool-language-tool-server-jar
"/opt/homebrew/opt/languagetool/libexec/languagetool-server.jar"
langtool-server-user-arguments
`("--port" "8082" "--config"
,(expand-file-name
(concat user-emacs-directory "languagetool.properties")))))
(use-package dired
:straight nil
:init
(setq delete-by-moving-to-trash t)
:config
(setq dired-dwim-target t)
(when have-gnu-ls
(setq dired-listing-switches
(string-join '("-ahl" "-v" "--group-directories-first") " "))))
(use-package eshell
:straight nil
:bind ("s-t" . eshell)
:custom
(eshell-last-dir-ring-file-name nil))
(use-package magit
:commands magit-after-save-refresh-status
:bind
("C-x g" . magit-status)
:custom
(magit-delete-by-moving-to-trash nil)
(magit-diff-refine-hunk t)
:config
(add-hook 'after-save-hook #'magit-after-save-refresh-status))
(use-package forge
:after magit)
(setq mail-envelope-from 'header
mail-specify-envelope-from t
message-auto-save-directory nil
message-kill-buffer-on-exit t
send-mail-function 'sendmail-send-it
sendmail-program (executable-find "msmtp"))
(use-package notmuch
:bind ("C-c m" . notmuch)
:custom
(mail-user-agent 'notmuch-user-agent)
(read-mail-command 'notmuch)
(notmuch-draft-folder (format "%s/Drafts" user-mail-address))
(notmuch-fcc-dirs `((".*" . ,(format "%s/Sent" user-mail-address))))
(notmuch-search-oldest-first nil)
(notmuch-show-logo nil)
:init
(define-advice compose-mail (:before (&rest ignored) "notmuch")
ignored
(require 'notmuch)))
(use-package consult-notmuch
:after notmuch
:bind (:map notmuch-hello-mode-map
("s" . consult-notmuch)))
(use-package pdf-tools
:mode ("\\.[pP][dD][fF]\\'" . pdf-view-mode)
:magic ("%PDF" . pdf-view-mode)
:config
;; Enable hiDPI support, but at the cost of memory!
;; See politza/pdf-tools#51
(setq pdf-view-use-scaling t
pdf-view-use-imagemagick nil)
;; Add retina support for MacOS users
(when (eq system-type 'darwin)
(advice-add #'pdf-util-frame-scale-factor :around
#'+pdf--util-frame-scale-factor-a)
(advice-add #'pdf-view-use-scaling-p :before-until
#'+pdf--view-use-scaling-p-a)
(dolist (fn '(pdf-annot-show-annotation
pdf-isearch-hl-matches
pdf-view-display-region))
(advice-add fn :around #'+pdf--supply-width-to-create-image-calls-a)))
;; Install epdfinfo binary if needed
(unless (file-executable-p pdf-info-epdfinfo-program)
(pdf-tools-install))
(pdf-tools-install-noverify))
(eval-when-compile
(require 'pdf-tools))
(defun +pdf--util-frame-scale-factor-a (orig-fn)
(if (and pdf-view-use-scaling
(memq (pdf-view-image-type) '(imagemagick image-io))
(fboundp 'frame-monitor-attributes))
(funcall orig-fn)
;; Add special support for retina displays on MacOS
(if (and (eq (framep-on-display) 'ns)
(> emacs-major-version 26))
2
1)))
(defun +pdf--view-use-scaling-p-a ()
"Returns t if on ns window-system on Emacs 27+."
(and (eq (framep-on-display) 'ns)
(> emacs-major-version 26)
pdf-view-use-scaling))
(defun +pdf--supply-width-to-create-image-calls-a (orig-fn &rest args)
(let ((create-image (symbol-function #'create-image)))
(cl-letf
(((symbol-function #'create-image)
(lambda
(file-or-data &optional type data-p &rest props)
(apply create-image file-or-data type data-p
:width (car (pdf-view-image-size))
props))))
(ignore create-image)
(apply orig-fn args))))
;; Indent Emacs Lisp with spaces
(add-to-list 'whitespace-global-modes 'emacs-lisp-mode)
(add-hook 'emacs-lisp-mode-hook #'indent-with-spaces)
;; Limited decoration for Emacs Lisp code
(add-to-list 'font-lock-maximum-decoration '(emacs-lisp-mode . 1))
;; Common Lisp
(use-package sly
:defer t
:init
;; Use SBCL
(setq inferior-lisp-program "sbcl")
;; Hyperspec location
(setq common-lisp-hyperspec-root
"/usr/local/share/doc/hyperspec/HyperSpec/"
common-lisp-hyperspec-symbol-table
(concat common-lisp-hyperspec-root "Data/Map_Sym.txt")
common-lisp-hyperspec-issuex-table
(concat common-lisp-hyperspec-root "Data/Map_IssX.txt"))
:config
;; Sly completions
(setq sly-complete-symbol-function 'sly-simple-completions)
:custom
(org-babel-lisp-eval-fn 'sly-eval))
;; Indent Common Lisp with spaces
(add-to-list 'whitespace-global-modes 'lisp-mode)
(add-hook 'lisp-mode-hook #'indent-with-spaces)
;; Limited decoration for Common Lisp code
(add-to-list 'font-lock-maximum-decoration '(lisp-mode . 1))
;; Enable Outline minor mode
(add-hook 'lisp-mode-hook #'outline-minor-mode)
We need ol-notmuch
to be able to link e-mails, which is part of
org-contrib
, i.e., part of the official org-mode
repository, but
not part of Emacs. So we install org-contrib
which also pulls in
the core org
package, and enable the ol-notmuch
module.
(use-package org
:straight org-contrib
:bind (("C-c a" . org-agenda)
("C-c c" . org-capture)
("C-c l" . org-store-link))
:custom
(org-modules '(ol-docview ol-info ol-notmuch))
(org-image-actual-width nil)
:config
(add-hook 'org-mode-hook #'turn-on-auto-fill))
We use org-mime
to compose HTML e-mails.
(use-package org-mime
:bind (:map message-mode-hook
("C-c M-o" . org-mime-htmlize))
:config
(setq org-mime-export-options '(:with-latex dvipng
:section-numbers nil
:with-author nil
:with-toc nil)))
By default, org-mode
recognises the TODO
and DONE
keywords only.
We add another two types of actionable items:
NEXT
marks next actions according to GTD terminology. They denote actions where it is immediately obvious what to do and how; there is no need to further break them down.WAIT
marks actions to be done by someone else, that we are merely waiting for.
Thus TODO
remains for what GTD calls projects: tasks to be
accomplished that usually need a closer analysis.
(set 'org-todo-keywords nil)
(add-to-list 'org-todo-keywords
'(type "TODO" "NEXT" "WAIT" "|" "DONE")
'append)
For activities that happen or must be done on a specific day or at
specific times, such as meetings and appointments, we define TIME
and PAST
, so that they can be used as dependencies to broader tasks.
(add-to-list 'org-todo-keywords '(type "TIME" "|" "PAST") 'append)
To be able to dim or hide blocked actions, we must enforce TODO dependencies:
(setq org-enforce-todo-dependencies t)
We are going to use scheduling to stop thinking about certain tasks until a more opportune time.
(setq org-agenda-todo-ignore-scheduled 'future
org-agenda-todo-ignore-time-comparison-use-seconds t)
(setq org-log-done 'time)
For the incubator and the tickler, we add some more keywords:
REMIND
marks headings that come up at the daily review. It is meant to be scheduled, so the reminder comes up first on the specified day.WEEKLY
marks headings that come up at the weekly review. It may also be scheduled for further delay.CANCELLED
marks project that we no longer wish to pursue.
(add-to-list 'org-todo-keywords
'(sequence "REMIND" "WEEKLY" "|" "CANCELLED")
'append)
(setq org-agenda-start-on-weekday 0
org-agenda-window-setup 'other-window)
;; Disable buggy selection and revert mere completion when setting
;; org-agenda filter
(defun disable-selection (orig &optional arg)
(let ((vertico-map minibuffer-local-completion-map)
(completion-cycle-threshold nil)
(completion-styles '(basic)))
(funcall orig arg)))
(advice-add #'org-agenda-filter :around #'disable-selection)
(defun org-file (filename)
"Interpret relative file names relative to `org-directory'."
(if (file-name-absolute-p filename)
filename
(progn
;; Make sure that `org-directory' is defined
(unless (boundp 'org-directory)
(require 'org))
(concat (file-name-as-directory org-directory) filename))))
(setq org-default-notes-file
(org-file "notes.org")
org-agenda-files
(mapcar #'org-file '("calendar/"
"incubator.org"
"ongoing.org"
"recurring.org"
"upcoming.org"))
org-archive-location
(org-file "archive/default.org::* From %s")
org-attach-id-dir
(org-file "data/"))
Note: setting the default notes file is actually superfluous if
org-capture-templates
is properly configured.
Not being familiar with Emacs bookmarks, we disable org-capture
from
messing with them.
(setq org-capture-bookmark nil)
(setq org-capture-templates
'(("t" "Task" entry (file "ongoing.org")
"* TODO %?")
("r" "Reminder" entry (file "incubator.org")
"* REMIND %?")
("n" "Note" item (file+olp "notes.org" "Inbox")
"- %i%? (%a)")
("s" "Scripture reading" item (file+olp "notes.org" "Scripture reading record")
"- %? %u")
("c" "Calendar entry" entry (file+olp "upcoming.org" "Miscellaneous")
"* %?\n%^{Calendar capture.}t")))
org-capture
saves the target file after committing the captured item
(unless the template is set up never to save the file). If there
are already unsaved changes in the target buffer, they would be saved
silently. To offer the user a choice, we advise
org-capture-target-buffer
:
(define-advice org-capture-target-buffer (:filter-return (target-buffer))
;; Offer to save buffer if there are unsaved changes
(save-some-buffers nil #'(lambda () (eq target-buffer (current-buffer))))
;; Just return target buffer!
target-buffer)
We define the following agenda views:
- Immediate view
- Reminders from the “tickler”, 3-day overview, next actions list, current “waiting for” entries, and any stuck projects.
- Daily view
- Detailed calendar of the day.
- Weekly review
- Fortnight overview, list of active projects, weekly reminders.
(defun default-org-agenda-skipper ()
"Skip incubator entries and scheduled entries."
(or (org-agenda-skip-entry-if 'todo '("REMIND" "WEEKLY"))
(org-agenda-skip-entry-if 'scheduled)))
(setq org-agenda-custom-commands
'(("i" "Immediate view"
((agenda "" ((org-agenda-span 'day)
(org-agenda-skip-function #'default-org-agenda-skipper)))
(tags-todo "today" ((org-agenda-overriding-header "For today:")))))
("d" "Daily review"
((todo "REMIND" ((org-agenda-overriding-header "Reminders:")
(org-agenda-dim-blocked-tasks 'invisible)))
(agenda "" ((org-agenda-span 3)
(org-agenda-skip-function #'default-org-agenda-skipper)
(org-agenda-use-time-grid nil)))
(todo "NEXT" ((org-agenda-overriding-header "Outstanding actions:")))
(todo "TIME|WAIT" ((org-agenda-overriding-header "Waiting for:")
(org-agenda-todo-ignore-timestamp 'future)))
(todo "TODO" ((org-agenda-overriding-header "Stuck projects:")
(org-agenda-dim-blocked-tasks 'invisible))))
((org-agenda-tag-filter-preset '("-everyday"))))
("w" "Weekly review"
((agenda "" ((org-agenda-span 'fortnight)
(org-agenda-skip-function #'default-org-agenda-skipper)
(org-agenda-use-time-grid nil)))
(todo "TODO" ((org-agenda-overriding-header "Active projects:")))
(todo "WEEKLY" ((org-agenda-overriding-header "Weekly reminders:"))))
((org-agenda-tag-filter-preset '("-everyday"))))))
(add-hook
'org-mode-hook
(lambda ()
(setq-local langtool-disabled-rules
'("WHITESPACE_RULE"
"DE_CASE"
"DE_DU_UPPER_LOWER"
"FALSCHE_VERWENDUNG_DES_BINDESTRICHS"
"GERMAN_SPELLER_RULE"
"LEERZEICHEN_HINTER_DOPPELPUNKT"))))
(use-package hydra
:defer t)
(use-package org-fc
:straight
(org-fc
:type git :repo "https://git.sr.ht/~l3kn/org-fc"
:files (:defaults "awk" "demo.org"))
:config
(require 'org-fc-hydra))
(use-package org-roam
:bind (("C-c n l" . org-roam-buffer-toggle)
("C-c n f" . org-roam-node-find)
("C-c n g" . org-roam-graph)
("C-c n i" . org-roam-node-insert)
("C-c n c" . org-roam-capture)
;; Dailies
("C-c n j" . org-roam-dailies-capture-today))
:config
;; If you're using a vertical completion framework, you might want a
;; more informative completion interface
(setq org-roam-node-display-template
(concat "${title:*} " (propertize "${tags:10}" 'face 'org-tag)))
(org-roam-db-autosync-mode))
;; Language server protocol settings
(use-package lsp-mode
:commands lsp
:init
(setq read-process-output-max (* 1024 1024))
(setq lsp-diagnostics-provider :none
lsp-modeline-diagnostics-enable nil))
(use-package lsp-ui
:after lsp-mode
:init (setq lsp-ui-doc-enable nil))
(eval-when-compile
(require 'cc-vars))
(eval-after-load 'cc-vars
(lambda ()
(setcdr (assoc 'other c-default-style) "linux")
(add-to-list 'whitespace-global-modes 'c-mode)
(add-hook 'c-mode-common-hook #'indent-with-tabs)
(add-hook 'c-mode-common-hook #'electric-pair-local-mode)))
;; Use lsp-mode
(add-hook 'python-mode-hook #'lsp)
;; Use Flycheck, but not with pylint
(eval-after-load 'flycheck
(lambda ()
(add-to-list 'flycheck-global-modes 'python-mode)
(push 'python-pylint (default-value 'flycheck-disabled-checkers))))
;; Python environment
(use-package pyvenv
:defer t
:init (setenv "WORKON_HOME" "~/.pyenv/versions")
:config (pyvenv-mode t))
;; Emacs IPython Notebook
(use-package ein
:defer t)
(use-package js
:straight nil
:defer t
:custom
(js-indent-level 2))
(use-package js2-mode
:mode ("\\.js\\'" . js2-mode)
:init
(add-hook 'js2-mode-hook #'lsp))
(use-package auctex
:defer t
:init
;; Ask AUCTeX to use PDFLaTeX
(setq TeX-PDF-mode t))
(use-package zig-mode
:defer t
:init
(add-hook 'zig-mode-hook #'lsp)
(add-hook 'zig-mode-hook #'electric-pair-local-mode))
;; Zig language server
(eval-after-load 'lsp-mode
(lambda ()
(setq lsp-zig-zls-executable
(expand-file-name "zls/zls" lsp-server-install-dir))))
(let ((opam-share
(eval-when-compile
(ignore-errors
(car (process-lines "opam" "var" "share"))))))
(when (and opam-share (file-directory-p opam-share))
;; Add opam load path
(add-to-list 'load-path (expand-file-name "emacs/site-lisp" opam-share))))
(use-package tuareg
:defer t)
(use-package merlin
:straight nil
:hook ((tuareg-mode . merlin-mode))
:custom
;; Use opam switch to lookup ocamlmerlin binary
(merlin-command 'opam))
;; Writable grep buffers
(use-package wgrep
:defer t)
;; Yet Another Markup Language
(use-package yaml-mode
:defer t)
;; Gregorio GABC files
(use-package gregorio-mode
:defer t)
(when (fboundp 'post-init-personal-settings)
(post-init-personal-settings))
(setq custom-file (concat user-emacs-directory "custom.el"))
(load custom-file)
(use-package server
:config
(unless (server-running-p)
(server-start)))
(eval-when-compile
(require 'ob-tangle))
(defun tangle-config.org ()
"If the current buffer is `config.org', tangle it!"
(when (equal (file-truename (buffer-file-name))
(expand-file-name "config.org" user-emacs-directory))
(org-babel-tangle)))
(add-hook 'after-save-hook #'tangle-config.org)
(add-hook 'after-revert-hook #'tangle-config.org)
This approach is better than file-local hooks, because it also works
with "C-x s"
as well as revert-buffer
.