Initial commit

This commit is contained in:
BirDt_ 2025-08-17 23:40:44 +08:00
commit 2a28d0b97a
21 changed files with 132395 additions and 0 deletions

24
.gitignore vendored Normal file
View file

@ -0,0 +1,24 @@
# Remove Emacs garbage
*~
\#*\#
# Internal config files
.cache/
eln-cache/
elpa/
games/
ielm-history.eld
org-roam.db
org/
places
projects
recentf
straight/*
tramp
transient/
url/
.org-id-locations
bookmarks
# Custom file
custom.el

23
README.org Normal file
View file

@ -0,0 +1,23 @@
#+title: Theurgy
Theurgy is an Emacs distribution.
The focus is to make Emacs a complete, distributable, consistent toolbox.
Additionally, good UI/UX should be provided on a per-workflow basis. Theurgy is tab oriented, with each tab intended to focus on a different workflow.
Theurgy is very opinionated, but easy to modify.
* Basics
The following is set up across all workflows.
** Dashboard
This is the first present screen when opening Emacs or opening a new tab.
** Burly
For frame and window configuration management.
* Workflows
** Exobrain
This is the main and default workflow, providing tools for note taking, note storage, and information aggregation.

2
early-init.el Normal file
View file

@ -0,0 +1,2 @@
;; Disable package.el in favor of straight.el
(setq package-enable-at-startup nil)

129704
en_AU.dict Normal file

File diff suppressed because it is too large Load diff

81
init.el Normal file
View file

@ -0,0 +1,81 @@
;; Fixes a MELPA 403
(setq gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3")
;; Show errors at minimum
(setq warning-minimum-level :error)
;;; Utility functions
;; https://emacs.stackexchange.com/a/18441
(defun load-directory (directory)
"Load recursively all `.el' files in DIRECTORY."
(dolist (element (directory-files-and-attributes directory nil nil nil))
(let* ((path (car element))
(fullpath (concat directory "/" path))
(isdir (car (cdr element)))
(ignore-dir (or (string= path ".") (string= path ".."))))
(cond
((and (eq isdir t) (not ignore-dir))
(load-directory fullpath))
((and (eq isdir nil) (string= (substring path -3) ".el"))
(load (file-name-sans-extension fullpath)))))))
;;; Package Repositories
(require 'package)
(add-to-list 'package-archives '("gnu" . "https://elpa.gnu.org/packages/"))
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(add-to-list 'package-archives '("nongnu" . "https://elpa.nongnu.org/nongnu/") t)
(package-initialize)
;; Install straight.el
(defvar bootstrap-version)
(let ((bootstrap-file
(expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
(bootstrap-version 6))
(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))
;; Install use-package
(straight-use-package 'use-package)
;; Use straight by default
(setq straight-use-package-by-default t)
;; Always ensure packages to install them automatically
(require 'use-package-ensure)
(setq use-package-always-ensure t)
;; TODO: https://github.com/joaotavora/eglot/discussions/1487
(use-package eldoc :straight (:type built-in))
(use-package org :straight (:type built-in))
(setq inhibit-startup-screen t
inhibit-startup-message t
inhibit-startup-echo-area-message t
initial-scratch-message nil)
;; Shared library packages
(load (concat user-emacs-directory "shared-packages.el"))
;; UI and display related packages
(load (concat user-emacs-directory "ui.el"))
;; Writing behavior and packages
(load (concat user-emacs-directory "writing.el"))
;; Navigation behavior and packages
(load (concat user-emacs-directory "navigation.el"))
;; Window rules
(load (concat user-emacs-directory "window-utils.el"))
;; Userland application replacements
(load-directory (concat user-emacs-directory "userland"))
;; Mode/workflow level configuration
(load-directory (concat user-emacs-directory "workflows"))
;; Custom screens
(load-directory (concat user-emacs-directory "screens"))
;; Use a custom file instead of putting customisations in init.el
(setq custom-file (concat user-emacs-directory "custom.el"))
(when (file-exists-p custom-file) (load custom-file))

88
navigation.el Normal file
View file

@ -0,0 +1,88 @@
;;; navigation.el --- Navigation behavior config -*- lexical-binding: t -*-
;; This file is not part of GNU Emacs
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; commentary
;;; Code:
;; Smooth scrolling
(setq bidi-paragraph-direction 'left-to-right
bidi-inhibit-bpa t
bidi-display-reordering nil)
(use-package ultra-scroll
:straight (:host github
:repo "jdtsmith/ultra-scroll")
:init
(setq scroll-conservatively 3 ; or whatever value you prefer, since v0.4
scroll-margin 0) ; important: scroll-margin>0 not yet supported
:config
(ultra-scroll-mode 1))
;; Keyboard Scrolling
(global-set-key (kbd "C-<down>") '(lambda () (interactive) (scroll-up-line)))
(global-set-key (kbd "C-<up>") '(lambda () (interactive) (scroll-down-line)))
(global-set-key (kbd "C-S-<down>") '(lambda () (interactive) (scroll-up-line 10)))
(global-set-key (kbd "C-S-<up>") '(lambda () (interactive) (scroll-down-line 10)))
(global-set-key (kbd "M-<up>") #'scroll-other-window-down)
(global-set-key (kbd "M-<down>") #'scroll-other-window)
;; Windmove bindings
(global-set-key (kbd "C-c <left>") 'windmove-left)
(global-set-key (kbd "C-c <right>") 'windmove-right)
(global-set-key (kbd "C-c <up>") 'windmove-up)
(global-set-key (kbd "C-c <down>") 'windmove-down)
;; Fullscreen and maximize button
(global-set-key (kbd "C-x <f10>") 'toggle-frame-maximized)
(global-set-key (kbd "C-x <f11>") 'toggle-frame-fullscreen)
;; Line jumps
(defun jump-to-line-absolute ()
(interactive)
(let ((p display-line-numbers))
(setq display-line-numbers t)
(call-interactively 'goto-line)
(setq display-line-numbers p)))
(defun relative-line-jump-helper (relative-line)
(interactive "nGoto line: ")
(goto-line (+ (line-number-at-pos) relative-line) '() t))
(defun jump-to-line-relative ()
(interactive)
(let ((p display-line-numbers))
(setq display-line-numbers 'relative)
(call-interactively 'relative-line-jump-helper)
(setq display-line-numbers p)))
(global-set-key (kbd "M-g g") 'jump-to-line-absolute)
(global-set-key (kbd "M-g r") 'jump-to-line-relative)
;; Eglot and flymake bindings
(global-set-key (kbd "M-g <left>") 'flymake-goto-prev-error)
(global-set-key (kbd "M-g <right>") 'flymake-goto-next-error)
(global-set-key (kbd "C-x M-f") 'eglot-code-actions)
(provide 'navigation)
;;; navigation.el ends here

149
screens/dashboard.el Normal file
View file

@ -0,0 +1,149 @@
;;; dashboard.el --- A grid-based dashboard -*- lexical-binding: t -*-
;; Author: Jakub
;; Maintainer: Jakub
;; Version: 1.0
;; Package-Requires: (grid straight)
;; This file is not part of GNU Emacs
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; commentary
;;; Code:
;; For layout
(require 'grid)
(defvar enlight-theurgy-logo
(propertize
"\n\n\n\n ###########
##### ##### #####
#### ## ### ####
### ### ## ###
## ### ### ##
## ############### ##
### ## ### ### ## ###
## ##### ##### ##
## ## ## ## ## ##
### ### ### ### ## ###
##### # ### ### # #####
#############################
### ###
#### ####
##### #####
########### "
'face 'font-lock-keyword-face))
(defvar enlight-calendar
(progn
(calendar)
(prog1 (with-current-buffer (buffer-name (current-buffer))
(buffer-string))
(calendar-exit))))
(defun get-weather ()
"Get the weather JSON for THEURGY-CITY."
(let ((buf (url-retrieve-synchronously (concat "https://wttr.in/" theurgy-city "?format=j1"))))
(when buf
(with-current-buffer buf
;; Skip HTTP headers
(goto-char (point-min))
(re-search-forward "\n\n" nil 'move)
(delete-region (point-min) (point))
;; Now display it
(switch-to-buffer buf)
(buffer-string)))))
(defun connected-p ()
"Are we connected to the host?"
(= 0 (call-process-shell-command "ping -W 5 -c 2 wttr.in")))
(defun enlight-weather ()
(if (connected-p)
(progn (let ((weather (aref (gethash "current_condition"
(json-parse-string (get-weather) :object-type 'hash-table)) 0)))
(concat (propertize "Weather\n\n" 'face 'font-lock-keyword-face)
"Currently " (gethash "value" (aref (gethash "weatherDesc" weather) 0)) "\n"
"It is " (gethash "temp_C" weather) " degrees, feels like " (gethash "FeelsLikeC" weather) "\n"
"Wind speed is " (gethash "windspeedKmph" weather) "km/h\n")))
(concat (propertize "Weather\n\n" 'face 'font-lock-keyword-face)
"No connection to weather service :(")))
;; Dashboard screen using enlight
(use-package enlight
:custom
(enlight-content
(concat
(grid-get-box `(:align center :content ,enlight-theurgy-logo :width 80))
(grid-get-row
(list
(grid-get-box
`(:content ,(concat
(grid-get-box
`( :content
,(concat
(grid-get-box `( :content ,(propertize "Theurgy Emacs" 'face 'variable-pitch-serif-text)
:width 80 :align center)))
:width 80))
enlight-calendar "\n\n"
(grid-get-row
(list (grid-get-box `(:content ,(concat
(enlight-menu
'(("Exobrain"
("Agenda" (org-agenda nil "a") "a")
("Go to Inbox" open-inbox "i")
("Capture" org-capture "c")))))
:align left
:width 20))
(grid-get-box `(:content ,(concat
(enlight-menu
'(("Projects"
("Open" open-project "o")
("Project List" open-inbox "p")))))
:align center
:width 20))
(grid-get-box `(:content ,(concat
(enlight-menu
'(("Userland"
("Dired" (dired "~") "d")
("RSS" elfeed "r")
("Terminal" theurgy-bottom-shell "t")
("Weather" theurgy-show-weather "w")))))
:align center
:width 20))
(grid-get-box `(:content ,(concat
(enlight-menu
'(("Meta"
("Elisp Scratch" open-elisp-scratch "s")
("Org Scratch" open-org-scratch "o")
("Init Dir" (dired user-emacs-directory) "e")
("Info" info "h")))))
:align right
:width 20)))))
))
))
;(grid-get-box `(:content ,(enlight-weather) :align center))
)))
(setopt initial-buffer-choice #'enlight)
(provide 'dashboard)
;;; dashboard.el ends here

44
shared-packages.el Normal file
View file

@ -0,0 +1,44 @@
;;; shared-packages.el --- Shared library packages -*- lexical-binding: t -*-
;; This file is not part of GNU Emacs
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; commentary
;;; Code:
;; We use this for displaying text a bit nicer in some buffers.
(use-package grid
:straight (grid :type git :host github :repo "ichernyshovvv/grid.el"))
;; This is used by nano
(use-package mini-frame)
;; Hydro, used by Treemacs
(use-package hydra)
;; All the icons
(use-package all-the-icons
:if (display-graphic-p))
;; Transient
(use-package transient)
(provide 'shared-packages)
;;; shared-packages.el ends here

180
ui.el Normal file
View file

@ -0,0 +1,180 @@
;;; ui.el --- UI components and packages -*- lexical-binding: t -*-
;; This file is not part of GNU Emacs
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; commentary
;;; Code:
;; Ouroboros themes
(straight-use-package
'(ouroboros-emacs-themes :type git
:host nil
:repo "https://git.cyan.sh/BirDt/ouroboros-emacs-themes.git"
:files ("*.el")))
;; Nano for making emacs look more slim
(straight-use-package
'(nano :type git :host github :repo "rougier/nano-emacs"))
;; Add ouroboros-emacs-themes to the theme load path
(add-to-list 'custom-theme-load-path
(expand-file-name (concat "straight/" straight-build-dir "/ouroboros-emacs-themes/") user-emacs-directory))
;; We need this for other nano stuff
(require 'nano-faces)
(require 'nano-modeline)
;; Default frame values - NOTE this is buggy when not launching emacsclient, but everyone should be using that anyway :)
(setq default-frame-alist
(append (list
'(min-height . 1)
'(height . 45)
'(min-width . 1)
'(width . 81)
'(vertical-scroll-bars . nil)
'(internal-border-width . 24)
'(left-fringe . 1)
'(right-fringe . 1)
'(tool-bar-lines . 0)
'(menu-bar-lines . 1))))
;; Smart line wrapping
(global-visual-line-mode)
;; Minibuffer must shrink
(setq resize-mini-windows nil
max-mini-window-height 1)
;; Use y instead of yes, etc
(if (>= emacs-major-version 29)
(setopt use-short-answers t)
(defalias 'yes-or-no-p 'y-or-n-p))
;; Display line numbers in programming modes
(add-hook 'prog-mode-hook #'display-line-numbers-mode)
;; Hide toolbar
(tool-bar-mode -1)
;; Don't show scrollbars
(scroll-bar-mode -1)
;; Don't show menu bar
(menu-bar-mode -1)
;; No tooltips
(tooltip-mode -1)
;; No ugly button for checkboxes
(setq widget-image-enable nil)
;; Don't pop up UI dialogs
(setq use-dialog-box nil)
;; Tabs
(when (< 26 emacs-major-version)
(tab-bar-mode 1) ;; enable tab bar
(global-unset-key (kbd "C-t"))
(define-key global-map (kbd "C-t 2") 'tab-new)
(define-key global-map (kbd "C-t 0") 'tab-close)
(setq tab-bar-close-button-show nil) ;; hide tab close / X button
(setq tab-bar-new-tab-choice "*enlight*");; buffer to show in new tabs
(setq tab-bar-tab-hints t) ;; show tab numbers
(setq tab-bar-format '(tab-bar-format-tabs tab-bar-separator))) ;; elements to include in bar
;; Hideshow
(add-hook 'prog-mode-hook #'hs-minor-mode)
;; Custom text-scale function with default scale
(defun set-default-text-scale (&optional default-scale)
(interactive)
(let ((default-scale (or default-scale 0)))
(text-scale-set default-scale)))
;; Keybindings for text-scale
(global-set-key (kbd "C-=") 'text-scale-increase)
(global-set-key (kbd "C--") 'text-scale-decrease)
(global-set-key (kbd "C-0") '(lambda () (interactive) (set-default-text-scale)))
;; Beacon to show the cursor
(use-package beacon
:config
(beacon-mode 1)
(setq beacon-color "#D5D5D5"))
;; Burly for frame layout saving
(use-package burly)
;; Highlight to-do
(use-package hl-todo
:straight (hl-todo :type git :host github :repo "tarsius/hl-todo")
:config (global-hl-todo-mode 1))
;; Completion
(use-package corfu
:init
;; Enable Corfu globally.
(global-corfu-mode)
;; Popup info
(corfu-popupinfo-mode)
:custom
(corfu-min-width 80)
(corfu-max-width corfu-min-width)
:config
(setq corfu-auto t
corfu-quit-no-match t
corfu-auto-delay 0
corfu-auto-prefix 0
corfu-popupinfo-delay 0)
(add-hook 'corfu-mode-hook
(lambda ()
;; Settings only for Corfu
(setq-local completion-styles '(basic)
completion-category-overrides nil
completion-category-defaults nil)))
(keymap-set corfu-map "RET" `( menu-item "" nil :filter
,(lambda (&optional _)
(and (derived-mode-p 'eshell-mode 'comint-mode)
#'corfu-send))))
)
;; Icons in corfu
(use-package kind-icon
:after corfu
:custom
(kind-icon-use-icons t)
(kind-icon-default-face 'corfu-default) ; Have background color be the same as `corfu' face background
(kind-icon-blend-background nil) ; Use midpoint color between foreground and background colors ("blended")?
(kind-icon-blend-frac 0.08)
:config
(add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter) ; Enable `kind-icon'
)
;; Flycheck is here
(use-package flycheck
:hook ((after-init . global-flycheck-mode)))
(provide 'ui)
;;; ui.el ends here

58
userland/browser.el Normal file
View file

@ -0,0 +1,58 @@
;;; browser.el --- Web browser configuration and shortcuts -*- lexical-binding: t -*-
;; This file is not part of GNU Emacs
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; commentary
;;; Code:
(defcustom theurgy-city
"Perth"
"The city used for fetching weather from wttr.in."
:type 'string
:group 'theurgy
:group 'theurgy-weather)
(defun theurgy-eww-rename-buffer ()
"Rename the eww buffer intelligently."
(when (eq major-mode 'eww-mode)
(let ((url (plist-get eww-data :url)))
(cond
((string-match-p "wttr.in" url) "*weather*")
(t "*eww*")))))
(setq eww-auto-rename-buffer #'theurgy-eww-rename-buffer)
(defun theurgy-show-weather ()
"Show wttr.in in an eww buffer."
(interactive)
(if (get-buffer "*weather*")
(switch-to-buffer "*weather*")
(eww (concat "wttr.in/" theurgy-city "?0"))))
(add-to-list 'display-buffer-alist
'("\\*weather\\*"
(display-buffer-in-side-window)
(side . left)
(slot . 4)
(window-width . 0.15)))
(provide 'browser)
;;; browser.el ends here

104
userland/dired-custom.el Normal file
View file

@ -0,0 +1,104 @@
;;; dired-custom.el --- Custom dired config -*- lexical-binding: t -*-
;; This file is not part of GNU Emacs
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; commentary
;;; Code:
(require 'dired)
(setq dired-listing-switches "-lh")
(setq dired-recursive-deletes t)
(setq dired-recursive-copies t)
(defun dired-init ()
"Theurgy Dired config."
;; Hide file permissions
(dired-hide-details-mode 1)
;; Kill new buffers
(when (>= emacs-major-version 28)
(setq dired-kill-when-opening-new-dired-buffer t))
(when (< emacs-major-version 28)
(progn
(define-key dired-mode-map (kbd "RET") 'dired-find-alternate-file)
(define-key dired-mode-map (kbd "^") (lambda () (interactive) (find-alternate-file "..")))))
;; Human readable file size
(setq dired-listing-switches "-lh"))
(add-hook 'dired-mode-hook 'dired-init)
(add-hook 'dired-mode-hook 'auto-revert-mode)
;; Icons in dired
(use-package all-the-icons-dired
:config
(add-hook 'dired-mode-hook 'all-the-icons-dired-mode))
;; Multimedia and PDF viewing
(when (not (equal system-type 'windows-nt))
(use-package ready-player
:ensure t
:config
(setq ready-player-autoplay nil)
(ready-player-mode 1))
(use-package pdf-tools
:config (pdf-loader-install)))
;; Preview files
(defun dired-preview-to-the-right ()
"My preferred `dired-preview-display-action-alist-function'."
'((display-buffer-in-side-window)
(side . right)
(slot . 0)
(window-width . 0.3)))
(use-package dired-preview
:after (ready-player)
:config
(setq dired-preview-display-action-alist #'dired-preview-to-the-right)
(setq dired-preview-buffer-name-indicator "[Preview]")
(setq dired-preview-ignored-extensions-regexp
(concat "\\."
"\\(gz\\|"
"zst\\|"
"tar\\|"
"xz\\|"
"rar\\|"
"zip\\|"
"iso\\|"
"epub"
"\\)"))
(dired-preview-global-mode 1))
;; Automatically kill preview buffers when opening a file
(add-hook 'find-file-hook (lambda ()
(dolist (buf (buffer-list))
(when (string-match-p "\\[Preview\\]" (buffer-name buf))
(kill-buffer buf)))))
(use-package neotree
:config
(define-key global-map (kbd "<f8>") 'neotree-toggle)
(setq neo-theme (if (display-graphic-p) 'icons 'arrow)))
(provide 'dired-custom)
;;; dired-custom.el ends here

View file

@ -0,0 +1,104 @@
;;; dired-custom.el --- Custom dired config -*- lexical-binding: t -*-
;; This file is not part of GNU Emacs
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; commentary
;;; Code:
(require 'dired)
(setq dired-listing-switches "-lh")
(setq dired-recursive-deletes t)
(setq dired-recursive-copies t)
(defun dired-init ()
"Theurgy Dired config."
;; Hide file permissions
(dired-hide-details-mode 1)
;; Kill new buffers
(when (>= emacs-major-version 28)
(setq dired-kill-when-opening-new-dired-buffer t))
(when (< emacs-major-version 28)
(progn
(define-key dired-mode-map (kbd "RET") 'dired-find-alternate-file)
(define-key dired-mode-map (kbd "^") (lambda () (interactive) (find-alternate-file "..")))))
;; Human readable file size
(setq dired-listing-switches "-lh"))
(add-hook 'dired-mode-hook 'dired-init)
(add-hook 'dired-mode-hook 'auto-revert-mode)
;; Icons in dired
(use-package all-the-icons-dired
:config
(add-hook 'dired-mode-hook 'all-the-icons-dired-mode))
;; Multimedia and PDF viewing
(when (not (equal system-type 'windows-nt))
(use-package ready-player
:ensure t
:config
(setq ready-player-autoplay nil)
(ready-player-mode 1))
(use-package pdf-tools
:config (pdf-loader-install)))
;; Preview files
(defun dired-preview-to-the-right ()
"My preferred `dired-preview-display-action-alist-function'."
'((display-buffer-in-side-window)
(side . right)
(slot . 0)
(window-width . 0.3)))
(use-package dired-preview
:after (ready-player)
:config
(setq dired-preview-display-action-alist #'dired-preview-to-the-right)
(setq dired-preview-buffer-name-indicator "[Preview]")
(setq dired-preview-ignored-extensions-regexp
(concat "\\."
"\\(gz\\|"
"zst\\|"
"tar\\|"
"xz\\|"
"rar\\|"
"zip\\|"
"iso\\|"
"epub"
"\\)"))
(dired-preview-global-mode 1))
;; Automatically kill preview buffers when opening a file
(add-hook 'find-file-hook (lambda ()
(dolist (buf (buffer-list))
(when (string-match-p "\\[Preview\\]" (buffer-name buf))
(kill-buffer buf)))))
(use-package neotree
:config
(define-key global-map (kbd "<f8>") 'neotree-toggle)
(setq neo-theme (if (display-graphic-p) 'icons 'arrow)))
(provide 'dired-custom)
;;; dired-custom.el ends here

40
userland/git-interace.el Normal file
View file

@ -0,0 +1,40 @@
;;; git-interface.el --- Magit config -*- lexical-binding: t -*-
;; This file is not part of GNU Emacs
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; commentary
;;; Code:
(use-package magit)
(use-package magit-todos
:after (magit)
:config (magit-todos-mode 1))
(add-to-list 'display-buffer-alist
'("magit:"
(display-buffer-in-side-window)
(side . right)
(slot . 4)
(window-width . 0.2)))
(provide 'git-interface)
;;; git-interface.el ends here

69
userland/rss.el Normal file
View file

@ -0,0 +1,69 @@
;;; rss.el --- RSS feed reading via elfeed -*- lexical-binding: t -*-
;; This file is not part of GNU Emacs
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; commentary
;;; Code:
;; Elfeed and aggregation setup
(use-package elfeed
:config
(setq elfeed-feeds
'(;; Planet aggregators
("https://planet.emacslife.com/atom.xml" planet emacs))))
;; This fixes a bug
(define-advice elfeed-search--header (:around (oldfun &rest args))
(if elfeed-db
(apply oldfun args)
"No database loaded yet"))
;; Display the elfeed entry buffer in the main window
(setq elfeed-show-entry-switch #'elfeed-display-buffer)
(defun elfeed-display-buffer (buf &optional act)
(pop-to-buffer buf)
(delete-other-main-windows))
;; Navigate elfeed entry from the search window
(defun elfeed-search-show-entry-pre (&optional lines)
"Returns a function to scroll forward or back in the Elfeed
search results, displaying entries without switching to them."
(lambda (times)
(interactive "p")
(forward-line (* times (or lines 0)))
(recenter)
(call-interactively #'elfeed-search-show-entry)
(select-window (previous-window))
(unless elfeed-search-remain-on-entry (forward-line -1))))
(define-key elfeed-search-mode-map (kbd "n") (elfeed-search-show-entry-pre +1))
(define-key elfeed-search-mode-map (kbd "p") (elfeed-search-show-entry-pre -1))
(define-key elfeed-search-mode-map (kbd "M-RET") (elfeed-search-show-entry-pre))
(add-to-list 'display-buffer-alist
'("\\*elfeed-search\\*"
(display-buffer-in-side-window)
(side . top)
(slot . 0)))
(provide 'rss)
;;; rss.el ends here

88
userland/scratch.el Normal file
View file

@ -0,0 +1,88 @@
;;; scratch.el --- Custom scratchpads -*- lexical-binding: t -*-
;; This file is not part of GNU Emacs
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; commentary
;;; Code:
;; We have our own scratch buffers, don't need this
(add-hook 'emacs-startup-hook (lambda ()
(when (get-buffer "*scratch*")
(kill-buffer "*scratch*"))))
(defcustom elisp-scratch-initial-contents
";; Emacs lisp scratch buffer\n\n"
"Initial text for *elisp scratch*."
:type 'string
:group 'theurgy
:group 'theurgy-scratch)
(defcustom org-scratch-initial-contents
"# Org scratch buffer\n\n"
"Initial text for *org scratch*."
:type 'string
:group 'theurgy
:group 'theurgy-scratch)
(with-current-buffer (generate-new-buffer "*elisp scratch*")
(insert elisp-scratch-initial-contents)
(emacs-lisp-mode))
(with-current-buffer (generate-new-buffer "*org scratch*")
(insert org-scratch-initial-contents)
(org-mode))
(defun open-elisp-scratch ()
(interactive)
(switch-to-buffer "*elisp scratch*"))
(defun open-org-scratch ()
(interactive)
(switch-to-buffer "*org scratch*"))
(defun toggle-elisp-scratch ()
(interactive)
(if (get-buffer-window "*elisp scratch*")
(delete-window (get-buffer-window "*elisp scratch*"))
(switch-to-buffer "*elisp scratch*")))
(defun toggle-org-scratch ()
(interactive)
(if (get-buffer-window "*org scratch*")
(delete-window (get-buffer-window "org scratch"))
(switch-to-buffer "*org scratch*")))
(add-to-list 'display-buffer-alist
'("\\*elisp scratch\\*"
(display-buffer-in-side-window)
(side . right)
(slot . 1)
(window-width . 0.2)))
(add-to-list 'display-buffer-alist
'("\\*org scratch\\*"
(display-buffer-in-side-window)
(side . right)
(slot . 2)
(window-width . 0.2)))
(provide 'scratch)
;;; scratch.el ends here

54
userland/terminal.el Normal file
View file

@ -0,0 +1,54 @@
;;; terminal.el --- Userland terminal with vterm -*- lexical-binding: t -*-
;; This file is not part of GNU Emacs
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; commentary
;;; Code:
(when (not (equal system-type 'windows-nt))
(use-package vterm
:ensure t))
(defun theurgy-shell ()
(interactive)
(if (fboundp 'vterm)
'vterm
'shell))
(defun theurgy-bottom-shell ()
(interactive)
(let ((win (car (window-at-side-list nil 'bottom))))
(if (and win (equal 'bottom (window-parameter win 'window-side)))
(delete-window win)
(funcall (theurgy-shell)))))
(global-set-key (kbd "M-RET") 'theurgy-bottom-shell)
(define-key vterm-mode-map (kbd "M-RET") 'theurgy-bottom-shell)
(add-to-list 'display-buffer-alist
'("\\*vterm\\*\\|\\*shell\\*"
(display-buffer-in-side-window)
(side . bottom)
(slot . 0)
(window-height . 0.2)))
(provide 'terminal)
;;; terminal.el ends here

51
window-utils.el Normal file
View file

@ -0,0 +1,51 @@
;;; window-utils.el --- Utility functions and config for window management -*- lexical-binding: t -*-
;; This file is not part of GNU Emacs
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; commentary
;;; Code:
(setq switch-to-buffer-obey-display-actions t)
(defun delete-other-main-windows ()
"Kill all windows except for side windows."
(interactive)
(dolist (win (window-list))
(when (and (not (equal (selected-window) win))
(not (window-parameter win 'window-side)))
(delete-window win))))
(define-key global-map (kbd "C-x 1") #'delete-other-main-windows)
(define-key global-map (kbd "C-S-x 1") #'delete-other-windows)
;; left, top, right, bottom
(setq window-sides-slots '(5 1 5 1))
(add-to-list 'display-buffer-alist
'("\\*Help\\*"
(display-buffer-in-side-window)
(side . right)
(slot . 4)
(window-height . 0.2)))
(provide 'window-utils)
;;; window-utils.el ends here

View file

@ -0,0 +1,698 @@
;;; org-custom.el --- Org-mode configuration -*- lexical-binding: t -*-
;; This file is not part of GNU Emacs
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; commentary
;;; Code:
(require 'org)
(require 'org-agenda)
(require 'org-attach)
(require 'transient)
(require 'subr-x)
(defun org-mode-init ()
"This is called through `org-mode-hook', so that I don't need to `add-hook' 20 million times."
;; I want org to display pictures inline.
(org-display-inline-images)
;; I don't want monospace text by default.
(variable-pitch-mode)
)
(add-hook 'org-mode-hook #'org-mode-init)
(defcustom theurgy-org-directory "~/.org/"
"The directory for all theurgy org-mode files."
:type 'string
:group 'theurgy
:group 'theurgy-org)
;; Change a bunch of default org-mode variables.
(setq org-log-done nil ;; I want to keep track of the time tasks are completed.
org-return-follows-link t ;; I want to be able to follow links easily.
org-hide-emphasis-markers t ;; This is a big one: hide markup syntax like ** and //.
org-pretty-entities t ;; These two variables together render latex entities as UTF8, which means that Greek letters, subscript, and superscript are all rendered correctly.
org-pretty-entities-include-sub-superscripts t
org-enforce-todo-dependencies t ;; Prevent changing a parent to DONE if the children aren't
org-enforce-todo-checkbox-dependencies t
org-agenda-skip-scheduled-if-done t ;; Don't show done stuff in the agenda view
org-agenda-skip-deadline-if-done t
org-agenda-skip-timestamp-if-done t
org-todo-keywords '((sequence "TODO(t)" "WIP(w)" "DELEGATED(p)" "WAITING(?)" "|" "CANCELLED(x)" "DONE(d)")) ;; Some extra keywords in addition to TODO and DONE
org-use-fast-todo-selection t ;; Always use fast selection
org-refile-targets '((org-agenda-files :maxlevel . 2)) ;; Allow any agenda files to be refile targets.
org-agenda-files (append (apply 'append (mapcar
(lambda (dir)
(directory-files-recursively dir org-agenda-file-regexp))
`(,(concat theurgy-org-directory "tasks"))))
`(,(concat theurgy-org-directory "inbox.org"))) ;; Agenda files location - gathered recursively
org-cite-global-bibliography `(,(concat theurgy-org-directory "global.bib")) ;; Global bibliography location
org-startup-indented t ;; Start indented - this fixed org-indent mode
org-attach-id-dir (concat theurgy-org-directory "lore/assets")
org-M-RET-may-split-line '((item . nil)) ;; https://irreal.org/blog/?p=6297
)
;; Org modern and other interface enhancements
(use-package org-modern
:hook ((org-mode . org-modern-mode)
(org-agenda-finalize . org-modern-agenda)))
(use-package org-modern-indent
:straight (org-modern-indent :type git :host github :repo "jdtsmith/org-modern-indent")
:hook ((org-mode . org-modern-indent-mode)))
(use-package wc-mode
:hook ((org-mode . wc-mode))
:config (setq wc-modeline-format "WC[%tw]"))
(use-package org-appear :ensure t
:init
(setq org-appear-delay 0.2)
:hook
org-mode)
;; Side tree
(use-package org-side-tree
:hook ((org-modern-mode . org-side-tree)))
;; Advice to display org-side-tree where we want it
(defun theurgy-org-side-tree ()
"Create or pop to Org-Side-Tree buffer."
(interactive)
(when (org-side-tree-buffer-p)
(error "Don't tree a tree"))
(unless (or (derived-mode-p 'org-mode)
(derived-mode-p 'outline-mode)
outline-minor-mode)
(error "Not an outline buffer"))
(let* ((tree-name (if (default-value 'org-side-tree-persistent)
"*Org-Side-Tree*"
(format "<tree>%s" (buffer-name))))
(tree-buffer (get-buffer tree-name))
(heading (org-side-tree-heading-number))
(dd default-directory)
(inv-spec buffer-invisibility-spec))
(unless (buffer-live-p tree-buffer)
(save-restriction
(widen)
(jit-lock-mode 1)
(jit-lock-fontify-now))
(setq tree-buffer (generate-new-buffer tree-name))
(add-hook 'kill-buffer-hook #'org-side-tree-cleanup nil t)
(let* ((headings (org-side-tree-get-headings))
(tree-head-line (or (cadar (org-collect-keywords
'("title")))
"Org-Side-Tree"))
(tree-mode-line (format "Org-Side-Tree - %s"
(buffer-name))))
(when (default-value 'org-side-tree-enable-folding)
(setq-local org-side-tree-enable-folding t))
(with-current-buffer tree-buffer
(setq-local default-directory dd)
(org-side-tree-mode)
(setq tabulated-list-entries headings)
(tabulated-list-print t t)
(when (default-value 'org-side-tree-enable-folding)
(setq-local org-side-tree-enable-folding t)
;; preserve org font-locking
(setq-local outline-minor-mode-highlight nil)
(outline-minor-mode 1))
(setq buffer-invisibility-spec inv-spec)
(setq header-line-format tree-head-line)
(setq mode-line-format tree-mode-line))))
(org-side-tree-set-timer)
(switch-to-buffer (buffer-name))
(display-buffer-in-side-window
tree-buffer
`((side . ,org-side-tree-display-side)
(slot . 1)
(window-width . ,org-side-tree-width)))
(when (default-value 'org-side-tree-persistent)
(setq-local org-side-tree-persistent
(buffer-name))
(org-side-tree-update))
(when org-side-tree-select
(pop-to-buffer tree-buffer))
(setq window-size-fixed (when org-side-tree-width 'width))
(pulse-momentary-highlight-one-line)
(org-side-tree-go-to-heading heading)))
(advice-add 'org-side-tree :override #'theur)
;; Helper functions for the side tree
(defun theurgy-toggle-org-side-tree ()
(interactive)
(if (get-buffer-window (concat "<tree>" (file-name-nondirectory (buffer-file-name))))
(delete-window (get-buffer-window (concat "<tree>" (file-name-nondirectory (buffer-file-name)))))
(org-side-tree)))
(add-to-list 'display-buffer-alist
'("<tree>.+\.org"
(display-buffer-in-side-window)
(side . left)
(slot . 1)
(window-height . 0.2)))
(define-key org-mode-map (kbd "C-<f8>") #'theurgy-toggle-org-side-tree)
;; https://chrismaiorana.com/summer-productivity-reset-emacs-functions/
(defun csm/replace-punctuation-at-point ()
"Replace punctuation at point (., !, ?) with the character typed by the user."
(interactive)
(let ((char (this-command-keys))) ;; Get the key pressed by the user
(if (and (member (char-after) '(?. ?! ?? ?,)) ;; Check if current char is ., !, or ?
(member (string-to-char char) '(?. ?! ??))) ;; Check if typed char is ., !, or ?
(progn
(delete-char 1) ;; Delete existing punctuation
(insert char)) ;; Insert new punctuation
(self-insert-command 1)))) ;; Default behavior: insert character
;; Bind this function to punctuation keys
(define-key global-map "." 'csm/replace-punctuation-at-point)
(define-key global-map "," 'csm/replace-punctuation-at-point)
(define-key global-map "!" 'csm/replace-punctuation-at-point)
(define-key global-map "?" 'csm/replace-punctuation-at-point)
;; Windmove bindings since org overwrites them otherwise
(define-key org-mode-map (kbd "C-c <left>") 'windmove-left)
(define-key org-mode-map (kbd "C-c <right>") 'windmove-right)
(define-key org-mode-map (kbd "C-c <up>") 'windmove-up)
(define-key org-mode-map (kbd "C-c <down>") 'windmove-down)
;; Same with scrolling
(define-key org-mode-map (kbd "C-S-<down>") '(lambda () (interactive) (scroll-up-line 10)))
(define-key org-mode-map (kbd "C-S-<up>") '(lambda () (interactive) (scroll-down-line 10)))
;; Sentence and word navigation and marking
(setq sentence-end-double-space nil) ;; Otherwise, M-e is broken in normal writing.
;;; Generally useful things
;; Mark word and sentence
(defun mark-whole-word ()
"Mark the whole word underneath the cursor."
(interactive)
(forward-word)
(set-mark-command nil)
(backward-word))
(defun mark-sentence ()
"Mark the entire sentence underneath the cursor."
(interactive)
(forward-sentence)
(set-mark-command nil)
(backward-sentence))
(define-key org-mode-map (kbd "C-@") #'mark-whole-word)
(define-key org-mode-map (kbd "M-@") #'mark-sentence)
(transient-define-prefix mark-menu-transient ()
"Transient menu for marking units of text."
["Mark" ("w" "Word" mark-whole-word)
("s" "Sentence" mark-sentence)
("p" "Paragraph" mark-paragraph)
("b" "Buffer" mark-whole-buffer)])
(define-key org-mode-map (kbd "C-c o SPC") #'mark-menu-transient)
;; Section navigation and reordering
(defun move-sentence-right (&optional arg)
"Move the whole sentence to the right of the next sentence, `ARG' times."
(interactive "^p")
(or arg (setq arg 1))
(while (> arg 0)
(forward-sentence)
(backward-sentence)
(left-char) ;; Capture previous space
(kill-sentence)
(forward-sentence)
(yank)
(backward-sentence)
(setq arg (1- arg))))
(defun move-sentence-left (&optional arg)
"Move the whole sentence to the left of the previous sentence, `ARG' times."
(interactive "^p")
(or arg (setq arg 1))
(while (> arg 0)
(forward-sentence)
(backward-sentence)
(left-char) ;; Capture previous space
(kill-sentence)
(backward-sentence)
(left-char) ;; Capture previous space
(yank)
(backward-sentence)
(setq arg (1- arg))))
(defun move-paragraph-up (&optional arg)
"Move the whole paragraph above the previous paragraph, `ARG' times."
(interactive "^p")
(or arg (setq arg 1))
(while (> arg 0)
(forward-paragraph)
(backward-paragraph)
(kill-paragraph nil)
(backward-paragraph)
(yank)
(backward-paragraph)
(setq arg (1- arg))))
(defun move-paragraph-down (&optional arg)
"Move the whole paragraph above the previous paragraph, `ARG' times."
(interactive "^p")
(or arg (setq arg 1))
(while (> arg 0)
(forward-paragraph)
(backward-paragraph)
(kill-paragraph nil)
(forward-paragraph)
(yank)
(backward-paragraph)
(setq arg (1- arg))))
(define-key org-mode-map (kbd "C-M-<left>") #'move-sentence-left)
(define-key org-mode-map (kbd "C-M-<right>") #'move-sentence-right)
(define-key org-mode-map (kbd "C-M-<up>") #'move-paragraph-up)
(define-key org-mode-map (kbd "C-M-<down>") #'move-paragraph-down)
(transient-define-prefix reorder-transient ()
"Transient menu for text re-ordering commands."
["Move sentence..." ("l" "Left" move-sentence-left)
("r" "Right" move-sentence-right)]
["Move paragraph..." ("u" "Up" move-paragraph-up)
("d" "Down" move-paragraph-down)])
(define-key org-mode-map (kbd "C-c o r") #'reorder-transient)
;; Focused rewriting
(defun break-out-sentence ()
"Breaks a sentence out into it's own buffer for editing."
(interactive)
(backward-sentence)
(kill-sentence)
(switch-to-buffer-other-window "*break-out*")
(erase-buffer)
(yank))
(defun break-out-choose-sentence ()
"Chooses a sentence from the break-out buffer."
(interactive)
(backward-sentence)
(kill-sentence)
(other-window -1)
(kill-buffer "*break-out*")
(yank))
(defun break-out-dwim ()
"Either break-out or choose sentency depending on buffer name."
(interactive)
(if (equal (buffer-name) "*break-out*")
(break-out-choose-sentence)
(break-out-sentence)))
(define-key org-mode-map (kbd "C-c o b") #'break-out-dwim)
;; Powerthesaurus
(use-package powerthesaurus
:bind ("C-x t" . powerthesaurus-transient))
;; Harper
(when (and (not (equal system-type 'windows-nt)) (locate-file "harper-ls" exec-path))
(with-eval-after-load 'eglot
(add-to-list 'eglot-server-programs
'(org-mode . ("harper-ls" "--stdio"))))
(setq-default eglot-workspace-configuration
'(:harper-ls (:dialect "Australian")))
(add-hook 'org-mode-hook 'eglot-ensure))
;;; Exobrain
;;; Task management
;; Maintenance and project file creation shortcuts
(defun new-maintenance-file (maintenance-area)
"Create a new maintenance file for a given `MAINTENANCE-AREA'."
(interactive "sMaintenance area is: ")
(find-file (concat "~/.org/tasks/maintenance/" maintenance-area ".org"))
(insert "\n\n* Calendar\n\n* TO-DO\n\n* Routines\n\n* Archive\n")
(goto-char (point-min)))
(defun new-project-file (project-name acceptance-criteria)
"Create a new project file for a given `PROJECT-NAME', with some `ACCEPTANCE-CRITERIA'."
(find-file (concat "~/.org/tasks/projects/" project-name ".org"))
(insert (concat "\n\n* Acceptance Criteria\n\n" acceptance-criteria "\n\n* Ideas\n\n* TODO Work\n\n** Calendar\n\n** TO-DO\n\n** Routines\n\n** Archive\n"))
(goto-char (point-min)))
(defun new-generic-project (project-name acceptance-criteria)
"Interactive wrapper for `new-project-file', for generic projects."
(interactive "sProject name is: \nsAcceptance criteria is: ")
(new-project-file project-name acceptance-criteria))
(defun new-career-project (project-name acceptance-criteria)
"Interactive wrapper for `new-project-file', for career projects."
(interactive "sProject name is: \nsAcceptance criteria is: ")
(new-project-file (concat "career/" project-name) acceptance-criteria))
;; Captures
(defun select-task-area (location headline)
"Prompt for a file in ~/.org/tasks/maintenance/ to insert the capture."
(let* ((files (directory-files (concat "~/.org/tasks/" location) t "\\.org$"))
(choices (mapcar
(lambda (x) (string-remove-suffix ".org" x))
(mapcar #'file-name-nondirectory files)))
(selected (completing-read "Choose a file: " choices nil t)))
(set-buffer (org-capture-target-buffer (concat "~/.org/tasks/" location selected ".org")))
(org-capture-put-target-region-and-position)
(widen)
(goto-char (point-min))
(setq headline (org-capture-expand-headline headline))
(re-search-forward (format org-complex-heading-regexp-format
(regexp-quote headline))
nil t)
(forward-line 0)))
(defun select-maintenance-area (headline)
(select-task-area "maintenance/" headline))
(defun select-project (headline)
(select-task-area "projects/" headline))
(defun select-career-project (headline)
(select-task-area "projects/career/" headline))
(setq org-capture-templates
'(("i" "Idea")
("ii" "Generic" entry
(file "~/.org/inbox.org")
"* %^{TITLE} :idea: \n\n%?"
:empty-lines 1)
("ip" "Project" entry
(function (lambda () (select-project "Ideas")))
"* %^{TITLE} \n\n%?"
:empty-lines 1)
("ic" "Career" entry
(function (lambda () (select-career-project "Ideas")))
"* %^{TITLE} \n\n%?"
:empty-lines 1)
("t" "Task")
("tt" "Generic")
("ttt" "Raw" entry
(file "~/.org/inbox.org")
"* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE} :task:\n\n%?"
:empty-lines 1)
("ttm" "Maintenance" entry
(function (lambda () (select-maintenance-area "TO-DO")))
"* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE}\n\n%?"
:empty-lines 1)
("ttp" "Project" entry
(function (lambda () (select-project "TO-DO")))
"* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE}\n\n%?"
:empty-lines 1)
("ttc" "Career" entry
(function (lambda () (select-career-project "TO-DO")))
"* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE}\n\n%?"
:empty-lines 1)
("ts" "Scheduled")
("tss" "Raw" entry
(file "~/.org/inbox.org")
"* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE} :scheduled:\n:SCHEDULED: %^T\n%?"
:empty-lines 1
:time-prompt t)
("tsm" "Maintenance" entry
(function (lambda () (select-maintenance-area "Calendar")))
"* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE} \n:SCHEDULED: %^T\n%?"
:empty-lines 1
:time-prompt t)
("tsp" "Project" entry
(function (lambda () (select-project "Calendar")))
"* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE} \n:SCHEDULED: %^T\n%?"
:empty-lines 1
:time-prompt t)
("tsc" "Career" entry
(function (lambda () (select-career-project "Calendar")))
"* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE} \n:SCHEDULED: %^T\n%?"
:empty-lines 1
:time-prompt t)
("n" "Note")
("nn" "Raw" entry
(file "~/.org/inbox.org")
"* %^{TITLE} :note: \n\n%?"
:empty-lines 1)
("nm" "Meeting")
("nmm" "Raw" entry
(file "~/.org/inbox.org")
"* %^{TITLE} :meeting: \n\n%?"
:clock-in t
:clock-resume t)
("nmc" "Career" entry
(function (lambda () (select-career-project "Calendar")))
"* %^{TITLE} \n\n%?"
:empty-lines 1
:clock-in t
:clock-resume t)))
(defun open-inbox ()
"Opens `~/.org/inbox.org'"
(interactive)
(find-file "~/.org/inbox.org"))
(defun open-maintenance ()
"Opens `~/.org/tasks/maintenance/'"
(interactive)
(find-file "~/.org/tasks/maintenance/"))
(defun open-projects ()
"Opens `~/.org/tasks/projects/'"
(interactive)
(find-file "~/.org/tasks/projects/"))
(defun open-career ()
"Opens `~/.org/tasks/projects/career/'"
(interactive)
(find-file "~/.org/tasks/projects/career/"))
(transient-define-prefix task-management-menu ()
"Transient menu for task management shortcuts."
["Go To" ("i" "Inbox" open-inbox)
("m" "Maintenance Directory" open-maintenance)
("p" "Projects Directory" open-projects)
("c" "Career Directory" open-career)]
["Create New" ("M" "Maintenance File" new-maintenance-file)
("P" "Project" new-generic-project)
("C" "Career Project" new-career-project)])
(define-key global-map (kbd "C-c o t") #'task-management-menu)
(define-key global-map (kbd "C-c a") #'org-agenda)
(define-key global-map (kbd "C-c c") #'org-capture)
(add-to-list 'display-buffer-alist
'("\\*Org Agenda\\*"
(display-buffer-in-side-window)
(side . right)
(slot . 3)
(window-width . 0.2)))
(add-to-list 'display-buffer-alist
'("\\*capture\\*\\|CAPTURE-"
(display-buffer-in-side-window)
(side . right)
(slot . 2)
(window-width . 0.2)))
;; Org roam
(use-package org-roam
:straight nil
:ensure t
:init
(setq org-roam-v2-ack t)
:custom
(org-roam-directory (file-truename "~/.org/lore/"))
(org-roam-dailies-directory (file-truename "~/.org/lore/refined/journal/"))
(org-roam-file-exclude-regexp "\\.git/.*\\|logseq/.*$")
(org-roam-completion-everywhere)
(org-roam-capture-templates
'(("r" "Raw")
("rr" "Note" plain
"%?"
:target (file+head "raw/${slug}.org" "#+title: ${title}\n")
:unnarrowed t)
("rq" "Question" plain
"%?"
:target (file+head "raw/${slug}.org" "#+title: ${title}\n#+filetags: :question:")
:unnarrowed t)
("i" "Index" plain
"%?"
:target (file+head "raw/${slug}.org" "#+title: ${title} - Index\n#+filetags: :index:")
:unnarrowed t)
("R" "Refined")
("Rm" "Mini-essay" plain
"%?"
:target (file+head "refined/wiki/${slug}.org" "#+title: ${title}\n#+author: Jakub Nowak\n#+filetags: :mini-essay:")
:unnarrowed t)
("Re" "Essay" plain
"%?"
:target (file+head "refined/wiki/${slug}.org" "#+title: ${title}\n#+author: Jakub Nowak\n#+filetags: :essay:")
:unnarrowed t)))
(org-roam-dailies-capture-templates
'(("d" "default" entry
"* %?"
:target (file+head "%<%Y-%m-%d>.org" ;; format matches Logseq
"#+title: %<%Y-%m-%d>\n"))))
: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)
:map org-roam-dailies-map
("Y" . org-roam-dailies-capture-yesterday)
("T" . org-roam-dailies-capture-tomorrow))
:bind-keymap ("C-c n d" . org-roam-dailies-map)
:config
(require 'org-roam-dailies) ;; Ensure the keymap is available
(org-roam-db-autosync-mode))
(defun org-roam-create-node-from-headline ()
"Create an Org-roam note from the current headline and jump to it.
Normally, insert the headlines title using the #title: file-level property
and delete the Org-mode headline. However, if the current headline has a
Org-mode properties drawer already, keep the headline and dont insert
#+title:'. Org-roam can extract the title from both kinds of notes, but using
#+title: is a bit cleaner for a short note, which Org-roam encourages."
(interactive)
(let ((title (nth 4 (org-heading-components)))
(has-properties (org-get-property-block)))
(org-cut-subtree)
(org-roam-find-file title nil nil 'no-confirm)
(org-paste-subtree)
(unless has-properties
(kill-line)
(while (outline-next-heading)
(org-promote)))
(goto-char (point-min))
(when has-properties
(kill-line)
(kill-line))))
(use-package logseq-org-roam
:straight (:host github
:repo "sbougerel/logseq-org-roam"
:files ("*.el")))
(transient-define-prefix org-roam-menu ()
"Transient menu for task management shortcuts."
["Node" ("c" "Capture" org-roam-node-find)
("i" "Insert" org-roam-node-insert)
("r" "Refile" org-roam-create-node-from-headline)
("f" "Find" org-roam-node-find) ]
["Dailies" ("t" "Today" org-roam-dailies-capture-today)
("y" "Yesterday" org-roam-dailies-capture-yesterday)
("n" "Tomorrow" org-roam-dailies-capture-tomorrow)]
["Logseq" ("R" "Fix links" logseq-org-roam)]
["Org Roam UI" ("U" "Enable org-roam-ui-mode" org-roam-ui-mode)])
(define-key global-map (kbd "C-c o n") #'org-roam-menu)
(use-package org-roam-ui
:straight
(:host github :repo "org-roam/org-roam-ui" :branch "main" :files ("*.el" "out"))
:after org-roam
;; normally we'd recommend hooking orui after org-roam, but since org-roam does not have
;; a hookable mode anymore, you're advised to pick something yourself
;; if you don't care about startup time, use
:hook (after-init . org-roam-ui-mode)
:config
(setq org-roam-ui-sync-theme t
org-roam-ui-follow t
org-roam-ui-update-on-save t
org-roam-ui-open-on-start t))
;; Bibliography stuff
(setq bibtex-dialect 'biblatex) ;; Use biblatex instead of bibtex.
(use-package biblio)
(defun bibtex-online-entry ()
(interactive)
(bibtex-entry "Online"))
(defun bibtex-misc-entry ()
(interactive)
(bibtex-entry "Misc"))
(defun bibtex-software-entry ()
(interactive)
(bibtex-entry "Software"))
(transient-define-prefix bibtex-transient-menu ()
"Transient menu for task management shortcuts."
["Biblio" ("d" "doi.org" doi-insert-bibtex)
("x" "arXiv" arxiv-lookup)]
["Templates" ("o" "Online Resource" bibtex-online-entry)
("m" "Misc" bibtex-misc-entry)
("s" "Software" bibtex-software-entry)])
(define-key bibtex-mode-map (kbd "C-c r") #'bibtex-transient-menu)
;;; Literate Programming
;; Racket
(use-package ob-racket
:after org
:config
(add-hook 'ob-racket-pre-runtime-library-load-hook
#'ob-racket-raco-make-runtime-library)
:straight (ob-racket
:type git :host github :repo "hasu/emacs-ob-racket"
:files ("*.el" "*.rkt")))
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(racket . t)
))
;;; LaTeX configuration
(require 'ox-latex)
(setq org-latex-pdf-process
'("pdflatex -interaction nonstopmode -output-directory %o %f"
"bibtex %b"
"pdflatex -interaction nonstopmode -output-directory %o %f"
"pdflatex -interaction nonstopmode -output-directory %o %f"))
(setq org-latex-with-hyperref nil) ;; stop org adding hypersetup{author..} to latex export
;; (setq org-latex-prefer-user-labels t)
;; deleted unwanted file extensions after latexMK
(setq org-latex-logfiles-extensions
(quote ("lof" "lot" "tex~" "aux" "idx" "log" "out" "toc" "nav" "snm" "vrb" "dvi" "fdb_latexmk" "blg" "brf" "fls" "entoc" "ps" "spl" "bbl" "xmpi" "run.xml" "bcf" "acn" "acr" "alg" "glg" "gls" "ist")))
(unless (boundp 'org-latex-classes)
(setq org-latex-classes nil))
(provide 'org-custom)
;;; org-custom.el ends here

45
workflows/lisp-custom.el Normal file
View file

@ -0,0 +1,45 @@
;;; lisp-custom.el --- Customisations for working with lisp -*- lexical-binding: t -*-
;; This file is not part of GNU Emacs
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; commentary
;;; Code:
;; Paredit my beloved
(use-package paredit
:hook ((emacs-lisp-mode . enable-paredit-mode)
(eval-expression-minibuffer-setup . enable-paredit-mode)
(lisp-mode . enable-paredit-mode)
(lisp-interaction-mode . enable-paredit-mode)
(scheme-mode . enable-paredit-mode))
:config
(add-hook 'ielm-mode-hook (lambda ()
(enable-paredit-mode)
;; This fixes newline behavior in IELM, but breaks it everywhere else, hence it's wrapped here
(define-key paredit-mode-map (kbd "RET") nil)
(define-key paredit-mode-map (kbd "C-j") 'paredit-newline))))
;; SICP my beloved
(use-package sicp
:ensure t)
(provide 'lisp-custom)
;;; lisp-custom.el ends here

698
workflows/org-custom.el Normal file
View file

@ -0,0 +1,698 @@
;;; org-custom.el --- Org-mode configuration -*- lexical-binding: t -*-
;; This file is not part of GNU Emacs
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; commentary
;;; Code:
(require 'org)
(require 'org-agenda)
(require 'org-attach)
(require 'transient)
(require 'subr-x)
(defun org-mode-init ()
"This is called through `org-mode-hook', so that I don't need to `add-hook' 20 million times."
;; I want org to display pictures inline.
(org-display-inline-images)
;; I don't want monospace text by default.
(variable-pitch-mode)
)
(add-hook 'org-mode-hook #'org-mode-init)
(defcustom theurgy-org-directory "~/.org/"
"The directory for all theurgy org-mode files."
:type 'string
:group 'theurgy
:group 'theurgy-org)
;; Change a bunch of default org-mode variables.
(setq org-log-done nil ;; I want to keep track of the time tasks are completed.
org-return-follows-link t ;; I want to be able to follow links easily.
org-hide-emphasis-markers t ;; This is a big one: hide markup syntax like ** and //.
org-pretty-entities t ;; These two variables together render latex entities as UTF8, which means that Greek letters, subscript, and superscript are all rendered correctly.
org-pretty-entities-include-sub-superscripts t
org-enforce-todo-dependencies t ;; Prevent changing a parent to DONE if the children aren't
org-enforce-todo-checkbox-dependencies t
org-agenda-skip-scheduled-if-done t ;; Don't show done stuff in the agenda view
org-agenda-skip-deadline-if-done t
org-agenda-skip-timestamp-if-done t
org-todo-keywords '((sequence "TODO(t)" "WIP(w)" "DELEGATED(p)" "WAITING(?)" "|" "CANCELLED(x)" "DONE(d)")) ;; Some extra keywords in addition to TODO and DONE
org-use-fast-todo-selection t ;; Always use fast selection
org-refile-targets '((org-agenda-files :maxlevel . 2)) ;; Allow any agenda files to be refile targets.
org-agenda-files (append (apply 'append (mapcar
(lambda (dir)
(directory-files-recursively dir org-agenda-file-regexp))
`(,(concat theurgy-org-directory "tasks"))))
`(,(concat theurgy-org-directory "inbox.org"))) ;; Agenda files location - gathered recursively
org-cite-global-bibliography `(,(concat theurgy-org-directory "global.bib")) ;; Global bibliography location
org-startup-indented t ;; Start indented - this fixed org-indent mode
org-attach-id-dir (concat theurgy-org-directory "lore/assets")
org-M-RET-may-split-line '((item . nil)) ;; https://irreal.org/blog/?p=6297
)
;; Org modern and other interface enhancements
(use-package org-modern
:hook ((org-mode . org-modern-mode)
(org-agenda-finalize . org-modern-agenda)))
(use-package org-modern-indent
:straight (org-modern-indent :type git :host github :repo "jdtsmith/org-modern-indent")
:hook ((org-mode . org-modern-indent-mode)))
(use-package wc-mode
:hook ((org-mode . wc-mode))
:config (setq wc-modeline-format "WC[%tw]"))
(use-package org-appear :ensure t
:init
(setq org-appear-delay 0.2)
:hook
org-mode)
;; Side tree
(use-package org-side-tree
:hook ((org-modern-mode . org-side-tree)))
;; Advice to display org-side-tree where we want it
(defun theurgy-org-side-tree ()
"Create or pop to Org-Side-Tree buffer."
(interactive)
(when (org-side-tree-buffer-p)
(error "Don't tree a tree"))
(unless (or (derived-mode-p 'org-mode)
(derived-mode-p 'outline-mode)
outline-minor-mode)
(error "Not an outline buffer"))
(let* ((tree-name (if (default-value 'org-side-tree-persistent)
"*Org-Side-Tree*"
(format "<tree>%s" (buffer-name))))
(tree-buffer (get-buffer tree-name))
(heading (org-side-tree-heading-number))
(dd default-directory)
(inv-spec buffer-invisibility-spec))
(unless (buffer-live-p tree-buffer)
(save-restriction
(widen)
(jit-lock-mode 1)
(jit-lock-fontify-now))
(setq tree-buffer (generate-new-buffer tree-name))
(add-hook 'kill-buffer-hook #'org-side-tree-cleanup nil t)
(let* ((headings (org-side-tree-get-headings))
(tree-head-line (or (cadar (org-collect-keywords
'("title")))
"Org-Side-Tree"))
(tree-mode-line (format "Org-Side-Tree - %s"
(buffer-name))))
(when (default-value 'org-side-tree-enable-folding)
(setq-local org-side-tree-enable-folding t))
(with-current-buffer tree-buffer
(setq-local default-directory dd)
(org-side-tree-mode)
(setq tabulated-list-entries headings)
(tabulated-list-print t t)
(when (default-value 'org-side-tree-enable-folding)
(setq-local org-side-tree-enable-folding t)
;; preserve org font-locking
(setq-local outline-minor-mode-highlight nil)
(outline-minor-mode 1))
(setq buffer-invisibility-spec inv-spec)
(setq header-line-format tree-head-line)
(setq mode-line-format tree-mode-line))))
(org-side-tree-set-timer)
(switch-to-buffer (buffer-name))
(display-buffer-in-side-window
tree-buffer
`((side . ,org-side-tree-display-side)
(slot . 1)
(window-width . ,org-side-tree-width)))
(when (default-value 'org-side-tree-persistent)
(setq-local org-side-tree-persistent
(buffer-name))
(org-side-tree-update))
(when org-side-tree-select
(pop-to-buffer tree-buffer))
(setq window-size-fixed (when org-side-tree-width 'width))
(pulse-momentary-highlight-one-line)
(org-side-tree-go-to-heading heading)))
(advice-add 'org-side-tree :override #'theurgy-org-side-tree)
;; Helper functions for the side tree
(defun theurgy-toggle-org-side-tree ()
(interactive)
(if (get-buffer-window (concat "<tree>" (file-name-nondirectory (buffer-file-name))))
(delete-window (get-buffer-window (concat "<tree>" (file-name-nondirectory (buffer-file-name)))))
(org-side-tree)))
(add-to-list 'display-buffer-alist
'("<tree>.+\.org"
(display-buffer-in-side-window)
(side . left)
(slot . 1)
(window-height . 0.2)))
(define-key org-mode-map (kbd "C-<f8>") #'theurgy-toggle-org-side-tree)
;; https://chrismaiorana.com/summer-productivity-reset-emacs-functions/
(defun csm/replace-punctuation-at-point ()
"Replace punctuation at point (., !, ?) with the character typed by the user."
(interactive)
(let ((char (this-command-keys))) ;; Get the key pressed by the user
(if (and (member (char-after) '(?. ?! ?? ?,)) ;; Check if current char is ., !, or ?
(member (string-to-char char) '(?. ?! ??))) ;; Check if typed char is ., !, or ?
(progn
(delete-char 1) ;; Delete existing punctuation
(insert char)) ;; Insert new punctuation
(self-insert-command 1)))) ;; Default behavior: insert character
;; Bind this function to punctuation keys
(define-key global-map "." 'csm/replace-punctuation-at-point)
(define-key global-map "," 'csm/replace-punctuation-at-point)
(define-key global-map "!" 'csm/replace-punctuation-at-point)
(define-key global-map "?" 'csm/replace-punctuation-at-point)
;; Windmove bindings since org overwrites them otherwise
(define-key org-mode-map (kbd "C-c <left>") 'windmove-left)
(define-key org-mode-map (kbd "C-c <right>") 'windmove-right)
(define-key org-mode-map (kbd "C-c <up>") 'windmove-up)
(define-key org-mode-map (kbd "C-c <down>") 'windmove-down)
;; Same with scrolling
(define-key org-mode-map (kbd "C-S-<down>") '(lambda () (interactive) (scroll-up-line 10)))
(define-key org-mode-map (kbd "C-S-<up>") '(lambda () (interactive) (scroll-down-line 10)))
;; Sentence and word navigation and marking
(setq sentence-end-double-space nil) ;; Otherwise, M-e is broken in normal writing.
;;; Generally useful things
;; Mark word and sentence
(defun mark-whole-word ()
"Mark the whole word underneath the cursor."
(interactive)
(forward-word)
(set-mark-command nil)
(backward-word))
(defun mark-sentence ()
"Mark the entire sentence underneath the cursor."
(interactive)
(forward-sentence)
(set-mark-command nil)
(backward-sentence))
(define-key org-mode-map (kbd "C-@") #'mark-whole-word)
(define-key org-mode-map (kbd "M-@") #'mark-sentence)
(transient-define-prefix mark-menu-transient ()
"Transient menu for marking units of text."
["Mark" ("w" "Word" mark-whole-word)
("s" "Sentence" mark-sentence)
("p" "Paragraph" mark-paragraph)
("b" "Buffer" mark-whole-buffer)])
(define-key org-mode-map (kbd "C-c o SPC") #'mark-menu-transient)
;; Section navigation and reordering
(defun move-sentence-right (&optional arg)
"Move the whole sentence to the right of the next sentence, `ARG' times."
(interactive "^p")
(or arg (setq arg 1))
(while (> arg 0)
(forward-sentence)
(backward-sentence)
(left-char) ;; Capture previous space
(kill-sentence)
(forward-sentence)
(yank)
(backward-sentence)
(setq arg (1- arg))))
(defun move-sentence-left (&optional arg)
"Move the whole sentence to the left of the previous sentence, `ARG' times."
(interactive "^p")
(or arg (setq arg 1))
(while (> arg 0)
(forward-sentence)
(backward-sentence)
(left-char) ;; Capture previous space
(kill-sentence)
(backward-sentence)
(left-char) ;; Capture previous space
(yank)
(backward-sentence)
(setq arg (1- arg))))
(defun move-paragraph-up (&optional arg)
"Move the whole paragraph above the previous paragraph, `ARG' times."
(interactive "^p")
(or arg (setq arg 1))
(while (> arg 0)
(forward-paragraph)
(backward-paragraph)
(kill-paragraph nil)
(backward-paragraph)
(yank)
(backward-paragraph)
(setq arg (1- arg))))
(defun move-paragraph-down (&optional arg)
"Move the whole paragraph above the previous paragraph, `ARG' times."
(interactive "^p")
(or arg (setq arg 1))
(while (> arg 0)
(forward-paragraph)
(backward-paragraph)
(kill-paragraph nil)
(forward-paragraph)
(yank)
(backward-paragraph)
(setq arg (1- arg))))
(define-key org-mode-map (kbd "C-M-<left>") #'move-sentence-left)
(define-key org-mode-map (kbd "C-M-<right>") #'move-sentence-right)
(define-key org-mode-map (kbd "C-M-<up>") #'move-paragraph-up)
(define-key org-mode-map (kbd "C-M-<down>") #'move-paragraph-down)
(transient-define-prefix reorder-transient ()
"Transient menu for text re-ordering commands."
["Move sentence..." ("l" "Left" move-sentence-left)
("r" "Right" move-sentence-right)]
["Move paragraph..." ("u" "Up" move-paragraph-up)
("d" "Down" move-paragraph-down)])
(define-key org-mode-map (kbd "C-c o r") #'reorder-transient)
;; Focused rewriting
(defun break-out-sentence ()
"Breaks a sentence out into it's own buffer for editing."
(interactive)
(backward-sentence)
(kill-sentence)
(switch-to-buffer-other-window "*break-out*")
(erase-buffer)
(yank))
(defun break-out-choose-sentence ()
"Chooses a sentence from the break-out buffer."
(interactive)
(backward-sentence)
(kill-sentence)
(other-window -1)
(kill-buffer "*break-out*")
(yank))
(defun break-out-dwim ()
"Either break-out or choose sentency depending on buffer name."
(interactive)
(if (equal (buffer-name) "*break-out*")
(break-out-choose-sentence)
(break-out-sentence)))
(define-key org-mode-map (kbd "C-c o b") #'break-out-dwim)
;; Powerthesaurus
(use-package powerthesaurus
:bind ("C-x t" . powerthesaurus-transient))
;; Harper
(when (and (not (equal system-type 'windows-nt)) (locate-file "harper-ls" exec-path))
(with-eval-after-load 'eglot
(add-to-list 'eglot-server-programs
'(org-mode . ("harper-ls" "--stdio"))))
(setq-default eglot-workspace-configuration
'(:harper-ls (:dialect "Australian")))
(add-hook 'org-mode-hook 'eglot-ensure))
;;; Exobrain
;;; Task management
;; Maintenance and project file creation shortcuts
(defun new-maintenance-file (maintenance-area)
"Create a new maintenance file for a given `MAINTENANCE-AREA'."
(interactive "sMaintenance area is: ")
(find-file (concat "~/.org/tasks/maintenance/" maintenance-area ".org"))
(insert "\n\n* Calendar\n\n* TO-DO\n\n* Routines\n\n* Archive\n")
(goto-char (point-min)))
(defun new-project-file (project-name acceptance-criteria)
"Create a new project file for a given `PROJECT-NAME', with some `ACCEPTANCE-CRITERIA'."
(find-file (concat "~/.org/tasks/projects/" project-name ".org"))
(insert (concat "\n\n* Acceptance Criteria\n\n" acceptance-criteria "\n\n* Ideas\n\n* TODO Work\n\n** Calendar\n\n** TO-DO\n\n** Routines\n\n** Archive\n"))
(goto-char (point-min)))
(defun new-generic-project (project-name acceptance-criteria)
"Interactive wrapper for `new-project-file', for generic projects."
(interactive "sProject name is: \nsAcceptance criteria is: ")
(new-project-file project-name acceptance-criteria))
(defun new-career-project (project-name acceptance-criteria)
"Interactive wrapper for `new-project-file', for career projects."
(interactive "sProject name is: \nsAcceptance criteria is: ")
(new-project-file (concat "career/" project-name) acceptance-criteria))
;; Captures
(defun select-task-area (location headline)
"Prompt for a file in ~/.org/tasks/maintenance/ to insert the capture."
(let* ((files (directory-files (concat "~/.org/tasks/" location) t "\\.org$"))
(choices (mapcar
(lambda (x) (string-remove-suffix ".org" x))
(mapcar #'file-name-nondirectory files)))
(selected (completing-read "Choose a file: " choices nil t)))
(set-buffer (org-capture-target-buffer (concat "~/.org/tasks/" location selected ".org")))
(org-capture-put-target-region-and-position)
(widen)
(goto-char (point-min))
(setq headline (org-capture-expand-headline headline))
(re-search-forward (format org-complex-heading-regexp-format
(regexp-quote headline))
nil t)
(forward-line 0)))
(defun select-maintenance-area (headline)
(select-task-area "maintenance/" headline))
(defun select-project (headline)
(select-task-area "projects/" headline))
(defun select-career-project (headline)
(select-task-area "projects/career/" headline))
(setq org-capture-templates
'(("i" "Idea")
("ii" "Generic" entry
(file "~/.org/inbox.org")
"* %^{TITLE} :idea: \n\n%?"
:empty-lines 1)
("ip" "Project" entry
(function (lambda () (select-project "Ideas")))
"* %^{TITLE} \n\n%?"
:empty-lines 1)
("ic" "Career" entry
(function (lambda () (select-career-project "Ideas")))
"* %^{TITLE} \n\n%?"
:empty-lines 1)
("t" "Task")
("tt" "Generic")
("ttt" "Raw" entry
(file "~/.org/inbox.org")
"* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE} :task:\n\n%?"
:empty-lines 1)
("ttm" "Maintenance" entry
(function (lambda () (select-maintenance-area "TO-DO")))
"* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE}\n\n%?"
:empty-lines 1)
("ttp" "Project" entry
(function (lambda () (select-project "TO-DO")))
"* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE}\n\n%?"
:empty-lines 1)
("ttc" "Career" entry
(function (lambda () (select-career-project "TO-DO")))
"* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE}\n\n%?"
:empty-lines 1)
("ts" "Scheduled")
("tss" "Raw" entry
(file "~/.org/inbox.org")
"* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE} :scheduled:\n:SCHEDULED: %^T\n%?"
:empty-lines 1
:time-prompt t)
("tsm" "Maintenance" entry
(function (lambda () (select-maintenance-area "Calendar")))
"* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE} \n:SCHEDULED: %^T\n%?"
:empty-lines 1
:time-prompt t)
("tsp" "Project" entry
(function (lambda () (select-project "Calendar")))
"* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE} \n:SCHEDULED: %^T\n%?"
:empty-lines 1
:time-prompt t)
("tsc" "Career" entry
(function (lambda () (select-career-project "Calendar")))
"* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE} \n:SCHEDULED: %^T\n%?"
:empty-lines 1
:time-prompt t)
("n" "Note")
("nn" "Raw" entry
(file "~/.org/inbox.org")
"* %^{TITLE} :note: \n\n%?"
:empty-lines 1)
("nm" "Meeting")
("nmm" "Raw" entry
(file "~/.org/inbox.org")
"* %^{TITLE} :meeting: \n\n%?"
:clock-in t
:clock-resume t)
("nmc" "Career" entry
(function (lambda () (select-career-project "Calendar")))
"* %^{TITLE} \n\n%?"
:empty-lines 1
:clock-in t
:clock-resume t)))
(defun open-inbox ()
"Opens `~/.org/inbox.org'"
(interactive)
(find-file "~/.org/inbox.org"))
(defun open-maintenance ()
"Opens `~/.org/tasks/maintenance/'"
(interactive)
(find-file "~/.org/tasks/maintenance/"))
(defun open-projects ()
"Opens `~/.org/tasks/projects/'"
(interactive)
(find-file "~/.org/tasks/projects/"))
(defun open-career ()
"Opens `~/.org/tasks/projects/career/'"
(interactive)
(find-file "~/.org/tasks/projects/career/"))
(transient-define-prefix task-management-menu ()
"Transient menu for task management shortcuts."
["Go To" ("i" "Inbox" open-inbox)
("m" "Maintenance Directory" open-maintenance)
("p" "Projects Directory" open-projects)
("c" "Career Directory" open-career)]
["Create New" ("M" "Maintenance File" new-maintenance-file)
("P" "Project" new-generic-project)
("C" "Career Project" new-career-project)])
(define-key global-map (kbd "C-c o t") #'task-management-menu)
(define-key global-map (kbd "C-c a") #'org-agenda)
(define-key global-map (kbd "C-c c") #'org-capture)
(add-to-list 'display-buffer-alist
'("\\*Org Agenda\\*"
(display-buffer-in-side-window)
(side . right)
(slot . 3)
(window-width . 0.2)))
(add-to-list 'display-buffer-alist
'("\\*capture\\*\\|CAPTURE-"
(display-buffer-in-side-window)
(side . right)
(slot . 2)
(window-width . 0.2)))
;; Org roam
(use-package org-roam
:straight nil
:ensure t
:init
(setq org-roam-v2-ack t)
:custom
(org-roam-directory (file-truename "~/.org/lore/"))
(org-roam-dailies-directory (file-truename "~/.org/lore/refined/journal/"))
(org-roam-file-exclude-regexp "\\.git/.*\\|logseq/.*$")
(org-roam-completion-everywhere)
(org-roam-capture-templates
'(("r" "Raw")
("rr" "Note" plain
"%?"
:target (file+head "raw/${slug}.org" "#+title: ${title}\n")
:unnarrowed t)
("rq" "Question" plain
"%?"
:target (file+head "raw/${slug}.org" "#+title: ${title}\n#+filetags: :question:")
:unnarrowed t)
("i" "Index" plain
"%?"
:target (file+head "raw/${slug}.org" "#+title: ${title} - Index\n#+filetags: :index:")
:unnarrowed t)
("R" "Refined")
("Rm" "Mini-essay" plain
"%?"
:target (file+head "refined/wiki/${slug}.org" "#+title: ${title}\n#+author: Jakub Nowak\n#+filetags: :mini-essay:")
:unnarrowed t)
("Re" "Essay" plain
"%?"
:target (file+head "refined/wiki/${slug}.org" "#+title: ${title}\n#+author: Jakub Nowak\n#+filetags: :essay:")
:unnarrowed t)))
(org-roam-dailies-capture-templates
'(("d" "default" entry
"* %?"
:target (file+head "%<%Y-%m-%d>.org" ;; format matches Logseq
"#+title: %<%Y-%m-%d>\n"))))
: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)
:map org-roam-dailies-map
("Y" . org-roam-dailies-capture-yesterday)
("T" . org-roam-dailies-capture-tomorrow))
:bind-keymap ("C-c n d" . org-roam-dailies-map)
:config
(require 'org-roam-dailies) ;; Ensure the keymap is available
(org-roam-db-autosync-mode))
(defun org-roam-create-node-from-headline ()
"Create an Org-roam note from the current headline and jump to it.
Normally, insert the headlines title using the #title: file-level property
and delete the Org-mode headline. However, if the current headline has a
Org-mode properties drawer already, keep the headline and dont insert
#+title:'. Org-roam can extract the title from both kinds of notes, but using
#+title: is a bit cleaner for a short note, which Org-roam encourages."
(interactive)
(let ((title (nth 4 (org-heading-components)))
(has-properties (org-get-property-block)))
(org-cut-subtree)
(org-roam-find-file title nil nil 'no-confirm)
(org-paste-subtree)
(unless has-properties
(kill-line)
(while (outline-next-heading)
(org-promote)))
(goto-char (point-min))
(when has-properties
(kill-line)
(kill-line))))
(use-package logseq-org-roam
:straight (:host github
:repo "sbougerel/logseq-org-roam"
:files ("*.el")))
(transient-define-prefix org-roam-menu ()
"Transient menu for task management shortcuts."
["Node" ("c" "Capture" org-roam-node-find)
("i" "Insert" org-roam-node-insert)
("r" "Refile" org-roam-create-node-from-headline)
("f" "Find" org-roam-node-find) ]
["Dailies" ("t" "Today" org-roam-dailies-capture-today)
("y" "Yesterday" org-roam-dailies-capture-yesterday)
("n" "Tomorrow" org-roam-dailies-capture-tomorrow)]
["Logseq" ("R" "Fix links" logseq-org-roam)]
["Org Roam UI" ("U" "Enable org-roam-ui-mode" org-roam-ui-mode)])
(define-key global-map (kbd "C-c o n") #'org-roam-menu)
(use-package org-roam-ui
:straight
(:host github :repo "org-roam/org-roam-ui" :branch "main" :files ("*.el" "out"))
:after org-roam
;; normally we'd recommend hooking orui after org-roam, but since org-roam does not have
;; a hookable mode anymore, you're advised to pick something yourself
;; if you don't care about startup time, use
:hook (after-init . org-roam-ui-mode)
:config
(setq org-roam-ui-sync-theme t
org-roam-ui-follow t
org-roam-ui-update-on-save t
org-roam-ui-open-on-start t))
;; Bibliography stuff
(setq bibtex-dialect 'biblatex) ;; Use biblatex instead of bibtex.
(use-package biblio)
(defun bibtex-online-entry ()
(interactive)
(bibtex-entry "Online"))
(defun bibtex-misc-entry ()
(interactive)
(bibtex-entry "Misc"))
(defun bibtex-software-entry ()
(interactive)
(bibtex-entry "Software"))
(transient-define-prefix bibtex-transient-menu ()
"Transient menu for task management shortcuts."
["Biblio" ("d" "doi.org" doi-insert-bibtex)
("x" "arXiv" arxiv-lookup)]
["Templates" ("o" "Online Resource" bibtex-online-entry)
("m" "Misc" bibtex-misc-entry)
("s" "Software" bibtex-software-entry)])
(define-key bibtex-mode-map (kbd "C-c r") #'bibtex-transient-menu)
;;; Literate Programming
;; Racket
(use-package ob-racket
:after org
:config
(add-hook 'ob-racket-pre-runtime-library-load-hook
#'ob-racket-raco-make-runtime-library)
:straight (ob-racket
:type git :host github :repo "hasu/emacs-ob-racket"
:files ("*.el" "*.rkt")))
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(racket . t)
))
;;; LaTeX configuration
(require 'ox-latex)
(setq org-latex-pdf-process
'("pdflatex -interaction nonstopmode -output-directory %o %f"
"bibtex %b"
"pdflatex -interaction nonstopmode -output-directory %o %f"
"pdflatex -interaction nonstopmode -output-directory %o %f"))
(setq org-latex-with-hyperref nil) ;; stop org adding hypersetup{author..} to latex export
;; (setq org-latex-prefer-user-labels t)
;; deleted unwanted file extensions after latexMK
(setq org-latex-logfiles-extensions
(quote ("lof" "lot" "tex~" "aux" "idx" "log" "out" "toc" "nav" "snm" "vrb" "dvi" "fdb_latexmk" "blg" "brf" "fls" "entoc" "ps" "spl" "bbl" "xmpi" "run.xml" "bcf" "acn" "acr" "alg" "glg" "gls" "ist")))
(unless (boundp 'org-latex-classes)
(setq org-latex-classes nil))
(provide 'org-custom)
;;; org-custom.el ends here

91
writing.el Normal file
View file

@ -0,0 +1,91 @@
;;; writing.el --- Writing behavior configuration -*- lexical-binding: t -*-
;; This file is not part of GNU Emacs
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; commentary
;;; Code:
;; Save place in file
(save-place-mode 1)
;; Replace selection when pasting
(delete-selection-mode 1)
;; Enable auto-pairing for parens
(electric-pair-mode 1)
;; Enable narrowing
(put 'narrow-to-region 'disabled nil)
;;; Spell Checking
;; Use hunspell if available
(if (file-exists-p "/usr/bin/hunspell")
(progn
(eval-after-load "ispell"
'(progn
(setq ispell-program-name "hunspell"
ispell-dictionary "en_AU"
ispell-alternate-dictionary (file-truename (concat user-emacs-directory "en_AU.dict"))
ispell-local-dictionary-alist '(("en_AU" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "en_AU,en_AU-med") nil utf-8)))
(defun ispell-get-coding-system () 'utf-8)))
(eval-after-load "flyspell"
'(progn
(setq ispell-program-name "hunspell"
ispell-dictionary "en_AU"
ispell-alternate-dictionary (file-truename (concat user-emacs-directory "en_AU.dict"))
ispell-local-dictionary-alist '(("en_AU" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "en_AU,en_AU-med") nil utf-8)))
(defun ispell-get-coding-system () 'utf-8)))))
;; Visual undo tree
(use-package vundo
:straight (vundo :type git :host github :repo "casouri/vundo")
:config
;; Take less on-screen space.
(setq vundo-compact-display t)
:bind
(:map global-map
("C-x u" . vundo)))
;; Rainbow delimiters. Almost necessary for lisp.
(use-package rainbow-delimiters
:hook ((prog-mode . rainbow-delimiters-mode)))
;; Snippeting
(use-package yasnippet
;; This seems to be a necessity for working on the daemon
:hook ((server-after-make-frame . (lambda () (yas-global-mode 1))))
:config
(yas-global-mode 1)
(setq yas-snippet-dirs `(,(concat user-emacs-directory "snippets"))))
(use-package yasnippet-snippets
:after (yasnippet))
;; Indentation
(straight-use-package 'aggressive-indent-mode)
(global-aggressive-indent-mode 1)
(add-hook 'shell-mode-hook
#'(lambda () (aggressive-indent-mode 0)))
(provide 'writing)
;;; writing.el ends here