summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKjetil Orbekk <kj@orbekk.com>2024-01-07 13:04:45 -0500
committerKjetil Orbekk <kj@orbekk.com>2024-01-07 13:04:45 -0500
commit7a07cdd61d626b42f2b05ef8fb843a29b57ed580 (patch)
treefe02140e0d66312e35ce0f1236fdf50eafb490f1
parentd2f973eb627c64f25e76d08418956e9a26b9853a (diff)
Testing new configuration
-rw-r--r--emacs/.config/chemacs/profiles.el4
-rw-r--r--emacs/.config/emacs-kj/init.el13
-rw-r--r--emacs/.config/emacs-kj/lisp/kj-history.el2
-rw-r--r--emacs/.config/emacs-v2/early-init.el32
-rw-r--r--emacs/.config/emacs-v2/elpaca-installer.el38
-rw-r--r--emacs/.config/emacs-v2/emacs-custom.el16
-rw-r--r--emacs/.config/emacs-v2/init.el40
-rw-r--r--emacs/.config/emacs-v2/lisp/kj-bindings.el34
-rw-r--r--emacs/.config/emacs-v2/lisp/kj-defaults.el50
-rw-r--r--emacs/.config/emacs-v2/lisp/kj-development.el91
-rw-r--r--emacs/.config/emacs-v2/lisp/kj-email.el84
-rw-r--r--emacs/.config/emacs-v2/lisp/kj-eshell.el83
-rw-r--r--emacs/.config/emacs-v2/lisp/kj-history.el66
-rw-r--r--emacs/.config/emacs-v2/lisp/kj-init.el861
-rw-r--r--emacs/.config/emacs-v2/lisp/kj-lib.el11
-rw-r--r--emacs/.config/emacs-v2/lisp/kj-multiple-cursors.el20
-rw-r--r--emacs/.config/emacs-v2/lisp/kj-org.el251
-rw-r--r--emacs/.config/emacs-v2/lisp/kj-popup.el95
18 files changed, 1781 insertions, 10 deletions
diff --git a/emacs/.config/chemacs/profiles.el b/emacs/.config/chemacs/profiles.el
index d988842..473c2b5 100644
--- a/emacs/.config/chemacs/profiles.el
+++ b/emacs/.config/chemacs/profiles.el
@@ -1,5 +1,5 @@
-(("default" . ((user-emacs-directory . "~/.config/emacs-kj")))
+(("default" . ((user-emacs-directory . "~/.config/emacs-v2")))
("doom" . ((user-emacs-directory . "~/build/doom-emacs")
(env . (("DOOMDIR" . "~/.config/doom")))))
("kj" . ((user-emacs-directory . "~/.config/emacs-kj")))
- )
+ ("v2" . ((user-emacs-directory . "~/.config/emacs-v2"))))
diff --git a/emacs/.config/emacs-kj/init.el b/emacs/.config/emacs-kj/init.el
index c4c3d48..83a9353 100644
--- a/emacs/.config/emacs-kj/init.el
+++ b/emacs/.config/emacs-kj/init.el
@@ -114,11 +114,6 @@
(process-buffer proc))
(kill-buffer (process-buffer proc)))))))))
-(defun kj/shell-history ()
- (interactive)
-
- )
-
(use-package coterm
:config
(coterm-mode 1))
@@ -359,6 +354,9 @@
;; Both < and C-+ work reasonably well.
(setq consult-narrow-key "<") ;; (kbd "C-+")
+ (setq consult-ripgrep-args
+ "rg --null --line-buffered --color=never --max-columns=1000 --path-separator / --smart-case --no-heading --with-filename --line-number --search-zip --hidden")
+
;; Optionally make narrowing help available in the minibuffer.
;; You may want to use `embark-prefix-help-command' or which-key instead.
;; (define-key consult-narrow-map (vconcat consult-narrow-key "?") #'consult-narrow-help)
@@ -433,7 +431,7 @@
:bind (:map projectile-mode-map
("C-x p" . projectile-command-map)))
-(use-package ediff
+(use-package emacs
:init
(setq ediff-diff-options "-w" ; turn off whitespace checking
ediff-split-window-function #'split-window-horizontally
@@ -882,8 +880,7 @@
(add-to-list 'display-buffer-alist
`(kj/help-modes ;predicate
- (display-buffer--maybe-same-window
- display-buffer-reuse-window
+ (display-buffer-reuse-window
display-buffer-reuse-mode-window) ;functions to try
(mode . ,kj/help-modes)
(inhibit-same-window . nil)))
diff --git a/emacs/.config/emacs-kj/lisp/kj-history.el b/emacs/.config/emacs-kj/lisp/kj-history.el
index debd23c..3b0bb68 100644
--- a/emacs/.config/emacs-kj/lisp/kj-history.el
+++ b/emacs/.config/emacs-kj/lisp/kj-history.el
@@ -1,3 +1,5 @@
+;; -*- lexical-binding: t; -*-
+
(defvar kj/history-command "fc -R $HISTFILE && fc -l 1"
"History command used to get shell history.")
diff --git a/emacs/.config/emacs-v2/early-init.el b/emacs/.config/emacs-v2/early-init.el
new file mode 100644
index 0000000..8184bbb
--- /dev/null
+++ b/emacs/.config/emacs-v2/early-init.el
@@ -0,0 +1,32 @@
+;;; -*- lexical-binding: t; -*-
+
+(setq package-enable-at-startup nil)
+
+;; Optimization: file name handlers not needed during startup.
+(defvar kj/file-name-handler-alist--old file-name-handler-alist)
+(setq file-name-handler-alist nil)
+(defun kj/restore-file-name-handler-alist ()
+ (if (not (boundp 'kj/file-name-handler-alist--old))
+ (message "File name handler already restored")
+ (setq file-name-handler-alist kj/file-name-handler-alist--old)
+ (makunbound 'kj/file-name-handler-alist--old)))
+
+;; Optimization: avoid garbage collection during startup.
+;; Reset by gcmh later.
+(setq gc-cons-threshold most-positive-fixnum)
+
+;; Move native compilation cache out of the way. Should be set in
+;; early-init so that package loding uses the updated value.
+(startup-redirect-eln-cache "~/.cache/emacs/eln-cache")
+
+;; Early options that affect startup.
+(setq
+ package-native-compile t
+ inhibit-startup-message t
+ native-comp-async-report-warnings-errors 'silent
+ default-frame-alist
+ '((tool-bar-lines . 0)
+ (menu-bar-lines . 0)
+ (undecorated . t)
+ (vertical-scroll-bars . nil)
+ (horizontal-scroll-bars . nil)))
diff --git a/emacs/.config/emacs-v2/elpaca-installer.el b/emacs/.config/emacs-v2/elpaca-installer.el
new file mode 100644
index 0000000..5a10886
--- /dev/null
+++ b/emacs/.config/emacs-v2/elpaca-installer.el
@@ -0,0 +1,38 @@
+;;; -*- lexical-binding: t; -*-
+
+(defvar elpaca-installer-version 0.6)
+(defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory))
+(defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory))
+(defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory))
+(defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git"
+ :ref nil
+ :files (:defaults "elpaca-test.el" (:exclude "extensions"))
+ :build (:not elpaca--activate-package)))
+(let* ((repo (expand-file-name "elpaca/" elpaca-repos-directory))
+ (build (expand-file-name "elpaca/" elpaca-builds-directory))
+ (order (cdr elpaca-order))
+ (default-directory repo))
+ (add-to-list 'load-path (if (file-exists-p build) build repo))
+ (unless (file-exists-p repo)
+ (make-directory repo t)
+ (when (< emacs-major-version 28) (require 'subr-x))
+ (condition-case-unless-debug err
+ (if-let ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*"))
+ ((zerop (call-process "git" nil buffer t "clone"
+ (plist-get order :repo) repo)))
+ ((zerop (call-process "git" nil buffer t "checkout"
+ (or (plist-get order :ref) "--"))))
+ (emacs (concat invocation-directory invocation-name))
+ ((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch"
+ "--eval" "(byte-recompile-directory \".\" 0 'force)")))
+ ((require 'elpaca))
+ ((elpaca-generate-autoloads "elpaca" repo)))
+ (progn (message "%s" (buffer-string)) (kill-buffer buffer))
+ (error "%s" (with-current-buffer buffer (buffer-string))))
+ ((error) (warn "%s" err) (delete-directory repo 'recursive))))
+ (unless (require 'elpaca-autoloads nil t)
+ (require 'elpaca)
+ (elpaca-generate-autoloads "elpaca" repo)
+ (load "./elpaca-autoloads")))
+(add-hook 'after-init-hook #'elpaca-process-queues)
+(elpaca `(,@elpaca-order))
diff --git a/emacs/.config/emacs-v2/emacs-custom.el b/emacs/.config/emacs-v2/emacs-custom.el
new file mode 100644
index 0000000..89bc0fc
--- /dev/null
+++ b/emacs/.config/emacs-v2/emacs-custom.el
@@ -0,0 +1,16 @@
+(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
+ '("88267200889975d801f6c667128301af0bc183f3450c4b86138bfb23e8a78fb1"
+ default))
+ '(safe-local-variable-values '((checkdoc-package-keywords-flag))))
+(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 "Iosevka" :height 150))))
+ '(variable-pitch ((t (:family "Noto Serif" :height 150)))))
diff --git a/emacs/.config/emacs-v2/init.el b/emacs/.config/emacs-v2/init.el
new file mode 100644
index 0000000..ddc40f1
--- /dev/null
+++ b/emacs/.config/emacs-v2/init.el
@@ -0,0 +1,40 @@
+;;; -*- lexical-binding: t; -*-
+
+;; NixOS emacs doesn't include a build time. Assume a recent version.
+(setq elpaca-core-date 20240101)
+(setq elpaca-directory "~/.cache/emacs/elpaca")
+(load-file (expand-file-name "elpaca-installer.el" user-emacs-directory))
+
+(add-hook 'emacs-startup-hook
+ (lambda ()
+ (message "Emacs ready in %s with %d garbage collections."
+ (format "%.2f seconds"
+ (float-time
+ (time-subtract after-init-time before-init-time)))
+ gcs-done)))
+
+(add-hook 'elpaca-after-init-hook 'kj/restore-file-name-handler-alist)
+
+(elpaca elpaca-use-package
+ ;; Enable :elpaca use-package keyword.
+ (elpaca-use-package-mode)
+ ;; Assume :elpaca t unless otherwise specified.
+ (setq elpaca-use-package-by-default t))
+(elpaca-wait)
+
+(use-package gcmh
+ :hook
+ (elpaca-after-init . kj/enable-gcmh)
+ :config
+ (defun kj/enable-gcmh ()
+ (message "Enabling gcmh")
+ (gcmh-mode 1)))
+
+;; Include user configuration.
+(defvar kj/module-directory (expand-file-name "lisp" user-emacs-directory))
+(push kj/module-directory load-path)
+(require 'kj-lib)
+(require 'kj-init)
+(cl-loop for path in (directory-files kj/module-directory)
+ if (string-match "\\([^/]*\\).el" path)
+ do (require (intern (match-string-no-properties 1 path))))
diff --git a/emacs/.config/emacs-v2/lisp/kj-bindings.el b/emacs/.config/emacs-v2/lisp/kj-bindings.el
new file mode 100644
index 0000000..0c0ba47
--- /dev/null
+++ b/emacs/.config/emacs-v2/lisp/kj-bindings.el
@@ -0,0 +1,34 @@
+;;; -*- lexical-binding: t; -*-
+
+(use-package emacs :elpaca nil
+ :bind
+ (("C-w" . kj/kill-word-or-region)
+ ("C-c s" . kj/shell-here)
+ ("C-c t" . eshell)
+ ("C-c ." . kj/eshell-here)
+ ("C-c c" . org-capture)
+ ("M-o" . other-window)
+ ("C-c a m" . notmuch)
+ ("C-c M" . smerge-ediff)
+ ("C-c b r" . revert-buffer)
+ ("C-c r l" . consult-flymake)
+ ("C-c r f" . eglot-format)
+ ("C-c r g" . recompile)
+ ("C-c r a" . eglot-code-actions)
+ ("C-c r r" . eglot-rename)
+ ("C-c r n" . flymake-goto-next-error)
+ ("C-c r p" . flymake-goto-prev-error)
+ ("C-c w u" . winner-undo)
+ ("C-c w U" . winner-redo)
+ ("C-<tab>" . completion-at-point)
+ ;; Replace downcase-word binding.
+ ("M-l" . iso-transl-ctl-x-8-map))
+
+ :config
+ (defvar kj/flymake-repeat-map (make-sparse-keymap "flymake"))
+ (define-repeating-key kj/flymake-repeat-map "n" flymake-goto-next-error "next error")
+ (define-repeating-key kj/flymake-repeat-map "p" flymake-goto-prev-error "prev error")
+ (define-key kj/flymake-repeat-map (kbd "C-g") '("quit" . ignore))
+ )
+
+(provide 'kj-bindings)
diff --git a/emacs/.config/emacs-v2/lisp/kj-defaults.el b/emacs/.config/emacs-v2/lisp/kj-defaults.el
new file mode 100644
index 0000000..c2cf6fd
--- /dev/null
+++ b/emacs/.config/emacs-v2/lisp/kj-defaults.el
@@ -0,0 +1,50 @@
+;;; -*- lexical-binding: t; -*-
+
+;; Backups
+(make-directory "~/.cache/emacs_backups/" t)
+(make-directory "~/.cache/emacs_autosave/" t)
+(setq backup-directory-alist '(("." . "~/.cache/emacs_backups/")))
+(setq auto-save-file-name-transforms '((".*" "~/.cache/emacs_autosave/" t)))
+
+;; Keep backups, but avoid same directory since it can be slow on tramp.
+(setq version-control t
+ delete-old-versions -1
+ vc-make-backup-files t
+ create-lockfiles nil
+ backup-by-copying t)
+
+(setq-default indent-tabs-mode nil)
+(setq sentence-end-double-space nil)
+(setq save-interprogram-paste-before-kill t)
+(setq mouse-yank-at-point t)
+(setq enable-recursive-minibuffers t)
+(set-language-environment "utf-8")
+(setq require-final-newline t)
+;; Save bookmark list every time a bookmark is added.
+(setq bookmark-save-flag 1)
+(delete-selection-mode 1)
+
+(setq savehist-file "~/.cache/emacs/savehist-history")
+(savehist-mode 1)
+
+;; Resize settings
+(setq frame-inhibit-implied-resize t)
+(setq pixel-scroll-precision-mode t)
+
+(setq kill-whole-line t)
+
+(defun kj/kill-word-or-region (arg)
+ (interactive "p")
+ (if (region-active-p)
+ (kill-region (region-beginning) (region-end))
+ (backward-kill-word (or arg 1))))
+(global-set-key (kbd "C-w") 'kj/kill-word-or-region)
+
+(defun kj/open-line (n)
+ (interactive "*p")
+ (beginning-of-line)
+ (open-line n)
+ (indent-according-to-mode))
+(global-set-key (kbd "C-o") 'kj/open-line)
+
+(provide 'kj-defaults)
diff --git a/emacs/.config/emacs-v2/lisp/kj-development.el b/emacs/.config/emacs-v2/lisp/kj-development.el
new file mode 100644
index 0000000..c7f1168
--- /dev/null
+++ b/emacs/.config/emacs-v2/lisp/kj-development.el
@@ -0,0 +1,91 @@
+;;; -*- lexical-binding: t; -*-
+(setq tab-stop-list (number-sequence 4 200 4))
+
+(use-package eglot :elpaca nil
+ :config
+ (setq eglot-events-buffer-size 0))
+
+(add-hook 'prog-mode-hook 'subword-mode)
+
+;; Only used to get better eglot snippets.
+(use-package yasnippet
+ :config
+ (add-to-list 'yas-snippet-dirs (expand-file-name "yasnippets" user-emacs-directory))
+ (yas-global-mode))
+
+(use-package rustic
+ :after eglot
+ :bind (("C-c C-r C-s" . rustic-popup)
+ ("C-c C-r C-n" . kj/rustic-nextest-all))
+ :config
+ (defun kj/rustic-nextest-all ()
+ (interactive)
+ (setq rustic-cargo-nextest-exec-command "nextest run --run-ignored all")
+ (rustic-cargo-run-nextest))
+ (setq
+ ;; eglot seems to be the best option right now.
+ rustic-lsp-client 'eglot
+ rustic-format-on-save nil
+ ;; Prevent automatic syntax checking, which was causing lags and stutters.
+ ;; eglot-send-changes-idle-time (* 60 60)
+ )
+ :hook
+ (rustic-mode . eglot-ensure)
+ ;; Disable the annoying doc popups in the minibuffer.
+ ;; (add-hook 'eglot-managed-mode-hook (lambda () (eldoc-mode -1)))
+ )
+
+(use-package markdown-mode
+ :ensure t
+ :mode ("README\\.md\\'" . gfm-mode)
+ :init (setq markdown-command "multimarkdown"))
+
+(use-package nix-mode
+ :mode "\\.nix\\'")
+
+(use-package ledger-mode
+ :mode "\\.journal")
+(use-package unobtrusive-magit-theme)
+
+(use-package editorconfig
+ :ensure t
+ :config
+ (editorconfig-mode 1))
+
+(use-package tree-sitter
+ :config
+ (global-tree-sitter-mode)
+ (add-hook 'tree-sitter-after-on-hook #'tree-sitter-hl-mode))
+
+(use-package tree-sitter-langs
+ :after tree-sitter)
+
+(use-package clojure-mode)
+
+;; Devicetree mode for keyboard configs.
+(use-package dts-mode
+ :mode "\\.keymap\\'")
+
+(use-package yaml-mode
+ :mode "\\.\(yml|yaml\)\\'")
+
+;; (use-package ansi-color
+;; :hook (compilation-filter . ansi-color-compilation-filter))
+
+(use-package diff-hl
+ :config
+ (add-hook 'magit-pre-refresh-hook 'diff-hl-magit-pre-refresh)
+ (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh)
+ (global-diff-hl-mode))
+
+(use-package graphviz-dot-mode
+ :ensure t)
+
+(use-package haskell-mode)
+(use-package tidal
+ :config
+ (setq tidal-boot-script-path "~/projects/audio/BootTidal.hs"))
+
+(use-package ess)
+
+(provide 'kj-development)
diff --git a/emacs/.config/emacs-v2/lisp/kj-email.el b/emacs/.config/emacs-v2/lisp/kj-email.el
new file mode 100644
index 0000000..4aa6439
--- /dev/null
+++ b/emacs/.config/emacs-v2/lisp/kj-email.el
@@ -0,0 +1,84 @@
+;;; -*- lexical-binding: t; -*-
+
+(setq kj/fastmail-maildir (expand-file-name "~/Maildir/fastmail"))
+
+(when (file-exists-p (expand-file-name "~/Maildir"))
+ (with-eval-after-load 'message
+ (setq message-mail-user-agent 'notmuch-user-agent
+ message-cite-style 'message-cite-style-gmail
+ message-citation-line-function 'message-insert-formatted-citation-line
+ message-kill-buffer-on-exit t)
+ (add-hook 'message-mode-hook 'turn-off-auto-fill)
+ (add-hook 'message-mode-hook 'visual-line-mode))
+ (use-package notmuch
+ :bind
+ (:map notmuch-hello-mode-map
+ ("T" . kj/notmuch-tree-by-tag))
+ :config
+
+ (setq notmuch-show-all-tags-list t
+ notmuch-show-relative-dates t
+ notmuch-hello-tag-list-make-query "tag:unread"
+ notmuch-archive-tags '("-inbox" "-unread")
+ notmuch-fcc-dirs nil
+ notmuch-show-logo nil
+ notmuch-mua-cite-function 'message-cite-original-without-signature
+ notmuch-wash-citation-lines-prefix most-positive-fixnum
+ notmuch-wash-citation-lines-suffix most-positive-fixnum
+ notmuch-show-indent-messages-width 0)
+ (add-hook 'notmuch-show-hook 'kj/disable-trailing-whitespace)
+
+ (defun kj/confirm-empty-subject ()
+ "Allow user to quit when current message subject is empty."
+ (or (message-field-value "Subject")
+ (yes-or-no-p "Really send without Subject? ")
+ (keyboard-quit)))
+ (add-hook 'message-send-hook #'kj/confirm-empty-subject)
+ (advice-add
+ 'message-send :before (defun kj/notmuch-switch-to-local (&args)
+ (when (file-remote-p default-directory)
+ (cd (expand-file-name "~")))))
+ (setq notmuch-saved-searches
+ '((:name "inbox" :query "tag:inbox -tag:_gz" :key "i" :search-type tree)
+ (:name "unread" :query "tag:unread" :key "u" :search-type tree)
+ (:name "flagged" :query "tag:flagged" :key "f" :search-type tree)
+ (:name "sent" :query "tag:sent" :key "t" :search-type tree)
+ (:name "drafts" :query "tag:draft" :key "d" :search-type tree)
+ (:name "all mail" :query "*" :key "a" :search-type tree)
+ )))
+
+ (setq kj/notmuch-wash-citation-regexp "^\\(\\(>\\|[[:space:]]\\)+\\).*\n")
+
+ ;;; XXX consider upstreaming this.
+ (defun kj/notmuch-wash-citations-by-depth (_msg _depth)
+ (goto-char (point-min))
+ (beginning-of-line)
+ (let ((maxlevel 1))
+ (while (intern-soft (format "message-cited-text-%d" maxlevel))
+ (setq maxlevel (1+ maxlevel)))
+ (setq maxlevel (1- maxlevel))
+
+ (while (and (< (point) (point-max))
+ (re-search-forward kj/notmuch-wash-citation-regexp nil t))
+ (let* ((cite-start (match-beginning 0))
+ (cite-end (match-end 0))
+ (cite-level (seq-count (lambda (c) (eql c ?>)) (match-string 1)))
+ (face-level (1+ (% (1- cite-level) maxlevel)))
+ (cited-text-face (intern (format "message-cited-text-%d" face-level))))
+ (overlay-put (make-overlay cite-start cite-end)
+ 'face cited-text-face)))))
+ ;; This doesn't seem to work that well... :(
+ ;;(add-hook 'notmuch-show-insert-text/plain-hook #'kj/notmuch-wash-citations-by-depth 90)
+
+ (defun kj/notmuch-tree-by-tag (tag)
+ (interactive
+ (list (notmuch-select-tag-with-completion "Notmuch search tag: ")))
+ (notmuch-tree (concat "tag:" tag))))
+
+(when (file-exists-p kj/fastmail-maildir)
+ (setq sendmail-program "mujmap"
+ message-send-mail-function 'message-send-mail-with-sendmail
+ message-signature "Kjetil"
+ message-sendmail-extra-arguments (list "-C" kj/fastmail-maildir "send")))
+
+(provide 'kj-email)
diff --git a/emacs/.config/emacs-v2/lisp/kj-eshell.el b/emacs/.config/emacs-v2/lisp/kj-eshell.el
new file mode 100644
index 0000000..5617658
--- /dev/null
+++ b/emacs/.config/emacs-v2/lisp/kj-eshell.el
@@ -0,0 +1,83 @@
+;;; -*- lexical-binding: t; -*-
+(use-package eshell :elpaca nil
+ :bind
+ (:map eshell-mode-map
+ ("M-r" . consult-history))
+ :hook ((eshell-first-time-mode . kj/eshell-on-load))
+ :config
+ (add-hook 'eshell-mode-hook
+ (lambda ()
+ (setenv "PAGER" "cat"))
+ (setenv "EDITOR" "emacsclient"))
+ (setq eshell-prefer-lisp-functions nil
+ eshell-scroll-to-bottom-on-input 'all
+ eshell-hist-ignoredups t
+ eshell-destroy-buffer-when-process-dies t
+ eshell-error-if-no-glob t)
+ (setq tramp-remote-path '(tramp-own-remote-path))
+
+ (defun kj/eshell-on-load ()
+ (dolist (command '("htop" "tmux" "vim" "nvim" "watch"))
+ (add-to-list 'eshell-visual-commands
+ command))
+
+ ;; (define-key eshell-prompt-mode-map (kbd "C-d") 'eshell-life-is-too-much)
+ )
+
+ (add-to-list 'eshell-complex-commands "ssh")
+
+ (add-to-list 'eshell-modules-list 'eshell-tramp)
+ (add-hook 'eshell-mode-hook 'visual-line-mode))
+
+(use-package pcmpl-args)
+
+(use-package esh-help
+ :init
+ (setup-esh-help-eldoc))
+
+(use-package eshell-syntax-highlighting
+ :hook (eshell-mode . eshell-syntax-highlighting-mode))
+
+(defun eshell/ssh (&rest args)
+ (if (= 1 (length args))
+ (throw 'eshell-replace-command
+ (eshell/cd
+ (format "/ssh:%s:" (car args))))
+ (throw 'eshell-replace-command
+ (eshell-parse-command "*ssh"
+ (eshell-stringify-list (flatten-tree args))))))
+
+(defvar kj/eshell-here-directory)
+
+(defun kj/eshell-buffer-name ()
+ (let ((base (concat "*eshell:" default-directory)))
+ (cond
+ ((not (get-buffer base)) base)
+ (t (cl-loop for n from 2
+ for name = (format "%s<%d>" base n)
+ until (not (get-buffer name))
+ finally return name)))))
+
+(defun kj/eshell-update-buffer-name ()
+ (interactive)
+ (rename-buffer (kj/eshell-buffer-name)))
+(add-hook 'eshell-directory-change-hook 'kj/eshell-update-buffer-name)
+
+(defun kj/new-eshell-here ()
+ (let ((default-directory kj/eshell-here-directory))
+ (eshell t)))
+
+(defun kj/eshell-here ()
+ (interactive)
+ (unless (eq last-command 'kj/eshell-here)
+ (setq kj/eshell-here-directory
+ (if (derived-mode-p 'eshell-mode)
+ (with-current-buffer (other-buffer (current-buffer) t)
+ default-directory)
+ default-directory)))
+ (if (derived-mode-p 'eshell-mode)
+ (kj/new-eshell-here)
+ (eshell))
+ (kj/eshell-update-buffer-name))
+
+(provide 'kj-eshell)
diff --git a/emacs/.config/emacs-v2/lisp/kj-history.el b/emacs/.config/emacs-v2/lisp/kj-history.el
new file mode 100644
index 0000000..5c9c0fe
--- /dev/null
+++ b/emacs/.config/emacs-v2/lisp/kj-history.el
@@ -0,0 +1,66 @@
+;; -*- lexical-binding: t; -*-
+
+(defvar kj/history-command "fc -R $HISTFILE && fc -l 1"
+ "History command used to get shell history.")
+
+(defvar kj/history-regexp
+ (rx bol (* " ")
+ ;; Index
+ (* digit)
+ (* " ")
+ ;; Command
+ (group (1+ (not "\n")))))
+
+(use-package emacs :elpaca nil
+ :after consult
+ :config
+ (defun kj/get-shell-history ()
+ (with-temp-buffer
+ (unless (process-file
+ "zsh"
+ nil
+ (current-buffer)
+ nil
+ "-c"
+ kj/history-command)
+ (error "Getting history failed: %s" (buffer-string)))
+ (goto-char (point-min))
+ (let ((result))
+ (save-match-data
+ (while (re-search-forward kj/history-regexp nil t)
+ (let ((command
+ ;; Fix up multi-line commands.
+ (string-replace "\\\\n" "\\\n" (match-string-no-properties 1))))
+ (push command result))))
+ result)))
+
+ (defun kj/consult-history ()
+ (interactive)
+ (if (not
+ (or (and (minibufferp)
+ (eq minibuffer-history-variable 'shell-command-history))
+ (derived-mode-p '(term-mode shell-mode))
+ ))
+ (call-interactively 'consult-history)
+ (pcase-let* ((bol
+ (cond ((derived-mode-p 'eshell-mode) 'eshell-bol)
+ ((derived-mode-p 'term-mode) 'term-bol)
+ ((derived-mode-p 'comint-mode) 'comint-bol)))
+ (`(,beg . ,end)
+ (cond ((minibufferp)
+ (cons (minibuffer-prompt-end) (point-max)))
+ (bol (save-excursion
+ (funcall bol)
+ (cons (point) (pos-eol))))
+ (t (cons (point) (point)))))
+ (str (consult--read
+ (kj/get-shell-history)
+ :prompt "Shell history: "
+ :history t
+ :sort nil
+ :initial (buffer-substring-no-properties beg end)
+ :state (consult--insertion-preview beg end))))
+ (delete-region beg end)
+ (insert (substring-no-properties str))))))
+
+(provide 'kj-history)
diff --git a/emacs/.config/emacs-v2/lisp/kj-init.el b/emacs/.config/emacs-v2/lisp/kj-init.el
new file mode 100644
index 0000000..049669b
--- /dev/null
+++ b/emacs/.config/emacs-v2/lisp/kj-init.el
@@ -0,0 +1,861 @@
+;;; -*- lexical-binding: t; -*-
+
+(setq user-full-name "KJ Orbekk"
+ user-mail-address "kj@orbekk.com")
+
+(defvar kj/cache-dir "~/.cache/emacs")
+
+(setq initial-scratch-message nil)
+
+(setq custom-file (expand-file-name "emacs-custom.el" user-emacs-directory))
+(load custom-file :noerror t)
+
+(setq use-short-answers t)
+
+(global-hl-line-mode 1)
+(global-auto-revert-mode 1)
+
+(setq visible-bell t
+ ring-bell-function 'ignore)
+
+(setq auto-save-list-file-prefix (expand-file-name "auto-save-list/.saves-"))
+
+(use-package modus-themes
+ :init
+ (load-theme 'modus-vivendi-tinted)
+ :config
+ (custom-theme-set-faces
+ 'modus-vivendi-tinted
+ '(avy-lead-face ((t :inherit (bold modus-themes-subtle-blue))))
+ '(avy-lead-face-0 ((t :inherit (bold modus-themes-subtle-cyan))))
+ '(avy-lead-face-1 ((t :inherit (bold modus-themes-subtle-green))))
+ '(avy-lead-face-2 ((t :inherit (bold modus-themes-subtle-magenta))))))
+
+(use-package doom-themes)
+
+(setq alert-default-style 'libnotify)
+
+(use-package all-the-icons
+ :if (display-graphic-p))
+(setq inhibit-compacting-font-caches t)
+(setq use-dialog-box nil)
+(setq display-time-24hr-format t)
+
+(display-battery-mode 1)
+(display-time-mode 1)
+
+(use-package doom-modeline
+ :ensure t
+ :init (doom-modeline-mode 1)
+ :config
+ (setq doom-modeline-buffer-encoding 'nondefault))
+
+(use-package emojify
+ :bind
+ (("C-c C-i C-e" . emojify-insert-emoji))
+ :hook (after-init . global-emojify-mode)
+ :config
+ (emojify-set-emoji-styles '(unicode)))
+
+(setq-default show-trailing-whitespace t)
+(dolist (mode '(calendar-mode-hook
+ eshell-mode-hook
+ term-mode-hook
+ comint-mode-hook
+ completion-list-mode))
+ (add-hook mode
+ (defun kj/disable-trailing-whitespace ()
+ (setq show-trailing-whitespace nil))))
+
+;; Deletes trailing whitespace on changed lines only.
+(use-package ws-butler
+ :diminish ws-butler-mode
+ :hook (prog-mode . ws-butler-mode))
+
+;; `M-x shell` settings
+(setq shell-prompt-pattern "^[^#$%>\n]*[#$%>❯] *"
+ explicit-shell-file-name "zsh"
+ explicit-zsh-args '("--login" "--interactive"))
+(defun kj/shell-buffer-name ()
+ (concat "*shell:" default-directory "*"))
+
+(defun kj/shell-here ()
+ (interactive)
+ (let ((buffer
+ (shell (kj/shell-buffer-name))))
+ (with-current-buffer buffer
+ (let* ((proc (get-buffer-process (current-buffer)))
+ (sentinel (process-sentinel proc)))
+ (set-process-sentinel
+ proc
+ `(lambda (proc signal)
+ (funcall ',sentinel proc signal)
+ (and (memq (process-status proc) '(exit signal))
+ (buffer-live-p (process-buffer proc))
+ (message "Shell died, killing buffer %s"
+ (process-buffer proc))
+ (kill-buffer (process-buffer proc)))))))))
+
+(use-package coterm
+ :config
+ (coterm-mode 1))
+
+;; Update buffer name to reflect directory.
+(advice-add 'cd :after
+ (defun kj/maybe-rename-shell (&rest args)
+ (and (derived-mode-p 'shell-mode)
+ (rename-buffer
+ (kj/shell-buffer-name)))))
+
+(use-package rainbow-delimiters
+ :defer t
+ :hook (prog-mode . rainbow-delimiters-mode)
+ )
+
+(column-number-mode)
+(set-fringe-mode 10)
+(setq next-screen-context-lines 10)
+
+(use-package avy
+ :bind
+ (("C-c l" . avy-goto-char)
+ ("C-c n" . avy-goto-line))
+ :config
+ (setq avy-keys '(?a ?o ?e ?u ?i ?d ?h ?t ?n ?s))
+ (defun avy-action-embark (pt)
+ (unwind-protect
+ (save-excursion
+ (goto-char pt)
+ (embark-act))
+ (select-window
+ (cdr (ring-ref avy-ring 0))))
+ t)
+
+(setf (alist-get ?. avy-dispatch-alist) 'avy-action-embark))
+(define-key isearch-mode-map (kbd "M-j") 'avy-isearch)
+
+(winner-mode +1)
+
+(use-package ace-window
+ :bind
+ (("C-c e" . ace-window))
+ :config
+ (setq aw-dispatch-always t)
+ (setq aw-keys '(?a ?o ?e ?u ?i ?d ?h ?t ?n ?s))
+ ;; Make ace window more visible
+ ;; (custom-set-faces!
+ ;; '(aw-leading-char-face
+ ;; :foreground "white" :background "red"
+ ;; :weight bold :height 2.5 :box (:line-width 10 :color "red")))
+ )
+
+(defun kj/balance-main-window ()
+ (balance-windows (window-main-window)))
+
+;; Prefer horizontal splits.
+(setq split-height-threshold 100)
+
+(defun kj/split-window-below ()
+ (interactive)
+ (split-window-below)
+ (kj/balance-main-window)
+ (other-window 1))
+
+(defun kj/split-window-right ()
+ (interactive)
+ (split-window-right)
+ (kj/balance-main-window)
+ (other-window 1))
+
+(global-set-key (kbd "C-x 3") #'kj/split-window-right)
+(global-set-key (kbd "C-x 2") #'kj/split-window-below)
+
+(use-package vertico
+ :init
+ (vertico-mode))
+
+;; Rebind C-x?
+(global-set-key (kbd "C-t") 'ctrl-x-map)
+(define-key global-map (kbd "C-t") ctl-x-map)
+;; (define-key key-translation-map "\C-t" "\C-t")
+;; (define-key key-translation-map "\C-x" "\C-t")
+
+(use-package marginalia
+ :bind
+ (:map minibuffer-local-map
+ ("M-n" . marginalia-cycle))
+ :init
+ (marginalia-mode))
+
+(use-package embark
+ :ensure t
+
+ :bind
+ (("C-." . embark-act)
+ ("C-;" . embark-dwim)
+ ("C-h C-b" . embark-bindings))
+
+ :init
+ (setq prefix-help-command #'embark-prefix-help-command)
+
+ :config
+ (defun find-file-keeping-default-directory (filename)
+ (let ((dir default-directory))
+ (with-current-buffer (find-file filename)
+ (setq-local default-directory dir))))
+ (define-key embark-file-map "@" 'find-file-keeping-default-directory)
+
+ ;; Hide the mode line of the Embark live/completions buffers
+ (add-to-list 'display-buffer-alist
+ '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
+ .
+ (window-parameters (mode-line-format . none)))))
+
+(use-package embark-consult
+ :ensure t
+ :after (embark consult)
+ :demand t
+ :hook (embark-mode . consult-preview-at-point-mode))
+
+;; For editing grep buffers.
+(use-package wgrep)
+(use-package deadgrep
+ :bind ("M-s g" . deadgrep))
+
+;; For recent files.
+(setq recentf-max-saved-items 1000)
+(setq recentf-auto-cleanup 'never)
+(defun kj/recentf-keep (file)
+ (cond
+ ((file-remote-p file) t)
+ ((file-readable-p file))))
+(setq recentf-keep '(kj/recentf-keep))
+(setq recentf-save-file (expand-file-name "emacs-recentf" kj/cache-dir))
+(recentf-mode 1)
+(run-at-time nil (* 5 60) 'recentf-save-list)
+
+(setq global-mark-ring-max 500
+ mark-ring-max 16)
+
+;; Example configuration for Consult
+(use-package consult
+ ;; Replace bindings. Lazily loaded due by `use-package'.
+ :bind (;; C-c bindings (mode-specific-map)
+ ("C-c h" . kj/consult-history)
+ ;;("C-c m" . consult-mode-command)
+ ("C-c k" . consult-kmacro)
+ ;; C-x bindings (ctl-x-map)
+ ("C-x M-:" . consult-complex-command) ;; orig. repeat-complex-command
+ ("C-x b" . consult-buffer) ;; orig. switch-to-buffer
+ ("C-x C-b" . consult-buffer) ;; orig. switch-to-buffer
+ ("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window
+ ("C-x 5 b" . consult-buffer-other-frame) ;; orig. switch-to-buffer-other-frame
+ ("C-x r b" . consult-bookmark) ;; orig. bookmark-jump
+ ("C-x p b" . consult-project-buffer) ;; orig. project-switch-to-buffer
+ ("C-c f r" . consult-recent-file)
+ ;; Custom M-# bindings for fast register access
+ ("M-#" . consult-register-load)
+ ("M-'" . consult-register-store) ;; orig. abbrev-prefix-mark (unrelated)
+ ("C-M-#" . consult-register)
+ ;; Other custom bindings
+ ("M-y" . consult-yank-pop) ;; orig. yank-pop
+ ("<help> a" . consult-apropos) ;; orig. apropos-command
+ ;; M-g bindings (goto-map)
+ ("M-g e" . consult-compile-error)
+ ("M-g f" . consult-flymake) ;; Alternative: consult-flycheck
+ ("M-g g" . consult-goto-line) ;; orig. goto-line
+ ("M-g M-g" . consult-goto-line) ;; orig. goto-line
+ ("M-g o" . consult-outline) ;; Alternative: consult-org-heading
+ ("M-g m" . consult-mark)
+ ("M-g k" . consult-global-mark)
+ ("M-g i" . consult-imenu)
+ ("M-g I" . consult-imenu-multi)
+ ;; M-s bindings (search-map)
+ ("M-s d" . consult-find)
+ ("M-s D" . consult-locate)
+ ;; ("M-s g" . consult-grep)
+ ("M-s G" . consult-git-grep)
+ ("M-s r" . consult-ripgrep)
+ ("M-s l" . consult-line)
+ ("M-s L" . consult-line-multi)
+ ("M-s m" . consult-multi-occur)
+ ("M-s k" . consult-keep-lines)
+ ("M-s u" . consult-focus-lines)
+ ;; Isearch integration
+ ("M-s e" . consult-isearch-history)
+ :map isearch-mode-map
+ ("M-e" . consult-isearch-history) ;; orig. isearch-edit-string
+ ("M-s e" . consult-isearch-history) ;; orig. isearch-edit-string
+ ("M-s l" . consult-line) ;; needed by consult-line to detect isearch
+ ("M-s L" . consult-line-multi) ;; needed by consult-line to detect isearch
+ ;; Minibuffer history
+ :map minibuffer-local-map
+ ("M-s" . kj/consult-history) ;; orig. next-matching-history-element
+ ("M-r" . kj/consult-history)) ;; orig. previous-matching-history-element
+
+ ;; Enable automatic preview at point in the *Completions* buffer. This is
+ ;; relevant when you use the default completion UI.
+ ;; :hook (completion-list-mode . consult-preview-at-point-mode)
+
+ ;; The :init configuration is always executed (Not lazy)
+ :init
+
+ ;; Optionally configure the register formatting. This improves the register
+ ;; preview for `consult-register', `consult-register-load',
+ ;; `consult-register-store' and the Emacs built-ins.
+ (setq register-preview-delay 0.5
+ register-preview-function #'consult-register-format)
+
+ ;; Optionally tweak the register preview window.
+ ;; This adds thin lines, sorting and hides the mode line of the window.
+ (advice-add #'register-preview :override #'consult-register-window)
+
+ ;; Use Consult to select xref locations with preview
+ (setq xref-show-xrefs-function #'consult-xref
+ xref-show-definitions-function #'consult-xref)
+
+ ;; Configure other variables and modes in the :config section,
+ ;; after lazily loading the package.
+ :config
+
+ ;; Use consult for completion.
+ (setq completion-in-region-function #'consult-completion-in-region)
+
+ ;; Optionally configure preview. The default value
+ ;; is 'any, such that any key triggers the preview.
+ (setq consult-preview-key '(:debounce 0.3 any))
+ ;; (setq consult-preview-key (kbd "M-."))
+ ;; (setq consult-preview-key (list (kbd "<S-down>") (kbd "<S-up>")))
+ ;; For some commands and buffer sources it is useful to configure the
+ ;; :preview-key on a per-command basis using the `consult-customize' macro.
+ ;; (consult-customize
+ ;; consult-theme
+ ;; :preview-key '(:debounce 0.2 any)
+ ;; consult-ripgrep consult-git-grep consult-grep
+ ;; consult-bookmark consult-recent-file consult-xref
+ ;; consult--source-bookmark consult--source-recent-file
+ ;; consult--source-project-recent-file
+ ;; :preview-key (kbd "M-."))
+
+ ;; Optionally configure the narrowing key.
+ ;; Both < and C-+ work reasonably well.
+ (setq consult-narrow-key "<") ;; (kbd "C-+")
+
+ (setq consult-ripgrep-args
+ "rg --null --line-buffered --color=never --max-columns=1000 --path-separator / --smart-case --no-heading --with-filename --line-number --search-zip --hidden")
+
+ ;; Optionally make narrowing help available in the minibuffer.
+ ;; You may want to use `embark-prefix-help-command' or which-key instead.
+ ;; (define-key consult-narrow-map (vconcat consult-narrow-key "?") #'consult-narrow-help)
+
+ ;; By default `consult-project-function' uses `project-root' from project.el.
+ ;; Optionally configure a different project root function.
+ ;; There are multiple reasonable alternatives to chose from.
+ ;;;; 1. project.el (the default)
+ ;;(setq consult-project-function #'consult--default-project-function)
+ ;;;; 2. projectile.el (projectile-project-root)
+ (autoload 'projectile-project-root "projectile")
+ (setq consult-project-function (lambda (_) (projectile-project-root)))
+ ;;;; 3. vc.el (vc-root-dir)
+ ;; (setq consult-project-function (lambda (_) (vc-root-dir)))
+ ;;;; 4. locate-dominating-file
+ ;; (setq consult-project-function (lambda (_) (locate-dominating-file "." ".git")))
+ )
+
+(use-package orderless
+ :init
+ (setq completion-styles '(orderless basic)
+ completion-category-defaults nil
+ completion-category-overrides '((file (styles partial-completion)))))
+
+(use-package which-key
+ :init
+ (which-key-mode))
+
+(use-package transient
+ :config
+ (setq transient-levels-file (expand-file-name "transient/levels.el" kj/cache-dir)
+ transient-values-file (expand-file-name "transient/values.el" kj/cache-dir)
+ transient-history-file (expand-file-name "transient/history.el" kj/cache-dir)))
+(use-package magit)
+
+(use-package mixed-pitch
+ :hook
+ (text-mode . mixed-pitch-mode)
+ (org-mode . mixed-pitch-mode)
+ )
+
+(when (string= (system-name) "fedora")
+ (setenv "SSH_AUTH_SOCK" (s-trim (shell-command-to-string "gpgconf --list-dirs agent-ssh-socket"))))
+
+(setq kj/font-height
+ (cond ((string= (system-name) "fedora") 100)
+ (t 150)))
+
+(custom-set-faces
+ `(variable-pitch ((t (:family "Noto Serif" :height ,kj/font-height))))
+ `(default ((t (:family "Iosevka" :height ,kj/font-height)))))
+
+;; (unless (equal system-name "minideck")
+;; (set-face-attribute 'fixed-pitch nil :font "Iosevka" :height 150)
+;; (set-face-attribute 'variable-pitch nil :font "Noto Serif"))
+
+;;; Compilation settings
+(setq compilation-ask-about-save nil)
+(setq compilation-scroll-output 'first-error)
+;; Allow longer output in compilation buffer.
+(add-to-list 'compilation-filter-hook #'comint-truncate-buffer)
+(setq comint-buffer-maximum-size 10000)
+
+(use-package ob-async)
+
+(use-package direnv
+ :config
+ (setq direnv-always-show-summary nil)
+ (direnv-mode))
+
+(use-package projectile
+ :ensure t
+ :defer 1
+ :init
+ (setq projectile-known-projects-file (expand-file-name "projectile-bookmarks.eld" kj/cache-dir))
+ (projectile-mode +1)
+ :bind (:map projectile-mode-map
+ ("C-x p" . projectile-command-map)))
+
+(use-package emacs :elpaca nil
+ :config
+ (setq ediff-diff-options "-w" ; turn off whitespace checking
+ ediff-split-window-function #'split-window-horizontally
+ ediff-window-setup-function #'ediff-setup-windows-plain))
+
+(use-package anzu
+ :config
+ (global-anzu-mode +1)
+ (global-set-key [remap query-replace] 'anzu-query-replace)
+ (global-set-key [remap query-replace-regexp] 'anzu-query-replace-regexp))
+
+(defun meow-setup ()
+ (setq meow-replace-state-name-list
+ '((normal . "N")
+ (motion . "M")
+ (keypad . "🖩")
+ (insert . "I")
+ (beacon . "📻")))
+ (setq meow-cheatsheet-layout meow-cheatsheet-layout-dvorak)
+ (meow-leader-define-key
+ '("1" . meow-digit-argument)
+ '("2" . meow-digit-argument)
+ '("3" . meow-digit-argument)
+ '("4" . meow-digit-argument)
+ '("5" . meow-digit-argument)
+ '("6" . meow-digit-argument)
+ '("7" . meow-digit-argument)
+ '("8" . meow-digit-argument)
+ '("9" . meow-digit-argument)
+ '("0" . meow-digit-argument)
+ '("/" . meow-keypad-describe-key)
+ '("?" . meow-cheatsheet)
+ '("b" . consult-buffer)
+ ;;'("p" . projectile-command-map)
+ )
+ (meow-motion-overwrite-define-key
+ ;; custom keybinding for motion state
+ '("<escape>" . ignore))
+ (meow-normal-define-key
+ '("0" . meow-expand-0)
+ '("9" . meow-expand-9)
+ '("8" . meow-expand-8)
+ '("7" . meow-expand-7)
+ '("6" . meow-expand-6)
+ '("5" . meow-expand-5)
+ '("4" . meow-expand-4)
+ '("3" . meow-expand-3)
+ '("2" . meow-expand-2)
+ '("1" . meow-expand-1)
+ '("-" . negative-argument)
+ '(";" . meow-reverse)
+ '("," . meow-inner-of-thing)
+ '("." . meow-bounds-of-thing)
+ '("@" . er/expand-region)
+ '("<" . meow-beginning-of-thing)
+ '(">" . meow-end-of-thing)
+ '("a" . meow-append)
+ '("A" . meow-open-below)
+ '("b" . meow-back-word)
+ '("B" . meow-back-symbol)
+ '("c" . meow-change)
+ '("d" . meow-delete)
+ '("D" . meow-backward-delete)
+ '("e" . meow-line)
+ '("E" . meow-goto-line)
+ '("f" . meow-find)
+ '("g" . meow-cancel-selection)
+ '("G" . meow-grab)
+ '("h" . meow-left)
+ '("H" . meow-left-expand)
+ '("i" . meow-insert)
+ '("I" . meow-open-above)
+ '("j" . meow-join)
+ '("k" . meow-kill)
+ '("l" . meow-till)
+ '("m" . meow-mark-word)
+ '("M" . meow-mark-symbol)
+ '("n" . meow-next)
+ '("N" . meow-next-expand)
+ '("o" . meow-block)
+ '("O" . meow-to-block)
+ '("p" . meow-prev)
+ '("P" . meow-prev-expand)
+ '("q" . meow-quit)
+ '("Q" . meow-goto-line)
+ '("r" . meow-replace)
+ '("R" . meow-swap-grab)
+ '("s" . meow-search)
+ '("/" . isearch-forward)
+ '("?" . isearch-backward)
+ '("t" . meow-right)
+ '("T" . meow-right-expand)
+ '("u" . meow-undo)
+ '("U" . meow-undo-in-selection)
+ '("v" . meow-visit)
+ '("w" . meow-next-word)
+ '("W" . meow-next-symbol)
+ '("x" . meow-save)
+ '("X" . meow-sync-grab)
+ '("y" . meow-yank)
+ '("z" . meow-pop-selection)
+ '("P" . (lambda () (interactive) (forward-line -10)))
+ '("N" . (lambda () (interactive) (forward-line 10)))
+ '("'" . repeat)
+ '("<escape>" . ignore))
+
+ (add-hook 'git-commit-mode-hook #'meow-insert))
+
+;; (use-package meow
+;; :bind
+;; :config
+;; (meow-setup)
+;; (meow-global-mode 1)
+;; ;; (meow-setup-indicator) ; Not needed with doom-modeline.
+;; (setq meow-use-clipboard t)
+;; ;; (add-to-list 'meow-keypad-start-keys '(?t . ?x))
+;; ;; (add-to-list 'meow-keypad-start-keys '(?x . ?t))
+;; (define-key meow-insert-state-keymap "\C-[" #'meow-insert-exit)
+;; )
+
+(use-package emacs :elpaca nil
+ :hook (dired-mode . dired-omit-mode)
+ :init
+ (setq dired-dwim-target t ; suggest a target for moving/copying intelligently
+ dired-hide-details-hide-symlink-targets nil
+ ;; don't prompt to revert, just do it
+ dired-auto-revert-buffer #'dired-buffer-stale-p
+ ;; Always copy/delete recursively
+ dired-recursive-copies 'always
+ dired-recursive-deletes 'top
+ ;; Ask whether destination dirs should get created when copying/removing files.
+ dired-create-destination-dirs 'ask))
+
+;; (use-package dirvish
+;; :init
+;; (setq dirvish-hide-details nil)
+;; (dirvish-override-dired-mode))
+
+(use-package diredfl
+ :hook (dired-mode . diredfl-mode))
+
+;; Tramp settings
+(setq tramp-use-ssh-controlmaster-options nil
+ tramp-default-method "ssh")
+;; Need 'eval-after-load' of 'tramp-sh' here because nixos has a
+;; startup file that messes with this variable.
+(with-eval-after-load 'tramp-sh
+ (add-to-list 'tramp-remote-path 'tramp-own-remote-path))
+
+(setq math-additional-units
+ '((ZiB "1024 * EiB" "Zebibyte")
+ (EiB "1024 * PiB" "Exbibyte")
+ (PiB "1024 * TiB" "Pebibyte")
+ (TiB "1024 * GiB" "Tebibyte")
+ (GiB "1024 * MiB" "Gibibyte")
+ (MiB "1024 * KiB" "Mebibyte")
+ (KiB "1024 * B" "Kibibyte")
+ (B nil "Byte")
+ (Zib "1024 * ZiB" "Zebibit")
+ (Eib "1024 * PiB" "Exbibit")
+ (Pib "1024 * TiB" "Pebibit")
+ (Tib "1024 * GiB" "Tebibit")
+ (Gib "1024 * Mib" "Gibibit")
+ (Mib "1024 * Kib" "Mebibit")
+ (Kib "1024 * b" "Kibibit")
+ (b "B / 8" "Bit")))
+
+
+;; Low menu delay.
+(setq which-key-idle-delay .5)
+
+;; Replace values in an alist from a list of replacements.
+;;
+;; Example:
+;; (kj/assq-replace '((:a . 1)) '((:a . 2)))
+(defun kj/assq-replace (replacements alist)
+ (let ((replace1 (lambda (aelem alist)
+ (cons aelem (assq-delete-all (car aelem) alist)))))
+ (if replacements
+ (kj/assq-replace (cdr replacements)
+ (funcall replace1 (car replacements) alist))
+ alist)))
+
+
+(use-package idle-highlight-mode
+ :config
+ (setq idle-highlight-idle-time 0.2)
+ (set-face-attribute 'idle-highlight nil :inherit 'underline)
+ :hook ((prog-mode text-mode) . idle-highlight-mode))
+
+(defun kj/smartparens-config ()
+ (require 'smartparens-config)
+ (bind-keys :map smartparens-mode-map
+ ("C-M-a" . sp-beginning-of-sexp)
+ ("C-M-e" . sp-end-of-sexp)
+
+ ("C-<down>" . sp-down-sexp)
+ ("C-<up>" . sp-up-sexp)
+ ("M-<down>" . sp-backward-down-sexp)
+ ("M-<up>" . sp-backward-up-sexp)
+
+ ("C-M-f" . sp-forward-sexp)
+ ("C-M-b" . sp-backward-sexp)
+
+ ("C-M-n" . sp-next-sexp)
+ ("C-M-p" . sp-previous-sexp)
+
+ ("C-S-f" . sp-forward-symbol)
+ ("C-S-b" . sp-backward-symbol)
+
+ ("C-<right>" . sp-forward-slurp-sexp)
+ ("M-<right>" . sp-forward-barf-sexp)
+ ("C-<left>" . sp-backward-slurp-sexp)
+ ("M-<left>" . sp-backward-barf-sexp)
+
+ ("C-M-t" . sp-transpose-sexp)
+ ("C-M-k" . sp-kill-sexp)
+ ("C-k" . sp-kill-hybrid-sexp)
+ ("C-M-w" . sp-copy-sexp)
+ ("C-M-d" . delete-sexp)
+
+ ("M-<backspace>" . backward-kill-word)
+ ("C-<backspace>" . sp-backward-kill-word)
+ ([remap sp-backward-kill-word] . backward-kill-word)
+
+ ("M-[" . sp-backward-unwrap-sexp)
+ ("M-]" . sp-unwrap-sexp)
+
+ ("C-x C-t" . sp-transpose-hybrid-sexp)
+
+ ("C-c (" . wrap-with-parens)
+ ("C-c [" . wrap-with-brackets)
+ ("C-c {" . wrap-with-braces)
+ ("C-c '" . wrap-with-single-quotes)
+ ("C-c \"" . wrap-with-double-quotes)
+ ("C-c _" . wrap-with-underscores)
+ ("C-c `" . wrap-with-back-quotes)
+
+ ("M-k" . sp-raise-sexp)
+ ("M-S" . sp-split-sexp)
+ ("M-I" . sp-splice-sexp))
+ )
+
+(use-package smartparens
+ :init
+ (kj/smartparens-config)
+ :hook ((prog-mode lisp-mode) . smartparens-strict-mode)
+ )
+
+;; (use-package paredit
+;; :hook ((lisp-mode emacs-lisp-mode) . paredit-mode)
+;; :bind (:map paredit-mode-map
+;; ("M-k" . paredit-raise-sexp)
+;; ("M-I" . paredit-splice-sexp))
+;; :hook (paredit-mode
+;; . (lambda ()
+;; (unbind-key "M-r" paredit-mode-map)
+;; (unbind-key "M-s" paredit-mode-map))))
+
+(define-key isearch-mode-map (kbd "C-RET")
+ #'isearch-exit-other-end)
+;; Why does C-RET not always work?
+(define-key isearch-mode-map (kbd "C-<return>")
+ #'isearch-exit-other-end)
+(defun isearch-exit-other-end ()
+ "Exit isearch, at the opposite end of the string."
+ (interactive)
+ (isearch-exit)
+ (goto-char isearch-other-end))
+(define-key isearch-mode-map (kbd "M-z")
+ #'kj/isearch-zap)
+(defun kj/isearch-zap ()
+ "Zap to beginning of search"
+ (interactive)
+ (isearch-exit)
+ (goto-char isearch-other-end)
+ (kill-region (region-beginning) (region-end)))
+
+(setq case-fold-search t)
+(setq case-replace t)
+
+(use-package expand-region
+ :bind (("C-@" . #'er/expand-region)))
+
+(use-package ace-mc
+ :bind (:map kj/leader-map
+ ("h" . ace-mc-add-multiple-cursors)
+ ("M-h" . ace-mc-add-single-cursor)))
+
+;; Disable VC over tramp.
+;;(setq-default vc-handled-backends nil)
+(defun kj/vc-off-if-remote ()
+ (if (file-remote-p (buffer-file-name))
+ (setq-local vc-handled-backends nil)))
+(add-hook 'find-file-hook 'kj/vc-off-if-remote)
+
+(use-package selected
+ :demand t
+ :diminish selected-minor-mode
+ :bind (:map selected-keymap
+ ("[" . align-code)
+ ("f" . fill-region)
+ ("U" . unfill-region)
+ ("d" . downcase-region)
+ ("r" . reverse-region)
+ ("S" . sort-lines))
+ :config
+ (selected-global-mode 1))
+
+(setq kj/some-lines 6)
+(defun kj/forward-some-lines ()
+ (interactive)
+ (forward-line kj/some-lines))
+(defun kj/previous-some-lines ()
+ (interactive)
+ (previous-line kj/some-lines))
+
+;; Repeat commands (e.g., C-x o o o)
+(repeat-mode 1)
+
+;; Disable the built-in repeat-mode hinting
+;;(setq repeat-echo-function #'ignore)
+
+;; Spawn or hide a which-key popup
+;; (advice-add 'repeat-post-hook :after
+;; (defun repeat-help--which-key-popup ()
+;; (if-let ((cmd (or this-command real-this-command))
+;; (keymap (or repeat-map
+;; (repeat--command-property 'repeat-map))))
+;; (run-at-time
+;; 0 nil
+;; (lambda ()
+;; (which-key--create-buffer-and-show
+;; nil (symbol-value keymap))))
+;; (which-key--hide-popup))))
+
+;; Calendar settings.
+(setq calendar-week-start-day 1)
+;; Show calendar week numbers.
+(copy-face font-lock-constant-face 'calendar-iso-week-face)
+(set-face-attribute 'calendar-iso-week-face nil
+ :height 0.7)
+(setq calendar-intermonth-text
+ '(propertize
+ (format "%2d"
+ (car
+ (calendar-iso-from-absolute
+ (calendar-absolute-from-gregorian (list month day year)))))
+ 'font-lock-face 'calendar-iso-week-face))
+
+;; (setq ispell-local-dictionary "en_US")
+;; (add-hook 'text-mode-hook flyspell-mode)
+;; (add-hook 'prog-mode-hook flyspell-prog-mode)
+
+;;; Email settings
+(setq message-kill-buffer-on-exit t) ;; After sending a message.
+
+(let ((google-config-file
+ (expand-file-name "google.el" user-emacs-directory)))
+ (when (file-exists-p google-config-file)
+ (load-file google-config-file)))
+
+;; Special symbols
+(use-package emacs :elpaca nil
+ :defer 1
+ :config
+ (defvar kj/iso-transl-char-map
+ '(
+ ;; fractions that emacs doesn't include
+ ("1/3" . [?⅓])
+ ("1/5" . [?⅕])
+ ("1/6" . [?⅙])
+ ("1/7" . [?⅐])
+ ("1/8" . [?⅛])
+ ("1/9" . [?⅑])
+ ("1/10" . [?⅒])
+ ("2/3" . [?⅔])
+ ("2/5" . [?⅖])
+ ("3/5" . [?⅗])
+ ("3/8" . [?⅜])
+ ("4/5" . [?⅘])
+ ("5/6" . [?⅚])
+ ("5/8" . [?⅝])
+ ("7/8" . [?⅞])
+ ;; add the emojis I commonly use
+ (":c" . [?✅])
+ (":u" . [?👍])
+ (":d" . [?👎])
+ (":w" . [?🖐])
+ ;; card suits
+ ("c" . [?♣])
+ ("h" . [?♥])
+ ("d" . [?♦])
+ ("s" . [?♠])))
+ (require 'iso-transl)
+ (iso-transl-define-keys kj/iso-transl-char-map)
+ :init
+ (require 'iso-transl))
+
+(use-package pcre2el)
+(use-package pdf-tools)
+
+(defvar kj/help-modes '(helpful-mode
+ help-mode
+ shortdoc-mode
+ Man-mode
+ woman-mode
+ Info-mode))
+
+(defun kj/buffer-help-p (buf act)
+ "BUF is a help buffer, ignore ACT."
+ (member (buffer-local-value 'major-mode (get-buffer buf)) kj/help-modes))
+
+(add-to-list 'display-buffer-alist
+ `(kj/help-modes ;predicate
+ (display-buffer--maybe-same-window
+ display-buffer-reuse-window
+ display-buffer-reuse-mode-window) ;functions to try
+ (mode . ,kj/help-modes)
+ (inhibit-same-window . nil)))
+
+(use-package helpful
+ :bind
+ (([remap describe-key] . 'helpful-key)
+ ([remap describe-variable] . 'helpful-variable)
+ ([remap describe-symbol] . 'helpful-symbol)
+ ([remap describe-command] . 'helpful-command)
+ ([remap describe-function] . 'helpful-callable)))
+
+;; Enabled commands
+(put 'narrow-to-region 'disabled nil)
+(put 'downcase-region 'disabled nil)
+(put 'upcase-region 'disabled nil)
+
+(provide 'kj-init)
diff --git a/emacs/.config/emacs-v2/lisp/kj-lib.el b/emacs/.config/emacs-v2/lisp/kj-lib.el
new file mode 100644
index 0000000..5df1822
--- /dev/null
+++ b/emacs/.config/emacs-v2/lisp/kj-lib.el
@@ -0,0 +1,11 @@
+;;; -*- lexical-binding: t; -*-
+
+(defmacro define-repeating-key (keymap key cmd &optional desc)
+ `(let ((def
+ ,(if desc
+ `'(,desc . ,cmd)
+ `',cmd)))
+ (define-key ,keymap ,key def)
+ (put ',cmd 'repeat-map ',keymap)))
+
+(provide 'kj-lib)
diff --git a/emacs/.config/emacs-v2/lisp/kj-multiple-cursors.el b/emacs/.config/emacs-v2/lisp/kj-multiple-cursors.el
new file mode 100644
index 0000000..962193d
--- /dev/null
+++ b/emacs/.config/emacs-v2/lisp/kj-multiple-cursors.el
@@ -0,0 +1,20 @@
+;;; -*- lexical-binding: t; -*-
+
+(use-package multiple-cursors
+ :config
+ (defvar kj/multiple-cursors-map (make-sparse-keymap "cursors"))
+ (define-repeating-key kj/multiple-cursors-map "." mc/mark-next-like-this "→ this")
+ (define-repeating-key kj/multiple-cursors-map "," mc/mark-previous-like-this "← this")
+ (define-repeating-key kj/multiple-cursors-map ">" mc/skip-to-next-like-this "⇥ this")
+ (define-repeating-key kj/multiple-cursors-map "<" mc/skip-to-previous-like-this "⇤ this")
+ (define-repeating-key kj/multiple-cursors-map "<" mc/skip-to-previous-like-this "⇤ this")
+ (define-repeating-key kj/multiple-cursors-map "*" mc/mark-all-like-this "all like this")
+ (define-repeating-key kj/multiple-cursors-map "w" mc/mark-next-like-this-word "word")
+ (define-repeating-key kj/multiple-cursors-map "s" mc/mark-next-like-this-symbol "symbol")
+ (define-repeating-key kj/multiple-cursors-map "(" mc/mark-all-symbols-like-this-in-defun "symbol defun")
+ (define-repeating-key kj/multiple-cursors-map (kbd "DEL") mc/unmark-next-like-this "DEL last")
+ (define-repeating-key kj/multiple-cursors-map (kbd "<delete>") mc/unmark-previous-like-this "DEL first")
+ (define-key kj/multiple-cursors-map (kbd "C-g") '("quit" . ignore))
+ (global-set-key (kbd "C-c m") (cons "cursors" kj/multiple-cursors-map)))
+
+(provide 'kj-multiple-cursors)
diff --git a/emacs/.config/emacs-v2/lisp/kj-org.el b/emacs/.config/emacs-v2/lisp/kj-org.el
new file mode 100644
index 0000000..b6fda9c
--- /dev/null
+++ b/emacs/.config/emacs-v2/lisp/kj-org.el
@@ -0,0 +1,251 @@
+;;; -*- lexical-binding: t; -*-
+
+(use-package org
+ :config
+ (org-babel-do-load-languages 'org-babel-load-languages
+ '((emacs-lisp . t)
+ (sql . t)
+ (shell . t)))
+ (setq org-confirm-babel-evaluate nil
+ org-return-follows-link t
+ )
+
+ ;; Install the link type
+ (org-add-link-type "notmuch" 'org-notmuch-open)
+ (add-hook 'org-store-link-functions 'org-notmuch-store-link)
+
+ (defun org-notmuch-store-link ()
+ "Store a link to a notmuch search or message."
+ (when (eq major-mode 'notmuch-show-mode)
+ (let* ((message-id (notmuch-show-get-prop :id))
+ (subject (notmuch-show-get-subject))
+ (to (notmuch-show-get-to))
+ (from (notmuch-show-get-from))
+ desc link)
+ (org-store-link-props :type "notmuch" :from from :to to
+ :subject subject :message-id message-id)
+ (setq desc (org-email-link-description))
+ (setq link (concat "notmuch:" "id:" message-id))
+ (org-add-link-props :link link :description desc)
+ link)))
+
+ (defun org-notmuch-open (path)
+ "Follow a notmuch message link specified by PATH."
+ (org-notmuch-follow-link path))
+
+ (defun org-notmuch-follow-link (search)
+ "Follow a notmuch link to SEARCH.
+
+Can link to more than one message, if so all matching messages are shown."
+ (require 'notmuch)
+ (notmuch-show (org-link-unescape search)))
+
+ (org-add-link-type "notmuch-search" 'org-notmuch-search-open)
+ (add-hook 'org-store-link-functions 'org-notmuch-search-store-link)
+
+ (defun org-notmuch-search-store-link ()
+ "Store a link to a notmuch search or message."
+ (when (eq major-mode 'notmuch-search-mode)
+ (let ((link (concat "notmuch-search:"
+ (org-link-escape notmuch-search-query-string)))
+ (desc (concat "Notmuch search: " notmuch-search-query-string)))
+ (org-store-link-props :type "notmuch-search"
+ :link link
+ :description desc)
+ link)))
+
+ (defun org-notmuch-search-open (path)
+ "Follow a notmuch message link specified by PATH."
+ (message path)
+ (org-notmuch-search-follow-link path))
+
+ (defun org-notmuch-search-follow-link (search)
+ "Follow a notmuch link by displaying SEARCH in notmuch-search mode."
+ (require 'notmuch)
+ (notmuch-search (org-link-unescape search)))
+
+ ;; If you use `org' and don't want your org files in the default location below,
+ ;; change `org-directory'. It must be set before org loads!
+ (setq org-directory "~/org/")
+ (setq org-todo-keywords
+ '((sequence
+ "TODO(t)" "ACTIVE(a!)" "WAIT(w@/!)" "|" "DONE(d!)" "CANCELED(c@)")))
+ (setq org-refile-use-outline-path nil)
+ (setq org-refile-targets '((nil . (:maxlevel . 2))))
+ (setq org-log-into-drawer t)
+ (setq org-agenda-log-mode-items '(closed clock state))
+ (setq org-agenda-start-day nil)
+ (setq org-agenda-block-separator nil)
+ (setq org-agenda-compact-blocks t)
+ (setq org-agenda-start-with-log-mode nil)
+ (setq org-habit-show-all-today t)
+ (setq org-agenda-time-grid '((daily today require-timed) (800 1600) "......" "----------------------"))
+ (setq org-roam-directory (concat org-directory "roam/"))
+ (setq-default org-agenda-files '())
+ (dolist (file '("tasks.org"
+ "running-2022.org"))
+ (add-to-list 'org-agenda-files (concat org-roam-directory file)))
+ (setq org-roam-db-location (concat org-roam-directory "/org-roam.db"))
+ (setq org-export-with-toc nil)
+ (setq deft-directory org-directory)
+ (setq deft-recursive t)
+ ;; Org html export
+ (setq org-html-htmlize-output-type 'css)
+ ;; Website publish settings.
+ (defvar kj/publish-tag "publish")
+ (defvar kj/publish-directory "/ssh:orbekk@dragon.orbekk.com:/storage/srv/kj.orbekk.com")
+ (add-to-list 'org-modules 'org-habit)
+
+ (defvar org-publish-project-alist)
+ (defun kj/find-agenda-files-containing-tag (tag)
+ (setq org-agenda-archives-mode nil)
+ (let* ((org-agenda-archives-mode nil)
+ (candidates (org-agenda-files nil 'ifmode))
+ (matcher (cdr (org-make-tags-matcher tag)))
+ (files))
+ ;; (message "Results:")
+ (dolist (file candidates files)
+ (org-check-agenda-file file)
+ (with-current-buffer (org-get-agenda-file-buffer file)
+ ;; (message "%S" (org-scan-tags 'agenda matcher nil))
+ (when (org-scan-tags 'agenda matcher nil)
+ (push file files))))))
+
+ (defun kj/org-publish (&optional project force)
+ (interactive)
+ (setq project (or project "all"))
+ (setq force (or force current-prefix-arg))
+ (setq org-agenda-files (list org-roam-directory))
+ (let* ((static-files-re (string-join '("css" "txt" "jpg" "png" "gif" "svg") "\\|"))
+ (files-to-include (kj/find-agenda-files-containing-tag kj/publish-tag))
+ ;; Disable org babel exports during publish entirely to speed up publish.
+ ;; This messes up babel output handling.
+ ;; (org-export-use-babel nil)
+ (org-babel-default-header-args
+ (kj/assq-replace '((:exports . "both")
+ (:eval . "never-export"))
+ org-babel-default-header-args))
+ (org-publish-project-alist
+ `(
+ ("static"
+ :base-directory ,(concat org-roam-directory "/static")
+ :base-extension ,static-files-re
+ :recursive t
+ :publishing-directory ,(concat kj/publish-directory "/static")
+ :publishing-function org-publish-attachment)
+ ("source"
+ :base-directory ,org-roam-directory
+ :base-extension "org"
+ :exclude ".*"
+ :include ,files-to-include
+ :recursive t
+ :publishing-directory ,kj/publish-directory
+ :publishing-function org-publish-attachment)
+ ("html"
+ :base-directory ,org-roam-directory
+ :base-extension "org"
+ :recursive t
+ :exclude ".*"
+ :include ,files-to-include
+ :publishing-directory ,kj/publish-directory
+ :publishing-function org-html-publish-to-html
+
+ :with-broken-links t
+ :with-toc nil
+ :with-latex t
+ :with-drawers t
+ :with-title t
+ :section-numbers nil
+
+ ;; HTML options
+ :html-toplevel-hlevel 2
+ :html-preamble ""
+ :html-postamble ""
+ :html-html5-fancy t
+ :html-doctype "html5"
+ :html-head "<link rel=\"stylesheet\" type=\"text/css\" href=\"static/org.css\"/>"
+ :html-head-include-scripts nil
+ :html-head-include-default-style nil
+ :html-container article)
+
+ ("all" :components ("static" "source" "html")))))
+
+ (org-publish project force)))
+ ; (setq org-appear-autolinks t)
+ ; (use-package! org-appear
+ ; :hook (org-mode . org-appear-mode))
+
+ (setq org-log-done 'time)
+ (setq org-todo-keywords
+ '((sequence
+ "TODO(t)" ; A task that needs doing & is ready to do
+ "PROJ(p)" ; A project, which usually contains other tasks
+ "LOOP(r)" ; A recurring task
+ "STRT(s)" ; A task that is in progress
+ "WAIT(w)" ; Something external is holding up this task
+ "HOLD(h)" ; This task is paused/on hold because of me
+ "IDEA(i)" ; An unconfirmed and unapproved task or notion
+ "|"
+ "DONE(d!)" ; Task successfully completed
+ "KILL(k)") ; Task was cancelled, aborted or is no longer applicable
+ (sequence
+ "[ ](T)" ; A task that needs doing
+ "[-](S)" ; Task is in progress
+ "[?](W)" ; Task is being held up or paused
+ "|"
+ "[X](D)") ; Task was completed
+ (sequence
+ "|"
+ "OKAY(o)"
+ "YES(y)"
+ "NO(n)")))
+
+ (setq org-journal-file-type 'weekly)
+ (setq org-journal-dir org-roam-directory)
+ (setq org-journal-file-format "journal-%Y-%m-%d.org")
+ (setq org-journal-enable-agenda-integration t)
+ (defun kj/org-journal-init ()
+ (interactive)
+ (save-excursion
+ (goto-char (point-min))
+ (org-id-get-create)
+ (org-set-property "category" "journal")))
+
+ (defun kj/org-journal-file-header (time)
+ (format-time-string "#+title: Week of %F (%W)\n"
+ (org-journal--convert-time-to-file-type-time time)))
+ (setq org-journal-file-header #'kj/org-journal-file-header)
+ (add-hook 'org-journal-after-header-create-hook 'kj/org-journal-init)
+
+ ;; Org
+ (add-to-list 'org-export-backends 'md)
+
+ (require 'org-tempo))
+
+(defun org-archive-done-tasks ()
+ (interactive)
+ (org-map-entries
+ (lambda ()
+ (org-archive-subtree)
+ (setq org-map-continue-from (org-element-property :begin (org-element-at-point))))
+ "/DONE" 'file))
+
+(use-package org-pomodoro
+ :ensure t
+ :after org
+ :commands (org-pomodoro)
+ :config
+ (setq
+ org-pomodoro-manual-break t
+ org-pomodoro-time-format "%m"
+ org-pomodoro-format "🍅%s"
+ org-pomodoro-play-sounds nil
+ alert-user-configuration (quote ((((:category . "org-pomodoro")) notifications nil))))
+ :bind
+ (("C-c o p" . org-pomodoro)))
+
+(use-package org-tidy
+ :after org
+ :hook (org-mode . org-tidy-mode))
+
+(provide 'kj-org)
diff --git a/emacs/.config/emacs-v2/lisp/kj-popup.el b/emacs/.config/emacs-v2/lisp/kj-popup.el
new file mode 100644
index 0000000..0ac99a5
--- /dev/null
+++ b/emacs/.config/emacs-v2/lisp/kj-popup.el
@@ -0,0 +1,95 @@
+;;; -*- lexical-binding: t; -*-
+;;; Window placement
+;;; TODO: Test popper.el.
+;; (global-set-key (kbd "C-`") #'window-toggle-side-windows)
+;; (global-set-key (kbd "M-`") #'kj/popup-toggle-override)
+;; (setq window-sides-slots '(0 0 2 2)) ; 2 right side windows
+(setq switch-to-buffer-obey-display-actions t)
+(setq switch-to-buffer-in-dedicated-window 'pop)
+(setq even-window-sizes 'width-only)
+(defvar kj/popup-override nil)
+
+(defun kj/popup-clear-overrides ()
+ (interactive)
+ (dolist (buffer (buffer-list))
+ (with-current-buffer buffer
+ (kill-local-variable 'kj/popup-override))))
+
+(defun kj/popup-toggle-override ()
+ (interactive)
+ (if (local-variable-p 'kj/popup-override)
+ (kill-local-variable 'kj/popup-override)
+ (setq-local kj/popup-override
+ (not (kj/popup-buffer-base (buffer-name)))))
+ (let ((buffer (buffer-name)))
+ (switch-to-prev-buffer)
+ (switch-to-buffer buffer)))
+
+(defun kj/popup-buffer-base (buffer &optional condition)
+ (or
+ (s-matches-p
+ (rx (| "*Help*"
+ "*info*"
+ (seq "*Messages*")
+ (seq "*Warnings*")
+ (seq "*Backtrace*")
+ (seq "*eldoc " (* anything) "*")
+ (seq "*WoMan " (* anything) "*")
+ (seq "*man " (* anything) "*")
+ (seq "fig: " (* anything))
+ (seq "*htop*" (* anything))
+ (seq "*Proced*" (* anything))
+ (seq "*eshell" (* anything))
+ (seq "*shell" (* anything))
+ "*Occur*")) buffer)
+ (with-current-buffer buffer
+ (apply #'derived-mode-p
+ '(magit-mode
+ compilation-mode
+ shell-mode
+ comint-mode
+ Custom-mode ; For customization
+ fig-commit-mode)))))
+
+(defun kj/popup-buffer (buffer &optional condition)
+ (with-current-buffer buffer
+ (if (local-variable-p 'kj/popup-override)
+ kj/popup-override
+ (kj/popup-buffer-base buffer condition))))
+
+;; (setq transient-display-buffer-action
+;; '(display-buffer-in-best-side
+;; (slot . -1)
+;; (inhibit-same-window . t)
+;; (window-parameters
+;; (no-other-window . t))))
+
+(defun kj/display-new-popup (buffer alist)
+ (let ((popup-window
+ (display-buffer-in-direction
+ buffer
+ (append (if (> (frame-width) 160)
+ '((direction . rightmost)
+ (window-width . 80))
+ '((direction . bottom)))))))
+ (set-window-parameter popup-window 'kj/popup t)
+ popup-window))
+
+(defun kj/find-popup-window ()
+ (seq-find
+ (lambda (w)
+ (window-parameter w 'kj/popup))
+ (window-list)))
+
+(defun display-popup-buffer (buffer alist)
+ (let ((window (kj/find-popup-window)))
+ (if window
+ (window--display-buffer buffer window 'reuse alist)
+ (kj/display-new-popup buffer alist))))
+
+;; (add-to-list 'display-buffer-alist
+;; `(kj/popup-buffer
+;; (display-popup-buffer)
+;; (slot . 0)))
+
+(provide 'kj-popup)