1628 lines
62 KiB
EmacsLisp
1628 lines
62 KiB
EmacsLisp
;; -*- 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. I’m 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))
|