From 7a07cdd61d626b42f2b05ef8fb843a29b57ed580 Mon Sep 17 00:00:00 2001 From: Kjetil Orbekk Date: Sun, 7 Jan 2024 13:04:45 -0500 Subject: Testing new configuration --- emacs/.config/chemacs/profiles.el | 4 +- emacs/.config/emacs-kj/init.el | 13 +- emacs/.config/emacs-kj/lisp/kj-history.el | 2 + emacs/.config/emacs-v2/early-init.el | 32 + emacs/.config/emacs-v2/elpaca-installer.el | 38 + emacs/.config/emacs-v2/emacs-custom.el | 16 + emacs/.config/emacs-v2/init.el | 40 + emacs/.config/emacs-v2/lisp/kj-bindings.el | 34 + emacs/.config/emacs-v2/lisp/kj-defaults.el | 50 ++ emacs/.config/emacs-v2/lisp/kj-development.el | 91 +++ emacs/.config/emacs-v2/lisp/kj-email.el | 84 ++ emacs/.config/emacs-v2/lisp/kj-eshell.el | 83 ++ emacs/.config/emacs-v2/lisp/kj-history.el | 66 ++ emacs/.config/emacs-v2/lisp/kj-init.el | 861 +++++++++++++++++++++ emacs/.config/emacs-v2/lisp/kj-lib.el | 11 + emacs/.config/emacs-v2/lisp/kj-multiple-cursors.el | 20 + emacs/.config/emacs-v2/lisp/kj-org.el | 251 ++++++ emacs/.config/emacs-v2/lisp/kj-popup.el | 95 +++ 18 files changed, 1781 insertions(+), 10 deletions(-) create mode 100644 emacs/.config/emacs-v2/early-init.el create mode 100644 emacs/.config/emacs-v2/elpaca-installer.el create mode 100644 emacs/.config/emacs-v2/emacs-custom.el create mode 100644 emacs/.config/emacs-v2/init.el create mode 100644 emacs/.config/emacs-v2/lisp/kj-bindings.el create mode 100644 emacs/.config/emacs-v2/lisp/kj-defaults.el create mode 100644 emacs/.config/emacs-v2/lisp/kj-development.el create mode 100644 emacs/.config/emacs-v2/lisp/kj-email.el create mode 100644 emacs/.config/emacs-v2/lisp/kj-eshell.el create mode 100644 emacs/.config/emacs-v2/lisp/kj-history.el create mode 100644 emacs/.config/emacs-v2/lisp/kj-init.el create mode 100644 emacs/.config/emacs-v2/lisp/kj-lib.el create mode 100644 emacs/.config/emacs-v2/lisp/kj-multiple-cursors.el create mode 100644 emacs/.config/emacs-v2/lisp/kj-org.el create mode 100644 emacs/.config/emacs-v2/lisp/kj-popup.el 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-" . 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 + (" 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 "") (kbd ""))) + ;; 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 + '("" . 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) + '("" . 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-" . sp-down-sexp) + ("C-" . sp-up-sexp) + ("M-" . sp-backward-down-sexp) + ("M-" . 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-" . sp-forward-slurp-sexp) + ("M-" . sp-forward-barf-sexp) + ("C-" . sp-backward-slurp-sexp) + ("M-" . 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-" . backward-kill-word) + ("C-" . 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-") + #'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 "") 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 "" + :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) -- cgit v1.2.3