Archived
1
0
Fork 0
This repository has been archived on 2024-10-19. You can view files and clone it, but cannot push or open issues or pull requests.
emacs/init.el

1628 lines
62 KiB
EmacsLisp
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;; -*- lexical-binding: t; -*-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; windows performance tweaks
(when (eq system-type 'windows-nt)
; https://lists.gnu.org/archive/html/bug-gnu-emacs/2012-10/msg00274.html
; https://gioorgi.com/2013/solving-emacs-freeze-andor-slowdown-on-windows7/
(setq w32-get-true-file-attributes nil)
; https://www.reddit.com/r/emacs/comments/c9ef5i/comment/esx5ndr/
(setq inhibit-compacting-font-caches t)
; https://www.reddit.com/r/emacs/comments/c9ef5i/comment/esx5snw/
(when (boundp 'w32-pipe-read-delay)
(setq w32-pipe-read-delay 0))
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; misc performance tweaks
;; Make gc pauses faster by decreasing the threshold.
(setq gc-cons-threshold (* 2 1000 1000))
;; disable warnings from popping up, they are still logged
(setq warning-suppress-types '((comp)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; os specific config
(when (eq system-type 'windows-nt)
; Fix resolution of ~ to match other client paths
(setenv "HOME" "C:/Users/mcros/Nextcloud")
(setq default-directory "C:/Users/mcros/Nextcloud")
; add executables to path ahead of them being used by extensions / emacs stuff
(add-to-list 'exec-path "C:/Users/mcros/OneDrive/Programs/PortableApps/sqlite3")
(add-to-list 'exec-path "C:/msys64/usr/bin/unzip.exe")
(setenv "PATH" (concat "C:\\msys64\\mingw64\\bin;" (getenv "PATH")))
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Packages related
(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) ; this goes in chemacs2 init -- DO NOT UNCOMMENT
(package-initialize) ; this goes in chemacs2 init -- DO NOT UNCOMMENT
(add-to-list 'package-selected-packages
'(which-key all-the-icons revert-buffer-all scratch persistent-scratchrainbow-mode rainbow-delimiters focus zoom popwin dired-single diredfl doominhibitinhibit-modeline helpful helm helm-org dired-rainbow dired-rainbow-listing dired-single dash s origami modus-themes use-package)
)
(add-to-list 'package-selected-packages
'(xclip)
)
(require 'use-package)
; ensure elisp plugins are compiled
(require 'dash)
(require 'f)
(defun was-compiled-p (path)
"Does the directory at PATH contain any .elc files?"
(--any-p (f-ext? it "elc") (f-files path)))
(defun ensure-packages-compiled ()
"If any packages installed with package.el aren't compiled yet, compile them."
(--each (f-directories package-user-dir)
(unless (was-compiled-p it)
(byte-recompile-directory it 0))))
(ensure-packages-compiled)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; dont wrap lines
(set-default 'truncate-lines t)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; highlight current line global -- Do NOT use
;(global-hl-line-mode +1) ; this overrides std hl-line-mode buffer config
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; hl-line-mode based on major mode hook (on by default, off for listed major modes)
; to get major mode: (message "%s" major-mode)
; to get minor modes: (describe-mode)
(add-hook 'minibuffer-setup-hook #'(lambda () (hl-line-mode 0)))
(add-hook 'after-change-major-mode-hook
#'(lambda () (hl-line-mode (if (member major-mode '(org-agenda-mode)) 0 1))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; backups
(defconst my/backup-dir
(expand-file-name "backups/" user-emacs-directory))
(unless (file-exists-p my/backup-dir)
(make-directory my/backup-dir))
(setq backup-directory-alist `((".*" . ,my/backup-dir)))
(setq auto-save-list-file-prefix my/backup-dir)
(setq auto-save-file-name-transforms
`(("\\(?:[^/]*/\\)*\\(.*\\)" ,(concat my/backup-dir "\\1") t)))
(setq backup-by-copying t) ; safest form of backup file creation
; Config backups so we have *more*, not less
(setq delete-old-versions t
kept-new-versions 1
kept-old-versions 3
version-control t
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; add all-the-icons so status icons (normally handled by a font patched w/ nerd font)
(require 'all-the-icons)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; which-key
(require 'which-key)
;; Allow C-h to trigger which-key before it is done automatically
;(setq which-key-show-early-on-C-h t)
;; make sure which-key doesn't show normally but refreshes quickly after it is
;; triggered.
;(setq which-key-idle-delay 10000)
;(setq which-key-idle-secondary-delay 0.05)
(which-key-mode)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; dired adjustments
(with-eval-after-load 'dired
(require 'dired-x)
;; Set dired-x global variables here. For example:
;; (setq dired-x-hands-off-my-keys nil)
)
(require 'dired-single)
(setq dired-single-use-magic-buffer t)
(setq dired-single-magic-buffer-name "dired - main")
; open dirs in same dired window + open files in main window
; facilitates static file browser sidebar
(defun my/dired-open()
(interactive)
(setq file (dired-get-file-for-visit))
(if (equal (file-directory-p file) t)
(progn
(dired-single-buffer)
)
(progn
(dired-find-file-other-window)
)
)
)
(defun my/dired-init ()
"Bunch of stuff to run for dired, either immediately or when it's
loaded."
;; <add other stuff here>
(define-key dired-mode-map [remap dired-find-file]
'my/dired-open)
(define-key dired-mode-map [remap dired-mouse-find-file-other-window]
'my/dired-open)
(define-key dired-mode-map [remap dired-up-directory]
'dired-single-up-directory))
;; if dired's already loaded, then the keymap will be bound
(if (boundp 'dired-mode-map)
;; we're good to go; just add our bindings
(my/dired-init)
;; it's not loaded yet, so add our bindings to the load-hook
(add-hook 'dired-load-hook 'my/dired-init))
; icons / colors / line tunes
(add-hook 'dired-mode-hook
(lambda ()
(interactive)
(all-the-icons-dired-mode 1)
(hl-line-mode 1)))
(use-package diredfl
:commands diredfl-global-mode
:init
(diredfl-global-mode))
; hide dired details by default, use `(` to toggle
(add-hook 'dired-mode-hook 'dired-hide-details-mode)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; coding general (in case its ever on)
(add-hook 'prog-mode-hook #'rainbow-delimiters-mode)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; origami config
(use-package origami
:demand
:config
(define-prefix-command 'origami-mode-map)
(define-key ctl-x-map (kbd "z") 'origami-mode-map)
(global-origami-mode)
:bind
(:map origami-mode-map
("o" . origami-open-node)
("O" . origami-open-node-recursively)
("c" . origami-close-node)
("C" . origami-close-node-recursively)
("a" . origami-toggle-node)
("t" . origami-recursively-toggle-node)
("R" . origami-open-all-nodes)
("M" . origami-close-all-nodes)
("v" . origami-show-only-node)
("k" . origami-previous-fold)
("j" . origami-forward-fold)
("x" . origami-reset)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Setup global keys
(cua-mode 1)
(windmove-default-keybindings 'meta)
(global-set-key (kbd "<escape>") 'keyboard-escape-quit)
(global-set-key (kbd "M-g") 'keyboard-quit)
(global-set-key (kbd "M-m") 'menu-bar-open)
(global-set-key (kbd "C-x C-z") nil)
(global-set-key (kbd "C-g") 'goto-line)
(global-set-key (kbd "C-c u") 'toggle-truncate-lines)
(global-set-key (kbd "C-o") 'helm-find-files)
(global-set-key (kbd "C-a") 'mark-whole-buffer)
(global-set-key (kbd "C-S-a") 'my/smart-beginning-of-line)
(global-set-key (kbd "M-a") 'my/smart-beginning-of-line)
(global-set-key (kbd "C-S-e") 'my/smart-end-of-line)
(global-set-key (kbd "M-e") 'my/smart-end-of-line)
(global-set-key (kbd "C-c v") 'previous-buffer)
(global-set-key (kbd "C-c w")
(lambda ()
(interactive)
(whitespace-mode)
(whitespace-newline-mode)
)
)
; tmux keybind compatibility
(setq windmove-wrap-around t) ; tmux does wraparound for window movements, enable for emacs
(define-key global-map (kbd "C-b") (make-sparse-keymap))
; prevent ardux typos w/ window sizing
(global-set-key (kbd "C-<up>") nil)
(global-set-key (kbd "C-<down>") nil)
(global-set-key (kbd "C-<right>") nil)
(global-set-key (kbd "C-<left>") nil)
; main tmux key binds
; *remember* C-b b in tmux sends a single C-b to the underlying tty
(global-set-key (kbd "C-b c") (lambda () (interactive)
(let ((frame (make-frame))
; yes, this is silly
; need a name for the buffer and emacs always re-uses buffers based on name
; to keep w/ tmux, use an arbitrary, 'unique' name of the new buffer
(buffer (get-buffer-create (format-time-string "%Y-%m-%d %H:%M:%S" (current-time)))))
(select-frame-set-input-focus frame)
(display-buffer buffer)
(switch-to-buffer buffer)
(delete-other-windows)
)))
(global-set-key (kbd "C-b &") 'delete-frame)
(global-set-key (kbd "C-b \"") 'split-window-below)
(global-set-key (kbd "C-b %") 'split-window-right)
(global-set-key (kbd "C-b x") 'delete-window)
(global-set-key (kbd "C-b <up>") 'windmove-up)
(global-set-key (kbd "C-b <down>") 'windmove-down)
(global-set-key (kbd "C-b <left>") 'windmove-left)
(global-set-key (kbd "C-b <right>") 'windmove-right)
(global-set-key (kbd "C-b !") 'make-frame)
(global-set-key (kbd "C-b C-<up>") 'enlarge-window)
(global-set-key (kbd "C-b C-<down>") 'shrink-window)
(global-set-key (kbd "C-b C-<right>") 'enlarge-window-horizontally)
(global-set-key (kbd "C-b C-<left>") 'shrink-window-horizontally)
(global-set-key (kbd "C-b :") 'helm-M-x)
(global-set-key (kbd "C-b SPC") 'zoom-mode) ; use 'balance-windows function if not using zoom.el or golden-ratio
;; Lookup the current symbol at point. C-c C-d is a common keybinding
;; for this in lisp modes.
(global-set-key (kbd "M-h") 'describe-bindings)
(global-set-key (kbd "C-c C-d") #'helpful-at-point)
(bind-key* "M-%" 'query-replace-region-or-from-top)
(global-set-key (kbd "C-h") 'query-replace-region-or-from-top)
; more vscode/gui styled find/replace
(global-set-key (kbd "C-s") 'isearch-forward)
(global-set-key (kbd "C-f") 'isearch-forward)
; C-h find/replace
(global-set-key (kbd "C-h") 'query-replace)
(global-set-key (kbd "C-H") 'query-replace-regexp)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Set some global config
(setq inhibit-splash-screen t) ; why do you need to tell me things i know by default
(setq inhibit-startup-screen t)
(setq inhibit-startup-echo-area-message t)
(setq-default tab-width 4) ; sanity!
(electric-indent-mode 0) ; return should NOT fuck with the indentation of the previous line
;; make typing delete/overwrites selected text
(delete-selection-mode 1)
(setq frame-inhibit-implied-resize t)
(setq pixel-scroll-precision-mode t)
;; UTF-8 as default encoding
(set-default-coding-systems 'utf-8)
(set-language-environment "UTF-8")
(set-default-coding-systems 'utf-8-unix)
; dont pop-up tooltips, show in echo area instead
(setq tooltip-mode nil)
(setq tooltip-use-echo-area t)
; auto revert when files/dired changes
(setq global-auto-revert-non-file-buffers t)
(global-auto-revert-mode 1)
; Clipboard integration
(use-package xclip :config (xclip-mode 1))
;; use y or n instead of yes or no
(fset 'yes-or-no-p 'y-or-n-p)
; org mode syntax highlight code blocks
(setq org-src-fontify-natively t)
; org mode - multiple agenda buffers
(setq org-agenda-sticky t)
; org show file name in refile, also allows refiling to the file as a top level headline
(setq org-refile-use-outline-path 'file)
(setq org-outline-path-complete-in-steps nil)
(setq org-refile-allow-creating-parent-nodes 'confirm)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; set font for emoji -- DOES NOT WORK ON MOBILE
(when (eq system-type 'windows-nt)
(set-fontset-font
t
'(#x1f300 . #x1fad0)
(cond
((member "Noto Color Emoji" (font-family-list)) "Noto Color Emoji")
((member "Noto Emoji" (font-family-list)) "Noto Emoji")
((member "Segoe UI Emoji" (font-family-list)) "Segoe UI Emoji")
((member "Symbola" (font-family-list)) "Symbola")
((member "Apple Color Emoji" (font-family-list)) "Apple Color Emoji"))
;; Apple Color Emoji should be before Symbola, but Richard Stallman skum disabled it.
;; GNU Emacs Removes Color Emoji Support on the Mac
;; http://ergoemacs.org/misc/emacs_macos_emoji.html
;;
)
(custom-theme-set-faces
'user
'(default ((t ( :family "MonoLisa Variable" :height 100))))
'(variable-pitch ((t (:family "Atkinson Hyperlegible" :height 120))))
'(fixed-pitch ((t ( :family "MonoLisa Variable" :height 120))))
)
; (custom-theme-set-faces
; 'user
; '(org-agenda ((t (:inherit fixed-pitch))))
; '(org-block ((t (:inherit fixed-pitch))))
; '(org-code ((t (:inherit (shadow fixed-pitch)))))
; '(org-document-info ((t (:inherit fixed-pitch))))
; '(org-document-info-keyword ((t (:inherit (shadow fixed-pitch)))))
; '(org-indent ((t (:inherit (org-hide fixed-pitch)))))
; '(org-link ((t (:inherit (shadow fixed-pitch)))))
; '(org-meta-line ((t (:inherit (font-lock-comment-face fixed-pitch)))))
; '(org-property-value ((t (:inherit fixed-pitch))) t)
; '(org-special-keyword ((t (:inherit (font-lock-comment-face fixed-pitch)))))
; '(org-table ((t (:inherit fixed-pitch))))
; '(org-tag ((t (:inherit (shadow fixed-pitch) :weight bold :height 0.8))))
; '(org-verbatim ((t (:inherit (shadow fixed-pitch)))))
;)
;(add-hook 'org-mode-hook 'variable-pitch-mode)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; line numbers
; find major mode : M-x eval-expression [enter] major-mode
(require 'display-line-numbers)
(defcustom display-line-numbers-exempt-modes '(org-mode org-agenda-mode)
"Major modes on which to disable the linum mode, exempts them from global requirement"
:group 'display-line-numbers
:type 'list
:version "green")
(defun display-line-numbers--turn-on ()
"turn on line numbers but excempting certain majore modes defined in `display-line-numbers-exempt-modes'"
(if (and
(not (member major-mode display-line-numbers-exempt-modes))
(not (minibufferp)))
(display-line-numbers-mode)))
(global-display-line-numbers-mode)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; whitespace
(progn
;; Make whitespace-mode with very basic background coloring for whitespaces.
;; http://xahlee.info/emacs/emacs/whitespace-mode.html
(setq whitespace-style (quote (face spaces tabs newline space-mark tab-mark newline-mark )))
;; Make whitespace-mode and whitespace-newline-mode use “¶” for end of line char and “▷” for tab.
(setq whitespace-display-mappings
;; all numbers are unicode codepoint in decimal. e.g. (insert-char 182 1)
'(
(space-mark 32 [183] [46]) ; SPACE 32 「 」, 183 MIDDLE DOT 「·」, 46 FULL STOP 「.」
(newline-mark 10 [8629 10]) ; LINE FEED,
(tab-mark 9 [8594 9] [92 9]) ; tab
)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; modus-themes setup and enable
(require-theme 'modus-themes)
(setq modus-themes-region '(no-extend)
modus-themes-fringes 'subtle
modus-themes-scale-headings t
)
(load-theme 'modus-vivendi :no-confirm)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; focus on text (turn on as desired/needed ; off by default
(require 'focus)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; helm
(setq completion-styles '(flex))
(require 'helm-color)
(require 'helm-bookmark)
(helm-autoresize-mode 1)
(setq helm-display-header-line nil
helm-autoresize-max-height 30
helm-autoresize-min-height 30)
(setq helm-split-window-in-side-p t ; open helm buffer inside current window, not occupy whole other window
helm-ff-file-name-history-use-recentf t
helm-echo-input-in-header-line t)
(global-set-key (kbd "C-x C-d") 'helm-browse-project)
(global-set-key (kbd "C-x b") 'list-buffers)
(global-set-key (kbd "C-x f") 'focus-mode)
(global-set-key (kbd "C-x c") 'focus-read-only-mode)
(define-key global-map [remap find-file] 'helm-find-files)
(define-key global-map [remap occur] 'helm-occur)
(define-key global-map [remap list-buffers] 'helm-buffers-list)
(define-key global-map [remap dabbrev-expand] 'helm-dabbrev)
(define-key global-map [remap execute-extended-command] 'helm-M-x)
(define-key global-map [remap apropos-command] 'helm-apropos)
(define-key global-map [remap bookmark-jump] 'helm-filtered-bookmarks)
(unless (boundp 'completion-in-region-function)
(define-key lisp-interaction-mode-map [remap completion-at-point] 'helm-lisp-completion-at-point)
(define-key emacs-lisp-mode-map [remap completion-at-point] 'helm-lisp-completion-at-point))
(add-hook 'kill-emacs-hook #'(lambda () (and (file-exists-p "$CONF_FILE") (delete-file "$CONF_FILE"))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; doom-modeline
(require 'doom-modeline)
(setq all-the-icons-color-icons nil)
(setq find-file-visit-truename t)
(doom-modeline-mode 1)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; flyspell
(require 'flyspell)
(setenv "LANG" "en_US")
(setq ispell-program-name "hunspell")
(setq ispell-dictionary "en_US")
(use-package flyspell :demand t
:config
(use-package
flyspell-correct-helm)
(defun flyspell-most-modes()
(add-hook 'text-mode-hook (lambda () (flyspell-mode 1) ))
(add-hook 'prog-mode-hook (lambda () (flyspell-mode 1) ))
(dolist (hook '(change-log-mode-hook log-edit-mode-hook org-agenda))
(add-hook hook (lambda ()
(flyspell-mode -1)))))
(flyspell-most-modes)
)
(defun flyspell-on-for-buffer-type ()
"Enable Flyspell appropriately for the major mode of the current buffer. Uses `flyspell-prog-mode' for modes derived from `prog-mode', so only strings and comments get checked. All other buffers get `flyspell-mode' to check all text. If flyspell is already enabled, does nothing."
(interactive)
(if (not (symbol-value flyspell-mode)) ; if not already on
(progn
(if (derived-mode-p 'prog-mode)
(progn
(message "Flyspell on (code)")
(flyspell-prog-mode))
;; else
(progn
(message "Flyspell on (text)")
(flyspell-mode 1)))
)))
(defun flyspell-toggle ()
"Turn Flyspell on if it is off, or off if it is on. When turning on, it uses `flyspell-on-for-buffer-type' so code-vs-text is handled appropriately."
(interactive)
(if (symbol-value flyspell-mode)
(progn ; flyspell is on, t it off
(message "Flyspell off")
(flyspell-mode -1))
; else - flyspell is off, turn it on
(flyspell-on-for-buffer-type)))
; flyspell keyboard shortcuts
(global-set-key (kbd "C-x y") 'flyspell-toggle)
(global-set-key (kbd "C-x w") 'flyspell-correct-wrapper)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; zoom (replaces golden-ratio)
(require 'zoom)
; sizing
;(setq zoom-size '(0.618 . 0.618))
(defun my-zoom-size-callback ()
(cond
((equal major-mode 'dired-mode) '(0.20 . 0.20))
((equal major-mode 'eww-mode) '(0.75 . 0.75))
((equal major-mode 'eshell-mode) '(0.75 . 0.75))
((string-equal (window-dedicated-p) "side") '(0.15 . 0.15))
(t '(0.618 . 0.618))
))
; tuning
(custom-set-variables
'(zoom-size 'my-zoom-size-callback)
'(temp-buffer-resize-mode t)
; '(zoom-ignored-major-modes '(dired-mode))
; '(zoom-ignore-predicates '((lambda ()
; (if (string-equal (window-dedicated-p) "side") 'Y' nil)
; )))
)
(global-set-key (kbd "C-x +") 'zoom)
(zoom-mode t)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; popup windows
(use-package popwin
:bind ( :map popwin:keymap
("h" . popwin:close-popup-window)
("r" . my/workspace-frame-scratch)
("R" . my/popwin-scratch)
("q" . my/popwin-quick-ref))
:config
(global-set-key (kbd "C-w") popwin:keymap)
(push "_quick_reference.org" popwin:special-display-config))
(popwin-mode 1)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; scratch buffer
; set mode options -- borrowed from : https://emacsredux.com/blog/2014/07/25/configure-the-scratch-buffers-mode/
; fundamental-mode / text-mode / org-mode
(setq initial-major-mode 'text-mode)
(setq initial-scratch-message "original scratch buffer")
; persistent-scratch (melpa)
(persistent-scratch-autosave-mode 0)
(global-set-key (kbd "C-c r S") 'persistent-scratch-save)
(global-set-key (kbd "C-c r r") 'persistent-scratch-restore)
(global-set-key (kbd "C-c r w") 'persistent-scratch-save-to-file)
(global-set-key (kbd "C-c r l") 'persistent-scratch-restore-from-file)
; scratch buffers frame - use 'scratch and prompts to manage as needed
(defun my/workspace-frame-scratch()
(interactive)
(let ((frame (make-frame)))
(select-frame-set-input-focus frame)
(delete-other-windows)
(scratch 'text-mode)
)
)
; scratch buffers frame, prompt for initial mode - use 'scratch and prompts to manage as needed
(defun my/workspace-frame-scratch-prompt()
(interactive)
(let ((frame (make-frame)))
(select-frame-set-input-focus frame)
(delete-other-windows)
(scratch (let ((current-prefix-arg t)) (scratch--buffer-querymode)))
)
)
; scratch.el (multi-scratch / melpa)
(global-set-key (kbd "C-c r s") #'my/workspace-frame-scratch)
(global-set-key (kbd "C-c r p") #'my/workspace-frame-scratch-prompt)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; generic functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; open org agenda in top side window
; BUGGED AND UNUSED, agenda does NOT work properly in side windows
(defun my/org-agenda-on-top ()
(defvar parameters
'(window-parameters . ((no-other-window . t)
(no-delete-other-windows . t))))
(setq fit-window-to-buffer-horizontally t)
(setq window-resize-pixelwise t)
(interactive)
(display-buffer-in-side-window
(get-buffer "*Org Agenda*") `((side . top) (slot . 0)
(window-width . fit-window-to-buffer)
(preserve-size . (t . nil)) , parameters))
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; open text-mode scratch in right side window
(defun my/scratch-text-on-right ()
(defvar parameters
'(window-parameters . ((no-other-window . t)
(no-delete-other-windows . t))))
(setq fit-window-to-buffer-horizontally t)
(setq window-resize-pixelwise t)
"Display `default-directory' in side window on right, hiding details."
(interactive)
(let ((buffer (scratch 'text-mode)))
(display-buffer-in-side-window
buffer `((side . right) (slot . -1)
(window-width . fit-window-to-buffer)
, parameters)
)
)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; open text-mode scratch in left side window
(defun my/scratch-text-on-left ()
(defvar parameters
'(window-parameters . ((no-other-window . t)
(no-delete-other-windows . t))))
(setq fit-window-to-buffer-horizontally t)
(setq window-resize-pixelwise t)
"Display `default-directory' in side window on left, hiding details."
(interactive)
(let ((buffer (scratch 'text-mode)))
(display-buffer-in-side-window
buffer `((side . left) (slot . -1)
(window-width . fit-window-to-buffer)
, parameters)
)
)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; open text-mode scratch in right side window
(defun my/scratch-prompt-on-right ()
(defvar parameters
'(window-parameters . ((no-other-window . t)
(no-delete-other-windows . t))))
(setq fit-window-to-buffer-horizontally t)
(setq window-resize-pixelwise t)
"Display `default-directory' in side window on left, hiding details."
(interactive)
(let ((buffer (scratch (let ((current-prefix-arg t)) (scratch--buffer-querymode)))))
(display-buffer-in-side-window
buffer `((side . right) (slot . -1)
(window-width . fit-window-to-buffer)
, parameters)
)
)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; open text-mode scratch in left side window
(defun my/scratch-prompt-on-left ()
(defvar parameters
'(window-parameters . ((no-other-window . t)
(no-delete-other-windows . t))))
(setq fit-window-to-buffer-horizontally t)
(setq window-resize-pixelwise t)
"Display `default-directory' in side window on left, hiding details."
(interactive)
(let ((buffer (scratch (let ((current-prefix-arg t)) (scratch--buffer-querymode)))))
(display-buffer-in-side-window
buffer `((side . left) (slot . -1)
(window-width . fit-window-to-buffer)
, parameters)
)
)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; text-mode scratch buffer as a pop up window
(defun my/popwin-scratch ()
(interactive)
(popwin:popup-buffer (scratch 'text-mode)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; quick reference notes as a pop up window
(defun my/popwin-quick-ref ()
(interactive)
(when (eq system-type 'windows-nt)
(popwin:find-file "~/org/_quick_reference.org"))
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; modern home/end via C-a and C-e
(defun my/smart-beginning-of-line ()
"Move point to `beginning-of-line'. If repeat command it cycle
position between `back-to-indentation' and `beginning-of-line'."
(interactive "^")
(if (eq last-command 'smart-beginning-of-line)
(if (= (line-beginning-position) (point))
(back-to-indentation)
(beginning-of-line))
(back-to-indentation)))
(defun my/smart-end-of-line ()
"Move point to `end-of-line'. If repeat command it cycle
position between last non-whitespace and `end-of-line'."
(interactive "^")
(if (and (eq last-command 'my/smart-end-of-line)
(= (line-end-position) (point)))
(skip-syntax-backward " " (line-beginning-position))
(end-of-line)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; function to kill all non special, non active buffers
(defun my/kill-other-buffers ()
"Kill all buffers but the current one. Don't mess with special buffers. Kill dired buffers"
(interactive)
; dired buffer cleanup
(mapc
(lambda (buffer)
(when (eq 'dired-mode (buffer-local-value 'major-mode buffer))
(kill-buffer buffer)))
(buffer-list))
; non special buffer cleanup
(dolist (buffer (buffer-list))
(unless (or (eql buffer (current-buffer)) (not (buffer-file-name buffer)))
(kill-buffer buffer))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; find/replace all (interactive)
(defun my/query-replace-region-or-from-top ()
"If marked, query-replace for the region, else for the whole buffer (start from the top)"
(interactive)
(progn
(let ((orig-point (point)))
(if (use-region-p)
(call-interactively 'query-replace)
(save-excursion
(goto-char (point-min))
(call-interactively 'query-replace)))
(message "Back to old point.")
(goto-char orig-point))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; dock current buffer in given side panel
(defun my/dock-current-buffer-on-side (selected_side)
(defvar parameters
'(window-parameters . ((no-delete-other-windows . t))))
(setq fit-window-to-buffer-horizontally t)
(setq window-resize-pixelwise t)
"Display `default-directory' in side window on left, hiding details."
(interactive)
(let ((buffer (current-buffer)))
(display-buffer-in-side-window
buffer `((side . ,selected_side) (slot . -1)
(window-width . fit-window-to-buffer)
, parameters)
)
)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; dock current buffer on right
(defun my/dock-current-buffer-on-right ()
(interactive)
(my/dock-current-buffer-on-side 'right)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; dock current buffer on right
(defun my/dock-current-buffer-on-left ()
(interactive)
(my/dock-current-buffer-on-side 'left)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; dock current buffer on right
(defun my/dock-current-buffer-on-top ()
(interactive)
(my/dock-current-buffer-on-side 'top)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; dock current buffer on right
(defun my/dock-current-buffer-on-bottom ()
(interactive)
(my/dock-current-buffer-on-side 'bottom)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; perform days math on a date string
(defun my/date-string-days-math (date-string days-to-subtract)
(format-time-string "%Y-%m-%d"
(encode-time
(decoded-time-add (parse-time-string date-string) (make-decoded-time :day days-to-subtract))
)
))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; subtract 7 days from a date string
(defun my/date-string-minus-seven-days (date-string)
(my/date-string-days-math date-string -7)
)
; subtract 3 days from a date string
(defun my/date-string-minus-three-days (date-string)
(my/date-string-days-math date-string -3)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; various workspaces used day to day
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; create new frame if not inside termux
(defun my/possibly-create-frame ()
(interactive)
(let ((frame (make-frame)))
(select-frame-set-input-focus frame))
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; org-mode workspace
(defun my/workspace-org ()
(interactive)
(my/possibly-create-frame)
; start with fresh frame
(delete-other-windows)
(my/kill-other-buffers)
(ignore-errors (kill-buffer "*scratch*"))
; Open main files used as 'gateway' to everything else
(find-file "~/org/_habits.org")
(find-file "~/org/_todo.org")
(find-file "~/org/_slipbox.org")
(find-file "~/org/_index.org")
; top window for habits/scheduled
(org-agenda nil "h")
(goto-char 0)
; main todo/slipbox
(split-window-below)
(next-multiframe-window)
(org-agenda nil "t")
(goto-char 0)
(next-multiframe-window)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; org-mode workspace for general kb work that doesnt use the agenda
(defun my/workspace-org-index ()
(interactive)
(my/possibly-create-frame)
; start with fresh frame
(delete-other-windows)
; setup scratch on right
(my/scratch-text-on-right)
(goto-char 0)
; Open main files used as 'gateway' to everything else
(find-file "~/org/_index.org")
(goto-char 0)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; disable toolbar in gui
(tool-bar-mode -1)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; additional packages
(add-to-list 'package-selected-packages
'(org-tidy org-auto-expand ox-pandoc org-super-agenda org-alert burnt-toast alert)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Load misc extensions
(require 'org)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; org-alert - windows only
(when (eq system-type 'windows-nt)
(use-package org-alert)
(use-package burnt-toast
:config
(require 'burnt-toast-alert)
(setq alert-default-style 'burnt-toast)
)
(setq org-alert-interval 300
org-alert-notify-cutoff 5
org-alert-notify-after-event-cutoff 5)
(org-alert-enable)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Update/add auto file handling
(add-to-list 'auto-mode-alist '("\\.org\\'" . org-mode))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; hide property drawers / minimize them
(use-package org-tidy
:ensure t
:config
(setq org-tidy-top-property-style 'keep)
(setq org-tidy-properties-style 'inline)
(setq org-tidy-properties-inline-symbol "")
(setq org-tidy-property-drawer-flag t)
;org-tidy-property-drawer-property-whitelist
(setq org-tidy-property-drawer-property-blacklist '("TIDY_DISABLE" "TIDY_OFF"))
(setq org-tidy-general-drawer-flag t)
;org-tidy-general-drawer-name-whitelist
;org-tidy-general-drawer-name-blacklist
:hook
(org-mode . org-tidy-mode))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; keyboard shortcuts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Setup global keys
(global-set-key (kbd "C-c a") 'org-agenda)
(global-set-key (kbd "C-c c") 'org-capture)
(global-set-key (kbd "C-c m x") 'org-cut-subtree)
(global-set-key (kbd "C-c m r") 'org-archive-subtree)
(global-set-key (kbd "C-c m s") 'org-agenda-schedule)
(global-set-key (kbd "C-c m d") 'org-agenda-deadline)
(global-set-key (kbd "C-w t t") 'org-tidy-untidy-buffer)
(global-set-key (kbd "C-w t h") 'org-tidy-toggle)
(global-set-key (kbd "C-c d n")
(lambda () (interactive) (find-file "~/org/_index.org"))
)
(global-set-key (kbd "C-c d t")
(lambda () (interactive) (find-file "~/org/_todo.org"))
)
(global-set-key (kbd "C-c d s")
(lambda () (interactive) (find-file "~/org/_slipbox.org"))
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Set some orgmode config
(defun my/org_timestamp ()
(interactive)
(org-insert-time-stamp (current-time) (current-time) "t")
)
(org-defkey org-mode-map (kbd "C-c !") #'my/org_timestamp)
(setq org-startup-folded t)
(setq org-return-follows-link t)
(setq org-startup-folded t)
(setq org-support-shift-select t)
(setq org-log-into-drawer t)
(setq org-log-done t)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; automatic ids for everything
; From https://web.archive.org/web/20220512120917/https://stackoverflow.com/questions/13340616/assign-ids-to-every-entry-in-org-mode
; From https://stackoverflow.com/questions/13340616/assign-ids-to-every-entry-in-org-mode
; This hunk of lisp automatically adds IDs to every header in an org file when you hit save. it usese org-id which can generated IDs of various types including timestamps. Im using the default UUIDs
(defun stackoverflow/org-add-ids-to-headlines-in-file ()
"cargo cult from stackoverflow to add ids"
(interactive)
(org-map-entries 'org-id-get-create)
)
(add-hook 'org-mode-hook
(lambda ()
(add-hook 'before-save-hook 'stackoverflow/org-add-ids-to-headlines-in-file nil 'local)))
(add-hook 'org-capture-prepare-finalize-hook 'org-id-get-create)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; keep 'ARCHIVE' property up to date for entire buffer so any archives are filed in 'todays' archive
; assumes you want archive dates to be the date of the archive operation, not the last time they were 'touched' (or created)
(defun my/org-set-archive-prop-for-todo ()
(interactive)
(save-excursion
(goto-char (point-max))
(while (outline-previous-heading)
(org-set-property "ARCHIVE" (concat
"~/Attic/org-mode/%s/"
(format-time-string "%Y" (current-time))
"/"
(format-time-string "%m" (current-time))
".archive.org::datetree/"
))
))
)
(add-hook 'org-mode-hook
(lambda ()
(add-hook 'before-save-hook 'my/org-set-archive-prop-for-todo nil 'local)))
(add-hook 'org-capture-prepare-finalize-hook 'my/org-set-archive-prop-for-todo)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; org-mode todo tuning
; the ! tells org to log state changes for todo entries
(setq org-todo-keywords
'((sequence "TODO(t!)" "READY(r!)" "WIP(w!)" "|" "SKIPPED(k!)" "COMPLETE(c!)" "CANCELED(x!)"))
)
(setq org-todo-keyword-faces
'(("TODO" . "turquoise") ("READY" . "yellow") ("WIP" . "magenta") ("COMPLETE" . "green") ("CANCELED" . "orange"))
)
; log to drawer of entry
(setq org-log-into-drawer t)
(setq org-treat-insert-todo-heading-as-state-change t)
; set completion stats to auto-calc for checkboxes and nested todo's
(setq org-provide-todo-statistics t)
(setq org-checkbox-hierarchical-statistics nil)
(setq org-hierarchical-todo-statistics nil)
; From https://christiantietze.de/posts/2021/02/emacs-org-todo-doing-done-checkbox-cycling/
; Ensure the uppermost todo, in a nested tree, has an appropriate status based on all sub-items in the tree
; Basically: - can only be marked 'complete' if all sub-items are in a completed state
; - auto-marked as 'todo' if no sub-items are complete
; - auto-marked as 'wip' if some sub-items are complete
; - auto-marked as 'complete' if all sub-items are complete
(defun org-todo-if-needed (state)
"Change header state to STATE unless the current item is in STATE already."
(unless (string-equal (org-get-todo-state) state)
(org-todo state)))
(defun ct/org-summary-todo-cookie (n-done n-not-done)
"Switch header state to DONE when all subentries are DONE, to TODO when none are DONE, and to DOING otherwise"
(let (org-log-done org-log-states) ; turn off logging
(org-todo-if-needed (cond ((= n-done 0)
"TODO")
((= n-not-done 0)
"COMPLETE")
(t
"WIP")))))
(add-hook 'org-after-todo-statistics-hook #'ct/org-summary-todo-cookie)
(defun ct/org-summary-checkbox-cookie ()
"Switch header state to DONE when all checkboxes are ticked, to TODO when none are ticked, and to DOING otherwise"
(let (beg end)
(unless (not (org-get-todo-state))
(save-excursion
(org-back-to-heading t)
(setq beg (point))
(end-of-line)
(setq end (point))
(goto-char beg)
;; Regex group 1: %-based cookie
;; Regex group 2 and 3: x/y cookie
(if (re-search-forward "\\[\\([0-9]*%\\)\\]\\|\\[\\([0-9]*\\)/\\([0-9]*\\)\\]"
end t)
(if (match-end 1)
;; [xx%] cookie support
(cond ((equal (match-string 1) "100%")
(org-todo-if-needed "COMPLETE"))
((equal (match-string 1) "0%")
(org-todo-if-needed "TODO"))
(t
(org-todo-if-needed "WIP")))
;; [x/y] cookie support
(if (> (match-end 2) (match-beginning 2)) ; = if not empty
(cond ((equal (match-string 2) (match-string 3))
(org-todo-if-needed "COMPLETE"))
((or (equal (string-trim (match-string 2)) "")
(equal (match-string 2) "0"))
(org-todo-if-needed "TODO"))
(t
(org-todo-if-needed "WIP")))
(org-todo-if-needed "WIP"))))))))
(add-hook 'org-checkbox-statistics-hook #'ct/org-summary-checkbox-cookie)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; files
(setq org-default-notes-file "~/org/_index.org")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; agendas
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; helper for building agenda file lists
(defun my/load-org-agenda-files-recursively (dir) "Find all directories in DIR."
(unless (file-directory-p dir) (error "Not a directory `%s'" dir))
(unless (equal (directory-files dir nil org-agenda-file-regexp t) nil)
(add-to-list 'org-agenda-files dir)
)
(dolist (file (directory-files dir nil nil t))
(unless (member file '("." ".."))
(let ((file (concat dir file "/")))
(when (file-directory-p file)
(load-org-agenda-files-recursively file)
)
)
)
)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; set org habit status for yesterday and schedule for today. this is to facilitate adding "skipped" log entries for better reporting and tracking of habits
; this assumes youre operating on daily habits
; https://emacs.stackexchange.com/questions/9433/how-to-make-org-prompt-for-a-timestamp-when-changing-state-of-a-todo/63809
; https://stackoverflow.com/questions/25437069/how-can-i-mark-org-habits-as-done-in-the-past
(defun my/org-habit-with-date (&optional arg)
(interactive "P")
(setq my-activation-time (current-time))
(cl-letf* ((org-read-date-prefer-future nil)
(org-popup-calendar-for-date-prompt nil)
(my-current-time (org-read-date t t nil "when:" nil "-1d" nil))
((symbol-function 'current-time)
#'(lambda () my-current-time))
((symbol-function 'org-today)
#'(lambda () (time-to-days my-current-time)))
((symbol-function 'org-current-effective-time)
#'(lambda () my-current-time))
(super-org-entry-put (symbol-function 'org-entry-put))
((symbol-function 'org-entry-put)
#'(lambda (pom property value)
(print property)
(if (lambda (or (equal property "LAST_REPEAT") (equal property "SCHEDULED")))
(let ((my-value (format-time-string (org-time-stamp-format t t) my-current-time)))
(funcall super-org-entry-put pom property my-value))
(funcall super-org-entry-put pom property value)
))))
(if (eq major-mode 'org-agenda-mode) (org-agenda-todo arg) (org-todo arg))
)
(if (eq major-mode 'org-agenda-mode) (org-agenda-schedule arg my-activation-time) (org-schedule arg my-activation-time) )
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; org-agenda tuning (view stuff only)
;; Remove completed deadline tasks from the agenda view
(setq org-agenda-skip-deadline-if-done t)
;; Remove completed scheduled tasks from the agenda view
(setq org-agenda-skip-scheduled-if-done t)
;; Remove completed items from search results
(setq org-agenda-skip-timestamp-if-done t)
;; Dont show deadline pre-warning if already scheduled (scheduled means the time when you /start work/ on a todo ; this is very different than norms
;; see https://orgmode.org/manual/Deadlines-and-Scheduling.html
(setq org-agenda-skip-deadline-prewarning-if-scheduled t)
;; dont tend to care about tag inheritance outside of search
(setq org-agenda-use-tag-inheritance nil)
;; add new option to update habit for yesterday and reschedule it as active today
(add-hook 'org-agenda-mode-hook
(lambda ()
(local-set-key (kbd "y") 'my/org-habit-with-date)))
(setq org-agenda-use-time-grid nil)
(setq org-agenda-window-setup "current-window")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Set some org agendas/dashboards
(setq org-agenda-sorting-strategy
'(
(agenda habit-down scheduled-up deadline-up scheduled-up timestamp-up time-up priority-down category-keep)
(todo habit-up time-down scheduled-down deadline-down todo-state-down priority-down alpha-up)
(tags priority-down category-keep)
(search category-keep)
)
)
(setq org-agenda-file-regexp "\\`[^.].*\\.org\\'")
(setq org-agenda-files (list
"~/org/_index.org"
"~/org/_todo.org"
"~/org/_slipbox.org"
))
; use *all* org agenda files as refile targets
(setq org-refile-targets
'((nil :maxlevel . 3)
(org-agenda-files :maxlevel . 3)))
(require 'org-super-agenda)
(org-super-agenda-mode)
(setq org-super-agenda-groups
'(
(:name "Health"
:and (:habit t
:tag "health"))
(:name "House"
:and (:habit t
:tag "house"))
(:name "Meds"
:and (:habit t
:tag "meds"))
(:name "Health"
:and (:tag "health" :tag "homework"))
(:name "Misc"
:tag "homework")
(:name "Health"
:and (:todo ("WIP" "READY")
:tag "health"))
(:order 200 :auto-parent "t")
(:name "Slipbox"
:and (:todo ("WIP" "READY")
:tag "slipbox"))
(:name "Personal"
:and (:todo ("WIP" "READY")
:tag "personal"))
(:name "To Do"
:todo ("WIP" "READY"))
(:name "Slipbox" :tag "slipbox")
(:name "Health" :tag "health")
(:name "Personal" :tag "personal")
(:name "To Do" :priority>= "C")
(:name "Low Priority" :priority< "C")
))
(setq org-agenda-custom-commands
'(
("r" "Personal (Combined)"
(
(agenda "" (
(org-agenda-overriding-header "Scheduled")
(org-agenda-remove-tags t)
(org-deadline-warning-days 14)
(org-agenda-entry-types '(:deadline :scheduled))
))
(agenda "" (
(org-agenda-overriding-header "Habits")
(org-agenda-files (list "~/org/_habits.org"
"~/org/_meds.org"))
(org-agenda-remove-tags t)
))
(tags-todo "+homework" (
(org-agenda-overriding-header "Homework")
(org-agenda-remove-tags t)
))
(tags-todo "+TODO=\"WIP\"&-homework" (
(org-agenda-overriding-header "WIP")
(org-agenda-remove-tags t)
))
(tags-todo "+TODO=\"READY\"&-homework" (
(org-agenda-overriding-header "Ready")
(org-agenda-remove-tags t)
))
(tags-todo "-TODO=\"WIP\"&-TODO=\"READY\"&-homework" (
(org-agenda-overriding-header "")
(org-agenda-remove-tags t)
))
)
)
("h" "Personal (Habits/Scheduled)"
(
(agenda "" (
(org-agenda-overriding-header "Scheduled")
(org-agenda-remove-tags t)
(org-deadline-warning-days 14)
(org-agenda-entry-types '(:deadline :scheduled))
))
(agenda "" (
(org-agenda-overriding-header "Habits")
(org-agenda-files (list "~/org/_habits.org"
"~/org/_meds.org"))
(org-agenda-remove-tags t)
))
)
)
("t" "Personal (Tasks)"
(
(tags-todo "+homework" (
(org-agenda-overriding-header "Homework")
(org-agenda-remove-tags t)
))
(tags-todo "+TODO=\"WIP\"&-homework" (
(org-agenda-overriding-header "WIP")
(org-agenda-remove-tags t)
))
(tags-todo "+TODO=\"READY\"&-homework" (
(org-agenda-overriding-header "Ready")
(org-agenda-remove-tags t)
))
(tags-todo "-TODO=\"WIP\"&-TODO=\"READY\"&-homework" (
(org-agenda-overriding-header "")
(org-agenda-remove-tags t)
))
)
)
)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; capture templates
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; inspiration for conditional capture template : https://storax.github.io/blog/2016/05/02/org-capture-tricks/
; helper functions
(defvar my-capture-prmt-history nil
"History of prompt answers for org capture.")
(defun my/prmt (prompt variable)
"PROMPT for string, save it to VARIABLE and insert it."
(make-local-variable variable)
(set variable (read-string (concat prompt ": ") nil my-capture-prmt-history)))
(defun my/inc (what text &rest fmtvars)
"Ask user to include WHAT. If user agrees return TEXT."
(when (y-or-n-p (concat "Include " what "?"))
(apply 'format text fmtvars)))
(defun my/inc_date (what prefix)
"Ask user to include a date. If user agrees prompt for date."
(when (y-or-n-p (concat "Include " what "?"))
(concat prefix (org-time-stamp nil))))
; orgmode capture templates
(setq org-capture-templates '(
("d" "TODO (Main)" entry
(file "~/org/_todo.org")
(file "~/org/_org-capture-templates/template_todo.org")
:prepend t
:immediate-finish "f"
:jump-to-captured "t"
)
("s" "TODO (Slipbox)" entry
(file "~/org/_slipbox.org")
(file "~/org/_org-capture-templates/template_slipbox.org")
:prepend t
:immediate-finish "f"
:jump-to-captured "t"
)
("h" "Health")
("hc" "Couples Appointment noteworthy developments / [time] in review"
entry (file "~/org/_slipbox.org")
(file "~/org/_org-capture-templates/template_couples_apt_developments.org")
:immediate-finish "f"
:jump-to-captured "t"
)
("hm" "Mike Appointment noteworthy developments / [time] in review"
entry (file "~/org/_slipbox.org")
(file "~/org/_org-capture-templates/template_apt_developments.org")
:immediate-finish "f"
:jump-to-captured "t"
)
("m" "Music import (beets)" entry
(file "~/org/_slipbox.org")
(file "~/org/_org-capture-templates/template_beets.org")
:prepend t
:immediate-finish "f"
:jump-to-captured "t"
)
))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; org-habit
(add-to-list 'org-modules 'org-habit)
(setq org-habit-preceding-days 14)
(setq org-habit-following-days 7)
(setq org-agenda-span 1)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; helm specific org adjustments
(load "helm-org.el")
(require 'helm-org)
(helm-mode 1)
(add-to-list 'helm-completing-read-handlers-alist '(org-capture . helm-org-completing-read-tags))
(add-to-list 'helm-completing-read-handlers-alist '(org-set-tags . helm-org-completing-read-tags))
(add-to-list 'helm-completing-read-handlers-alist '(org-set-tags-command . helm-org-completing-read-tags))
; fix helm multi-tag select (needs custom seprator. default is comma
;(setq helm-crm-default-separator ":"
(define-advice helm--completion-in-region (:around (helm-fun origfun start end collection &optional predicate) temporary-helm-crm-separator-for-tags)
(setq tcrmds helm-crm-default-separator)
;; If the last command was any of these values, we're looking at tags most likely
(when (or (member last-command '(org-capture org-ctrl-c-ctrl-c org-set-tags org-set-tags-command))
;;This is a workaround for completions when you've already started typing.
(and (eq this-command 'crm-complete)
(eq major-mode 'org-mode))
;; This is probably the only thing we really need, but it doesn't handle custom "Tags" prompts
(and (active-minibuffer-window)
(eq "Tags: " (minibuffer-prompt))))
(setq helm-crm-default-separator ":"))
;; Call the original Helm Completion function with all the original arguments
(funcall helm-fun origfun start end collection predicate)
(setq helm-crm-default-separator tcrmds))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; obsidian link handler from https://forum.obsidian.md/t/emacs-on-macos-org-mode-link-handler-for-obsidian-links/19230
;; obsidan link handling for obsidian:// links
(defun org-obsidian-link-open (slash-message-id)
"Handler for org-link-set-parameters that opens a obsidian:// link in obsidian"
;; remove any / at the start of slash-message-id to create real note-id
(let ((message-id
(replace-regexp-in-string (rx bos (* "/"))
""
slash-message-id)))
(async-shell-command (concat "/c/Users/mcros/AppData/Local/Programs/Obsidian/Obsidian.exe \"obsidian://" message-id "\""))))
;; on obsdian://aoeu link, this will call handler with //aoeu
(org-link-set-parameters "obsidian" :follow #'org-obsidian-link-open)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ui tuning
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; misc desktop specific overrides
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; set more reasonable org habit line width
(setq org-habit-graph-column 75)
; set ui to standard org-workspace at launch
(add-hook 'after-init-hook (lambda ()
(my/workspace-org)
))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; pomidor
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(use-package pomidor
:bind (("C-w P" . pomidor))
:config (setq pomidor-update-interval 30
pomidor-save-session-file "~/org/pomidor-sessions.json"
pomidor-alert nil
pomidor-sound-tick nil
pomidor-sound-tack nil
pomidor-sound-overwork nil
pomidor-sound-break-over nil)
:hook (pomidor-mode . (lambda ()
(display-line-numbers-mode -1) ; Emacs 26.1+
(setq left-fringe-width 0 right-fringe-width 0)
(setq left-margin-width 2 right-margin-width 0)
;; force fringe update
(set-window-buffer nil (current-buffer)))))
; setup 6 pomodoros for 8 hr work day
; 7 hours of work, with 1 hr of long breaks
; each pomodor is:
; warm up of 15 mins
; work of 45 mins
; total work period: 60 mins
; 10 min break
; total interval period: 70 mins
; long break is 30 mins
; every 3rd pomodoro give 30 minute break
; actual values
(setq pomidor-seconds (* 60 60)) ; total work period
(setq pomidor-warmup-seconds (* 15 60)) ; warmup time
(setq pomidor-break-seconds (* 10 60))
(setq pomidor-breaks-before-long 3)
(setq pomidor-long-break-seconds (* 30 60))
; setup emoji separators to enhance visual state indicators
(defun my-pomidor-separator-hook ()
(let* ((state (pomidor--current-state))
(total (pomidor--total-duration state))
(elapsed (round (time-to-seconds total))))
(cond ; watch out for the order of this conditional
; there are overlapping states and this order is meaninful
((or (pomidor-overwork-p) (pomidor-break-over-p))
(setq pomidor-header-separator " ⚠️ "))
((pomidor-should-long-break-p)
(setq pomidor-header-separator " 🪁 "))
((pomidor--break state)
(setq pomidor-header-separator " 🚶 "))
((<= elapsed pomidor-warmup-seconds)
(setq pomidor-header-separator " 🌡️↑ "))
(t (setq pomidor-header-separator " 🏢 "))
)
)
)
; add separator logic as std pomidor update hook to ensure the emoji separators get displayed
; this will be delayed up to the pomidor-update-interval for display
(add-hook 'pomidor-update-hook #'my-pomidor-separator-hook)
; trigger the emoji separator for common operations so it 'triggers' faster than via the std pomidor update hook
; this should show the emoji separators 'faster' than relying on just the update hook
(advice-add #'pomidor-reset :after #'my-pomidor-separator-hook)
(advice-add #'pomidor-stop :after #'my-pomidor-separator-hook)
(advice-add #'pomidor-break :after #'my-pomidor-separator-hook)
; 'hold' visual indicator
(defun my-pomidor-hold-separator ()
(interactive)
(setq pomidor-header-separator " 💤 ")
(pomidor--update)
)
(advice-add #'pomidor-hold :before #'my-pomidor-hold-separator)
; 'unhold' -- reset visual indicator
(defun my-pomidor-unhold-separator ()
(interactive)
(my-pomidor-separator-hook)
(pomidor--update)
)
(advice-add #'pomidor-unhold :before #'my-pomidor-unhold-separator)
; position cursor @ top of pomidor buffer after rendering
(defun my-pomidor-beginning-of-buffer (buffer states)
(interactive)
(with-current-buffer (get-buffer pomidor-buffer-name)
(goto-char (point-min))
)
)
(advice-add #'pomidor--render :after #'my-pomidor-beginning-of-buffer)
; safely run pomidor in a buffer programatically
; doesnt mung or reset state
(defun my-pomidor-run-safe ()
; make sure pomidor is running + active w/o resetting the current state
(when (or (not (boundp 'pomidor-buffer-name))
(not (get-buffer pomidor-buffer-name)))
(pomidor)
(previous-buffer) ; pomidor switches buffers -- go back to orig buffer
)
)
; frame for pomidor, just the main timer text visible
; centered at bottom of the screen
(defun my-pomidor-frame ()
(interactive)
(select-frame (make-frame '(
(name . "Pomidor")
(menu-bar-lines 0)
(tool-bar-lines 0)
(width . (text-pixels . 818))
(height . (text-pixels . 144))
)))
; single monitor / main monitor positing
(let* ((dw (display-pixel-width))
(dh (display-pixel-height))
(f (selected-frame))
(fw (frame-pixel-width f))
(fh (frame-pixel-height f))
; handle multi-monitor goofy (main panel is /left/ of first panel)
; assumes a 1440p multi-monitor setup with monitor 1 'to the right of' monitor 2
; remove the (- [stuff] 2506) to undo this workaround
(x (- (- (/ dw 2) (/ fw 2)) 2560))
(y (- (- dh fh ) 125)))
(set-frame-position f x y)
)
; ensure pomidor is running & do /not/ reset state
(my-pomidor-run-safe)
; switch to pomidor buffer
(switch-to-buffer pomidor-buffer-name)
)
(global-set-key (kbd "C-M-p") 'my-pomidor-frame)
; pop win pomidor setup
; main way to invoke
; ensure pomidor is running before trying to open the buffer
; use global buffer name for pomidor
; make the popwin window 'sticky'
; position sticky popwin @ top
(push '(pomidor-mode :position top :stick t) popwin:special-display-config)
(global-set-key (kbd "C-w p")
(lambda () (interactive)
(my-pomidor-run-safe)
; open pomidor in popwin
(popwin:display-buffer (get-buffer pomidor-buffer-name))
)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; origami + super agenda config
(defvar ap/org-super-agenda-auto-hide-groups
'(".*Low Priority.*" "^.*?[^R]+.*?(desc)$"))
(defun ap/org-super-agenda-origami-fold-default ()
"Fold certain groups by default in Org Super Agenda buffer."
(--each ap/org-super-agenda-auto-hide-groups
(goto-char (point-min))
(cl-loop
while (re-search-forward it nil t)
do
(if (not (or
(string-match "TODO" (thing-at-point 'line))
(string-match "READY" (thing-at-point 'line))
(string-match "WIP" (thing-at-point 'line))
))
(origami-close-node (current-buffer) (point))
)
)
)
)
(add-hook 'org-agenda-mode-hook #'origami-mode)
(add-hook 'org-agenda-finalize-hook #'ap/org-super-agenda-origami-fold-default)
(define-key org-super-agenda-header-map (kbd "<tab>") #'origami-toggle-node)
(define-key org-agenda-mode-map (kbd "<tab>") #'origami-toggle-node)
(custom-set-faces
;; custom-set-faces was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
'(default ((t (:family "MonoLisa Variable" :height 100))))
'(fixed-pitch ((t (:family "MonoLisa Variable" :height 120))))
'(variable-pitch ((t (:family "Atkinson Hyperlegible" :height 120)))))
(custom-set-variables
;; custom-set-variables was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
'(custom-safe-themes
'("dde643b0efb339c0de5645a2bc2e8b4176976d5298065b8e6ca45bc4ddf188b7" "bfc0b9c3de0382e452a878a1fb4726e1302bf9da20e69d6ec1cd1d5d82f61e3d" default))
'(package-selected-packages
'(pomidor
(org-tidy org-auto-expand ox-pandoc org-super-agenda org-alert burnt-toast alert)
(xclip)
(which-key all-the-icons revert-buffer-all scratch persistent-scratch rainbow-mode rainbow-delimiters focus zoom popwin dired-single diredfl doominhibitinhibit-modeline helpful helm helm-org dired-rainbow dired-rainbow-listing dired-single dash s origami modus-themes use-package)))
'(safe-local-variable-values
'((org-duration-format . h:mm)
(org-duration-format quote h:mm)
(eval setq org-html-postamble nil)
(eval setq org-export-html-postamble-format nil)
(eval add-hook 'after-save-hook 'org-pandoc-export-to-latex-pdf t t)
(eval add-hook 'after-save-hook 'org-pandoc-export-to-docx t t)
(eval add-hook 'after-save-hook 'org-ascii-export-to-ascii t t)
(eval add-hook 'after-save-hook 'org-md-export-to-markdown t t)
(eval add-hook 'after-save-hook 'org-org-export-to-org t t)
(eval add-hook 'after-save-hook 'org-html-export-to-html t t)
(eval add-hook 'after-save-hook 'org-gfm-export-to-markdown t t)))
'(temp-buffer-resize-mode t)
'(zoom-ignored-major-modes '(dired-mode))
'(zoom-size 'my-zoom-size-callback))