#+TODO: TODO WIP WISH CLEANUP FIXME REVIEW | #+OPTIONS: use-property-inheritance:t #+PROPERTY: header-args :load yes :tangle init.el :comments link #+EXCLUDE_TAGS: disabled #+BEGIN_SRC emacs-lisp :comments no :tangle yes ;; -*- lexical-binding: t -*- #+END_SRC Some background is important to understand my keybinding choices - 1. I use modifier-less bindings wherever possible. Hydra and modal editing make up most of my interaction with Emacs. 2. I use Kmonad to have Space act as Control when held. This makes Control the modifier which disrupts the typing position the least. It is also held with the thumbs - the strongest digits - reducing fatigue. So if I must use a modifier, I use Control. 3. I use the Dvorak layout. 4. I've used Vim and Evil for the longest time, but I like Boon's movement layout best - * QWERTY IO move up and down * QWERTY KL move left and right in small units (characters), and * QWERTY J; move left and right in larger units (words or s-expressions) This has several benefits over HJKL - 1. To move left, the wrist need not to be angled awkwardly to sit on QWERTY HJKL (which also means the keyboard tactile nub on J does not sit under the index finger), 2. ...nor need the finger make an awkward jump to QWERTY H on each left-moving operation. 3. The relaxed position of the fingers placed on a flat surface is a curl, not a line, which is reflected in Boon's default JIO; position. * package.el :package: :PROPERTIES: :CREATED: 2021-12-20T20:41:17+0530 :CUSTOM_ID: package.el :END: #+BEGIN_SRC emacs-lisp (require 'package) ;; ;; when GNU ELPA is down ;; (setq package-archives (assoc-delete-all "gnu" package-archives)) (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) ;; Comment/uncomment this line to enable MELPA Stable if desired. See ;; `package-archive-priorities` and `package-pinned-packages`. Most ;; users will not need or want to do this. ;; (add-to-list 'package-archives '("melpa-stable" . "https://stable.melpa.org/packages/") t) (package-initialize) #+END_SRC * use-package :package: :PROPERTIES: :CUSTOM_ID: use-package :END: #+BEGIN_SRC emacs-lisp (add-to-list 'load-path "~/.emacs.d/elisp-git/use-package/") (require 'use-package) (setq use-package-compute-statistics t) #+END_SRC * quelpa :PROPERTIES: :CUSTOM_ID: quelpa :END: #+BEGIN_SRC emacs-lisp (unless (package-installed-p 'quelpa) (with-temp-buffer (url-insert-file-contents "https://github.com/quelpa/quelpa/raw/master/quelpa.el") (eval-buffer) (quelpa-self-upgrade))) (quelpa '(quelpa-use-package :fetcher git :url "https://github.com/quelpa/quelpa-use-package.git")) (require 'quelpa-use-package) ;; Don't upgrade Quelpa packages if there's no connectivity. ;; Courtesy https://emacs.stackexchange.com/a/18515 (defun my-internet-up-p (host) (= 0 (call-process "ping" nil nil nil "-c" "1" "-W" "1" host))) (setq quelpa-checkout-melpa-p nil quelpa-upgrade-p (my-internet-up-p "github.com")) #+END_SRC * TODO dvorak [60%] :PROPERTIES: :CUSTOM_ID: dvorak :END: At first I decided to remap all Emacs, Boon, and Hydra bindings, but it turned out to not be my idea of fun. I wrote boon-dvorak, and let [most of] the others be. (QWERTY) C-w, C-u, and C-h are probably best left as they are, since they are also present in other applications. 1. [X] M-n, M-p, -> M-r, M-c 2. [X] M-q -> M-' 3. [X] M-- 4. [ ] multiple-cursors 5. [ ] boon-c-god is bound to "j", which emulates QWERTY "c", but the bindings after it do not. Thus, to input "C-c C-c", one must press "j c" in Dvorak. Yuck. 6. [ ] M-n M-n not getting bound to font-lock-fontify-block ? 7. [ ] binding C-d to keyboard-quit in the minibuffer #+BEGIN_SRC emacs-lisp (use-package general :commands (general-auto-unbind-keys general-def general-define-key)) #+END_SRC * exwm :disabled: :PROPERTIES: :literate-load: no :header-args: :load no :tangle no :CUSTOM_ID: exwm :END: #+BEGIN_SRC emacs-lisp :load no (setq exwm-input-global-keys '(([?\C-1] . select-window-1) ([?\C-2] . select-window-2) ([?\C-3] . select-window-3) ([?\C-4] . select-window-4) ([?\C-7] . select-window-1) ([?\C-8] . select-window-2) ([?\C-9] . select-window-3) ([?\C-0] . select-window-4) ([?\C-\\] . toggle-input-method) ([?\C-`] . shell) ([?\s-q] . exwm-input-toggle-keyboard) ([?\s-j] . split-window-below) ([?\s-k] . split-window-right) ([?\s-x] . delete-other-windows) ([?\s-b] . delete-other-windows) ([?\s-m] . my-general-hydra/body) ([?\s-w] . consult-buffer) ([?\s-v] . exwm-input-toggle-keyboard))) #+END_SRC Use window title for buffer names. #+BEGIN_SRC emacs-lisp :load no (require 'exwm) (add-hook 'exwm-update-title-hook (lambda () (exwm-workspace-rename-buffer exwm-title))) (exwm-enable) #+END_SRC ** Helpers :PROPERTIES: :literate-load: yes :header-args: :load yes :tangle yes :CUSTOM_ID: helpers :END: A less repetitive way to start processes. Also enables Boon in the process buffer, so I can easy navigate it, copy text in it, or switch away from it. #+BEGIN_SRC emacs-lisp :load no (defun my-start-process (program &optional name &rest args) "Run PROGRAM with ARGS in buffer NAME." (interactive "P") (let* ((name (if name name program)) (proc-buffer (generate-new-buffer-name name))) (apply #'start-process name proc-buffer program args) (with-current-buffer proc-buffer (boon-mode)))) #+END_SRC This is useful even without EXWM - switching does not work (...yet?), but I can launch apps via Hydra, and the output goes into the familiar environment of an Emacs buffer. #+BEGIN_SRC emacs-lisp (defun my-start-app-or-switch (program &optional class-re title-re name &rest args) "Switch to EXWM buffer matching CLASS-RE or TITLE-RE, or run PROGRAM with ARGS. NAME is the name of the process and its buffer." (let* ((name (if name name program)) (regex (or class-re title-re program)) (buffer (when (featurep 'exwm) (seq-find (lambda (buffer) (with-current-buffer buffer (if title-re (and exwm-title (string-match-p title-re exwm-title)) (and exwm-class-name (string-match-p regex exwm-class-name))))) (buffer-list))))) (if buffer (switch-to-buffer buffer) (apply #'my-start-process program name args)))) #+END_SRC ** Startup programs :disabled: :PROPERTIES: :CUSTOM_ID: startup-programs :END: #+BEGIN_SRC emacs-lisp :load no :tangle no (my-start-process (expand-file-name "~/bin/kmonad") "kmonad" (expand-file-name "~/kmonad-tvs.kbd")) (my-start-process (expand-file-name "~/bin/kmonad") "kmonad" (expand-file-name "~/kmonad-legion.kbd")) (my-start-process "transmission-gtk") (my-start-process "gajim") (my-start-process "/usr/lib/notification-daemon/notification-daemon") #+END_SRC ** volume :PROPERTIES: :CUSTOM_ID: volume :END: #+BEGIN_SRC emacs-lisp (use-package volume :ensure t :bind (:map volume-mode-map ("c" . volume-raise) ("r" . volume-lower) ("C-c" . volume-raise-10) ("C-r" . volume-lower-10))) #+END_SRC * Emacs-wide settings and Emacs-specific packages :PROPERTIES: :CUSTOM_ID: emacs-settings :END: #+BEGIN_SRC emacs-lisp (use-package emacs :config (setq gc-cons-threshold 100000000 delete-by-moving-to-trash t trash-directory "~/.trash/" history-length 10000 use-file-dialog nil load-prefer-newer t ;; disable the disabled commands behavior disabled-command-function nil custom-file "~/.emacs.d/custom.el" print-length nil eval-expression-print-length nil edebug-print-length nil ispell-dictionary "en" scroll-conservatively 10000 scroll-preserve-screen-position t auto-window-vscroll nil locale-coding-system 'utf-8 file-name-coding-system 'utf-8 buffer-file-coding-system 'utf-8 inhibit-startup-screen t save-interprogram-paste-before-kill t large-file-warning-threshold (* 50 1000 1000)) (set-terminal-coding-system 'utf-8) (set-keyboard-coding-system 'utf-8) (set-selection-coding-system 'utf-8) (prefer-coding-system 'utf-8) (setq-default undo-limit (* 80 1000)) (load custom-file) :bind ("M-h" . default-indent-new-line) ;; QWERTY M-j ;; see also org-mode ("M-'" . fill-paragraph) ;; QWERTY M-q ("C-j" . ctl-x-map) ;; QWERTY C-c ("M-n M-n" . font-lock-fontify-block) ;; basic Modifier-based editing commands ;; "C-d" 'backward-delete-char ;; unnecessary - QWERTY C-h is merely next to your index finger, but Dvorak C-h is directly under it ("C-," . backward-kill-word) ;; QWERTY C-w ;; ;; Emacs-like ("M-e" . kill-word) ;; QWERTY M-d ("C-e" . delete-char) ;; QWERTY C-d ("M-u" . forward-word) ;; QWERTY M-f ("C-." . end-of-line) ;; QWERTY C-e ;; new ("C-h" . backward-delete-char) ;; Dvorak C-h = QWERTY C-j (an improvement) ("C-c RET" . #'consult-mark) ;; c m/c RET with boon-c-god ("C-c S-RET" . #'consult-global-mark) ;; c M/c S-RET with boon-c-god ;; for swapped parenthesis and square brackets layout ("C-)" . abort-recursive-edit) (:map text-mode-map ("M-." . forward-sentence)) ;; ;; The old C-n/C-p/M-n/M-p were not the most comfortable to begin with, so I take this opportunity to bind them to something consistent with Boon (:map minibuffer-local-map ("C-c" . previous-line) ("C-r" . next-line) ("M-c" . previous-history-element) ("M-r" . next-history-element) ("M-p" . previous-matching-history-element)) (:map read-expression-map ;; not sure if this works ("C-c" . previous-line) ("C-r" . next-line) ("M-c" . previous-history-element) ("M-r" . next-history-element))) #+END_SRC ** package.el :package: :PROPERTIES: :CUSTOM_ID: package.el-1 :END: #+BEGIN_SRC emacs-lisp (use-package package :bind (:map package-menu-mode-map ("k" . package-autoremove) ("c" . previous-line) ("r" . next-line) ("X" . package-menu-execute))) #+END_SRC ** feather :package: :PROPERTIES: :CUSTOM_ID: feather :END: #+BEGIN_SRC emacs-lisp (use-package feather :disabled t :ensure t :diminish :hook (package-menu-mode . feather-mode) :bind (" p " . list-packages)) #+END_SRC ** esup, the Emacs StartUp Profiler :PROPERTIES: :CUSTOM_ID: esup :END: #+BEGIN_SRC emacs-lisp (use-package esup :ensure t :config (setq esup-depth 0)) #+END_SRC ** gnutls :PROPERTIES: :CUSTOM_ID: gnutls :END: #+BEGIN_SRC emacs-lisp (use-package gnutls :config ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=36749 (setq gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3")) #+END_SRC ** help-mode :viewer: :PROPERTIES: :CUSTOM_ID: help-mode :END: #+BEGIN_SRC emacs-lisp (use-package help-mode :bind (:map help-mode-map ("b" . help-go-back) ("f" . help-go-forward))) #+END_SRC ** helpful :viewer: :PROPERTIES: :CUSTOM_ID: helpful :END: #+BEGIN_SRC emacs-lisp (use-package helpful :ensure t :bind ((" " . #'helpful-at-point) (" f" . #'helpful-callable) (" c" . #'helpful-command) (" k" . #'helpful-key) (" v" . #'helpful-variable))) #+END_SRC ** keyfreq :disabled: :PROPERTIES: :CUSTOM_ID: keyfreq :END: #+BEGIN_SRC emacs-lisp (use-package keyfreq :init (keyfreq-mode 1) (keyfreq-autosave-mode 1)) #+END_SRC #+BEGIN_SRC emacs-lisp ;; ;; disabled on 2017-08-18T19:39:21+0530, no longer interested ;; (open-dribble-file (concat "~/.emacs.d/keylogs/" ;; (format-time-string "%Y%m%d-%H%M%S") ;; ".txt")) ;; 2017-10-14T15:22:56+0530 - I suspect devanagari-itrans tires the ;; left hand faster than the right - let's find out! ;; ;; 2020-08-05T16:28:07+0530 commented out, no longer interested ;; (add-hook ;; 'input-method-activate-hook ;; (lambda () ;; (open-dribble-file ;; (concat ;; "~/.emacs.d/keylogs/" ;; (format-time-string "%Y%m%d-%H%M%S") ;; "-" ;; current-input-method ;; ".txt")))) ;; (add-hook ;; 'input-method-deactivate-hook ;; (lambda () (open-dribble-file nil))) #+END_SRC * User interface :PROPERTIES: :CUSTOM_ID: user-interface :END: #+BEGIN_SRC emacs-lisp ;; Simplify the GUI, thanks ;; http://www.masteringemacs.org/articles/2010/10/04/beginners-guide-to-emacs/ ;; (menu-bar-mode -1) (tool-bar-mode -1) (scroll-bar-mode -1) (horizontal-scroll-bar-mode -1) (display-time-mode t) (setq focus-follows-mouse t mouse-autoselect-window t display-time-format "%a, %d %h %Y %T" display-time-interval 1 use-dialog-box nil) ;; Highlight current line (global-hl-line-mode 1) (defalias 'yes-or-no-p 'y-or-n-p) #+END_SRC ** font size :PROPERTIES: :CUSTOM_ID: font-size :END: #+BEGIN_SRC emacs-lisp (setq default-frame-alist '((font . "DejaVu Sans Mono-12"))) ;; (set-face-attribute 'default nil :height 120) #+END_SRC ** time :PROPERTIES: :CREATED: 2022-01-16T12:47:09+0530 :CUSTOM_ID: time :END: #+BEGIN_SRC emacs-lisp (use-package time :config (setq display-time-next-load-average t) (add-to-list 'zoneinfo-style-world-list '("Europe/Berlin" "Berlin"))) #+END_SRC ** shackle :PROPERTIES: :CUSTOM_ID: shackle :END: #+BEGIN_SRC emacs-lisp (use-package shackle :ensure t :init (shackle-mode) :config (setq shackle-rules '((Info-mode :same t)))) #+END_SRC ** Theme :PROPERTIES: :CUSTOM_ID: theme :END: Must put this after loading the custom file, or I get prompted about the theme each time. #+BEGIN_SRC emacs-lisp (require 'doom-themes) #+END_SRC #+BEGIN_SRC emacs-lisp :load no :tangle no (load-theme 'doom-acario-dark) ;; Similar to doom-acario-dark, with paler colors. But Org headlines are all much too similar for my liking. (load-theme 'doom-material) #+END_SRC #+BEGIN_SRC emacs-lisp (load-theme 'doom-molokai) #+END_SRC #+BEGIN_SRC emacs-lisp ;(load-theme 'distinguished t) ;(require 'github-theme) ;(require 'heroku-theme) ;;;; Good theme but uses different font sizes in org-mode, and (I think) some weird non-monospace font ;; (require 'monokai-theme) ;; ;; disabled on 2021-02-11T19:58:37+0530 - was coloring Org source blocks as comments? :\ ;; (require 'molokai-theme) ;; (enable-theme 'molokai) ;; (require 'fullscreen-mode) ;; (fullscreen-mode 1) ;; (require 'relative-line-numbers) ;; ;; old ;; ;; (setq relative-line-numbers-motion-function 'vertical-motion) ;; ;; new ;; (setq relative-line-numbers-motion-function 'forward-visible-line) ;; (global-relative-line-numbers-mode 1) ;; (add-hook 'shell-mode-hook 'previous-buffer) ;; ;; is this what deletes windows when you run a shell command? ;; (add-hook 'shell-mode-hook 'delete-other-windows) ;; 2018-08-26T22:39:09+0530 (defun cp/change-split () "Switch between two windows in horizontal layout and vice-versa." (interactive) (let (())) ;; if height of window > width, we're in a vertical split ;; otherwise we're in a horizontal split ) #+END_SRC ** #+BEGIN_SRC emacs-lisp (autoload 'byte-recompile-file "bytecomp" "byte-recompile-file" t) ;; (2017-12-29T13:21:57+0530 ;; TODO - watch Org and MD files and recompile it if they are newer ;; than their associated HTML files (e.g. I edited the source on a ;; phone and synced it back to the laptop) ;; see (info "(elisp) File Notifications") ;; and (describe-function 'file-newer-than-file-p) ;; ) #+END_SRC ** #+BEGIN_SRC emacs-lisp (defun cp/after-save () (let* ((file-path (buffer-file-name)) (file-path-shell (shell-quote-argument file-path))) (cl-case major-mode ;; ;; This would be more useful if it was only displayed when ;; ;; tests failed. But even a constantly failing test result ;; ;; being shown each time you save can be annoying. ;; ('emacs-lisp-mode (let ((project-dir (locate-dominating-file file-path "Cask"))) ;; (when project-dir ;; (cd project-dir) ;; (compile "cask exec buttercup -L . --traceback pretty")))) ;; ;; Handy as long as my only means of viewing Org data on mobile was the HTML export; not so much since I have Orgzly ;; ('org-mode ;; (pcase (file-name-nondirectory ;; (buffer-file-name ;; (current-buffer))) ;; ((or "chronometrist.org" "chronometrist-key-values.org" "init.org") t) ;; (_ (org-html-export-to-html)))) ('LilyPond-mode (my-compile-project "mkly" nil "./mkly dev")) ('latex-mode (if (file-exists-p "Makefile") (compile (car compile-history)) (compile (concat "xelatex " file-path-shell)))) ;; ('markdown-mode (markdown-export)) ('c-mode (compile (concat "gcc -static -o " (shell-quote-argument (file-name-base)) " " file-path-shell)))))) (add-hook 'after-save-hook 'cp/after-save) #+END_SRC ** compile :PROPERTIES: :CUSTOM_ID: compile :END: #+BEGIN_SRC emacs-lisp (use-package compile :config ;; (add-hook 'compilation-start-hook ;; (lambda (proc) (delete-other-windows))) (setq compilation-always-kill t)) #+END_SRC *** my-compile-project :PROPERTIES: :CREATED: 2022-01-16T12:44:33+0530 :CUSTOM_ID: my-compile-project :END: #+BEGIN_SRC emacs-lisp (defun my-compile-project (file &optional prefix cmd) "Enter ancestor directory containing FILE and run compile command CMD. If CMD is not supplied, use `compile-command'. With PREFIX argument and omitted CMD, prompt for command." (interactive "fDominating File: \nP") (when-let ((dir (locate-dominating-file default-directory file))) (cd dir)) (compile (cond (prefix (compilation-read-command (or cmd ""))) (cmd cmd) (t compile-command))) ;; (select-window (split-window-below)) ;; (switch-to-buffer (compilation-find-buffer)) (boon-mode)) #+END_SRC *** jump to Org LP from compilation output :PROPERTIES: :CUSTOM_ID: my-org-lp-goto-error :END: #+BEGIN_SRC emacs-lisp (defun my-org-lp-goto-error (oldfn &optional prefix &rest args) "Make `compile-goto-error' lead to an Org literate program, if present. This is meant to be used as `:around' advice for `compile-goto-error'. OLDFN is `compile-goto-error'. With PREFIX arg, just run `compile-goto-error' as though unadvised. ARGS are ignored." (interactive "P") (if prefix (funcall oldfn) (let (buffer position column tangled-file-exists-p) (save-window-excursion (funcall oldfn) (setq column (- (point) (point-at-bol))) ;; `compile-goto-error' might be called from the output of ;; `literate-elisp-byte-compile-file', which means ;; `org-babel-tangle-jump-to-org' would error (when (ignore-errors (org-babel-tangle-jump-to-org)) (setq buffer (current-buffer) position (point) tangled-file-exists-p t))) ;; back to where we started - the `compilation-mode' buffer (if tangled-file-exists-p (let ((org-window (get-buffer-window buffer))) ;; if the Org buffer is visible, switch to its window (if (window-live-p org-window) (select-window org-window) (switch-to-buffer buffer)) (goto-char (+ position column))) (funcall oldfn))))) (advice-add 'compile-goto-error :around #'my-org-lp-goto-error) ;; (advice-remove 'compile-goto-error #'my-org-lp-goto-error) #+END_SRC ** #+BEGIN_SRC emacs-lisp (add-to-list 'load-path "~/.emacs.d/contrapunctus/") (require 'cp-hindi) #+END_SRC ** TODO Hydra [0%] :PROPERTIES: :CUSTOM_ID: hydra :END: I started off using Hydra for programming modes, when I noticed that Elisp, Common Lisp, and Scheme all had some semantically-analogous operations with different names, which could be abstracted away behind a generic interface. Then, around the time I got into using Org for literate programs, I added an Org hydra, and then a general hydra for frequently-used operations. Add these common operations to the hydra - 1. [X] =enlarge-window=, =enlarge-window-horizontally= 2. [X] =save-buffer=, =kill-buffer= 3. [ ] switch to last buffer, such that pressing it twice brings you back to the original buffer. Bind to "m" (same key as the one to launch the Hydra), moving magit to "M". * Trickier to implement than I thought. 4. [X] =toggle-debug-on-error= 5. [X] "insert" hydra, for timestamps, dates; in Elisp, insert as strings; in Org, in angular brackets; etc 6. [X] remove duplication #+BEGIN_SRC emacs-lisp (use-package hydra :ensure t :commands defhydra) #+END_SRC *** common hydra heads :PROPERTIES: :CUSTOM_ID: common-hydra-heads :END: #+BEGIN_SRC emacs-lisp (defvar my-hydra-common-heads '(("0" delete-window "delete this" :color red) ("1" delete-other-windows "delete others" :color red) ("2" split-window-below "split below" :color red) ("3" split-window-right "split right" :color red) ("7" delete-other-windows "delete others" :color red) ("8" split-window-below "split below" :color red) ("9" split-window-right "split right" :color red) ("]" text-scale-increase "zoom in" :color red) ("[" text-scale-decrease "zoom out" :color red) ("\\" (text-scale-increase 0) "zoom reset" :color red) ("a" my-app-hydra/body "applications") ("z" my-app-hydra/body "applications") ("C" contrapunctus-mc-hydra/body "multiple cursors") ("d" dired-jump "dired-jump") ("D" (cp-insert-timestamp t) "date") ("f" my-search-hydra/body "find") ("i" (find-file "~/.emacs.d/init.org") "open init") ("I" contrapunctus-info-hydra/body "Info") ("k" (kill-buffer (current-buffer)) "kill" :color red) ("m" my-buffer-switch "Switch buffers") ("n" imenus "navigation") ("N" contrapunctus-line-display-hydra/body "line display") ("o" save-buffer "save") ;; QWERTY s ("s" save-buffer "save") ("S" imenu-list "sidebar") ("T" cp-insert-timestamp "timestamp") ("C-t" tempel-insert "template") ("u" find-file "new") ("U" launch-file "launch-file") ("V" volume "volume") ("C-v" find-alternate-file "revert") ("w" contrapunctus-window-hydra/body "window"))) (defmacro my-defhydra (name body docstring &rest unique-forms) (declare (indent defun)) `(defhydra ,name ,body ,docstring ,@unique-forms ,@my-hydra-common-heads)) #+END_SRC *** Line display :PROPERTIES: :CUSTOM_ID: line-display :END: #+BEGIN_SRC emacs-lisp (defhydra contrapunctus-line-display-hydra (:color red) "Line display" ("a" adaptive-wrap-prefix-mode "adaptive-prefix-wrap") ("c" visual-fill-column-mode "visual-fill-column") ("k" visual-line-mode "visual-line") ("o" org-indent-mode "org-indent-mode") ("t" toggle-truncate-lines "truncate")) #+END_SRC *** Window :PROPERTIES: :CUSTOM_ID: window :END: #+BEGIN_SRC emacs-lisp (defhydra contrapunctus-window-hydra (:color red) "Window" ("e" delete-window "delete this") ;; QWERTY d ("o" delete-other-windows "delete others") ("z" delete-window "delete this") ("v" delete-other-windows "delete others") ("t" split-window-below "split below") ("n" split-window-right "split right") ("c" enlarge-window "increase height") ("r" shrink-window "decrease height") ("h" enlarge-window-horizontally "increase width") ("s" shrink-window-horizontally "decrease width") ("b" balance-windows "balance")) #+END_SRC *** multiple cursors :PROPERTIES: :CUSTOM_ID: multiple-cursors :END: #+BEGIN_SRC emacs-lisp (defhydra contrapunctus-mc-hydra (:color red :hint none) " _a_: previous word ^^^^ _h_: previous whole symbol _,_: all words ^_i_: insert letters^ _c_: all symbols _._: words in defun ^_d_: insert numbers^ _r_: symbols in defun _u_: next word ^_l_: edit lines^ _s_: next whole symbol " ("l" mc/edit-lines) ("a" mc/mark-previous-word-like-this) ("," mc/mark-all-words-like-this) ("." mc/mark-all-words-like-this-in-defun) ("u" mc/mark-next-word-like-this) ;; ("a" mc/mark-previous-like-this-word "previous word") ;; ("u" mc/mark-next-like-this-word "next word") ("h" mc/mark-previous-symbol-like-this) ("c" mc/mark-all-symbols-like-this) ("r" mc/mark-all-symbols-like-this-in-defun) ("s" mc/mark-next-symbol-like-this) ;; ("s" mc/mark-previous-like-this-symbol "previous symbol") ;; ("h" mc/mark-next-like-this-symbol "next symbol") ("d" mc/insert-numbers) ("i" mc/insert-letters)) #+END_SRC *** info :PROPERTIES: :CUSTOM_ID: hydra-info :END: #+BEGIN_SRC emacs-lisp (defhydra contrapunctus-info-hydra (:color blue) "Info" ("i" info "info") ("a" info-apropos "apropos") ("E" (info "(emacs)") "Emacs") ("e" (info "(elisp)") "Elisp") ("c" (info "(cl)") "CL-Lib") ("l" (info "(lilypond-notation)") "Lilypond notation") ("L" (info "(lilypond-learning)") "Lilypond learning") ("o" (info "(org)") "Org") ("g" (info "(guile)") "Guile") ("t" (info "(texinfo)") "Texinfo")) #+END_SRC *** Help :PROPERTIES: :CUSTOM_ID: help :END: #+BEGIN_SRC emacs-lisp (defhydra my-help-hydra (:color blue) "Help" ("l" find-library "library") ("v" helpful-variable "variable") ("f" helpful-callable "function") ("F" describe-face "face") ("k" helpful-key "key") ("h" helpful-at-point "here") ("m" man "man page") ("c" describe-char "character") ("i" (eww-open-file "/media/data/contrapunctus/Documents/web/www.lispworks.com/documentation/lww42/CLIM-W/html/climguide.htm") "CLIM User Guide")) #+END_SRC *** Search :PROPERTIES: :CUSTOM_ID: search :END: #+BEGIN_SRC emacs-lisp (defhydra my-search-hydra (:color blue) "Search command:" ("f" find-dired "find-dired") ("g" grep "grep") ("r" rgrep "rgrep") ("a" ag "ag") ("h" find-grep "find-grep")) #+END_SRC *** General :PROPERTIES: :CUSTOM_ID: general :END: #+BEGIN_SRC emacs-lisp (my-defhydra my-general-hydra (:color blue) "What command?" ("b" (my-compile-project "Makefile") "compile") ("B" (my-compile-project "Makefile" t) "compile (prompt)") ("h" my-help-hydra/body "Help") ("E" toggle-debug-on-error "tdoe") ("Q" toggle-debug-on-quit "tdoq")) #+END_SRC *** applications :PROPERTIES: :CUSTOM_ID: hydra-applications :END: #+BEGIN_SRC emacs-lisp (defhydra my-app-hydra (:color blue) "What application?" ;; built-in ("c" chronometrist "chronometrist") ("e" contrapunctus-emms-hydra/body "emms") ("i" sxiv "sxiv") ("l" proced "list processes") ("m" mu4e "mu4e") ("v" magit-status "magit") ("p" list-packages "packages") ("j" (my-start-app-or-switch "java" nil nil "josm" "-jar" (expand-file-name "~/josm-tested.jar")) "JOSM") ;; less-used ("J" my-jabber-hydra/body "Jabber") ("E" elpher "elpher") ;; external ("g" (my-start-app-or-switch "gajim" nil "^Gajim$") "Gajim") ("b" (my-start-app-or-switch "/media/data/contrapunctus/ext/tor-browser_en-US/Browser/start-tor-browser" "^Tor Browser$") "Tor Browser") ("B" (my-start-app-or-switch "/media/data/contrapunctus/ext/firefox/firefox" "firefox") "Firefox") ("k" (my-start-app-or-switch "keepassxc") "KeePassXC") ("t" (my-start-app-or-switch "xfce4-terminal") "terminal") ("T" (my-start-app-or-switch "transmission-gtk") "bittorrent") ("s" (my-start-app-or-switch "xfce4-screenshooter") "screenshot") ("q" (my-start-app-or-switch "qtractor") "Qtractor") ("W" my-eww "eww") ("w" eww-list-bookmarks "eww bookmarks")) #+END_SRC *** org :PROPERTIES: :CUSTOM_ID: hydra-org :END: #+BEGIN_SRC emacs-lisp (defun my-compile-org-lp (&optional prefix) (interactive) (my-compile-project "Makefile" prefix)) (defhydra my-literate-elisp-hydra (:color blue) "Literate Elisp" ("l" (literate-elisp-load (buffer-file-name)) "load") ("b" (literate-elisp-byte-compile-file (buffer-file-name)) "byte-compile")) (my-defhydra my-org-hydra (:color blue) "Org" ("c" org-toggle-checkbox "checkbox") ("L" my-literate-elisp-hydra/body "literate-elisp") ("p" org-set-property "property") ("l" my-org-hydra-block/body "source block") ("t" my-org-set-tags "tags") ("C-t" org-todo "todo" :color red) ("v" my-org-hydra-nav/body "navigation") ("b" (my-compile-org-lp) "compile") ("B" (my-compile-org-lp t) "compile (prompt)") ("C-b" contrapunctus-async-tangle "babel-tangle") ("e" my-org-eval-hydra/body "eval") ("n" consult-org-heading "consult-org-heading") ("h" my-help-hydra/body "Help") ("E" toggle-debug-on-error "tdoe") ("Q" toggle-debug-on-quit "tdoq")) #+END_SRC **** eval :PROPERTIES: :CUSTOM_ID: eval :END: #+BEGIN_SRC emacs-lisp (defhydra my-org-eval-hydra (:color blue) "Eval" ("e" org-babel-execute-src-block "eval block")) #+END_SRC **** org block :PROPERTIES: :CUSTOM_ID: org-block :END: #+BEGIN_SRC emacs-lisp (defhydra my-org-hydra-block (:color blue) "Org block" ("l" (my-org-insert-block "SRC" nil) "Source (current language)") ("L" (my-org-insert-block "SRC" nil nil t) "Source (prompt)") ("t" (my-org-insert-block "SRC" nil ":tangle test :load test") "Source test") ("e" (my-org-insert-block "SRC" nil ":tangle no :load no") "Source example") ("o" (my-org-insert-block "QUOTE") "quote") ("v" (my-org-insert-block "VERSE") "verse") ("x" (my-org-insert-block "EXPORT" nil nil t) "Export")) #+END_SRC **** org navigation :PROPERTIES: :CUSTOM_ID: org-navigation :END: #+BEGIN_SRC emacs-lisp (defhydra my-org-hydra-nav (:color red) "Navigation" ;; movement ("c" org-previous-visible-heading "previous heading") ("r" org-next-visible-heading "next heading") ("h" outline-up-heading "up heading") ("t" org-backward-heading-same-level "backward heading") ("n" org-forward-heading-same-level "forward heading") ;; folding ("k" outline-show-branches "branches") ("" org-cycle "cycle") ("" org-global-cycle "global cycle") ;; modification ("C" org-metaup "drag backward") ("R" org-metadown "drag forward") ("H" org-metaleft "promote") ("S" org-metaright "demote")) #+END_SRC *** jabber :PROPERTIES: :CUSTOM_ID: hydra-jabber :END: #+BEGIN_SRC emacs-lisp (my-defhydra my-jabber-hydra (:color blue) "Jabber" ("c" jabber-connect "connect") ("r" jabber-display-roster "roster") ("n" jabber-activity-switch-to "next" :color red) ("E" toggle-debug-on-error "tdoe") ("Q" toggle-debug-on-quit "tdoq")) #+END_SRC *** Emacs Lisp :PROPERTIES: :CUSTOM_ID: hydra-emacs-lisp :END: #+BEGIN_SRC emacs-lisp (my-defhydra my-elisp-hydra (:color blue) "Emacs Lisp" ("c" my-elisp-bytecomp-hydra/body "byte compile") ("b" my-compile-org-lp "compile") ("B" (my-compile-org-lp t) "compile (prompt)") ("C-b" contrapunctus-async-tangle "babel-tangle") ("e" my-elisp-hydra-eval/body "Eval") ("E" my-elisp-hydra-debug/body "Debug") ("h" my-help-hydra/body "Help") ("j" xref-find-definitions "Jump to definition") ("J" org-babel-tangle-jump-to-org "Jump to definition (Org)") ("r" ielm "REPL") ("t" contrapunctus-el-test/body "Test") ("L" my-literate-elisp-hydra/body "literate-elisp") ("n" imenu "imenu")) #+END_SRC **** eval :PROPERTIES: :CUSTOM_ID: eval-1 :END: #+BEGIN_SRC emacs-lisp (defhydra my-elisp-hydra-eval (:color blue) ("b" eval-buffer "buffer") ("e" eval-defun "defun") ("l" eval-last-sexp "last sexp")) #+END_SRC **** unit testing :PROPERTIES: :CUSTOM_ID: unit-testing :END: #+BEGIN_SRC emacs-lisp (defhydra contrapunctus-el-test (:color blue) ("e" (my-compile-project "Cask" "cask exec buttercup -L . --traceback pretty") "buttercup") ("r" ert "ert")) #+END_SRC **** debug :PROPERTIES: :CUSTOM_ID: debug :END: #+BEGIN_SRC emacs-lisp (defhydra my-elisp-hydra-debug (:color blue) "Debug" ("e" (funcall-interactively #'eval-defun t) "edebug") ("o" toggle-debug-on-error "tdoe") ("u" toggle-debug-on-quit "tdoq")) #+END_SRC **** byte-compile :PROPERTIES: :CUSTOM_ID: byte-compile :END: #+BEGIN_SRC emacs-lisp (defhydra my-elisp-bytecomp-hydra (:color blue) "Byte compile" ("d" byte-recompile-directory "directory") ("c" (byte-compile-file (buffer-file-name)) "file") ("r" (byte-recompile-file (buffer-file-name)) "recompile") ("R" byte-force-recompile "force recompile")) #+END_SRC **** insert :PROPERTIES: :CREATED: 2022-01-05T19:45:33+0530 :CUSTOM_ID: insert :END: #+BEGIN_SRC emacs-lisp (defhydra my-elisp-insert-hydra (:color blue) "Insert" ("g" (yas-expand-snippet (yas-lookup-snippet "cl-defgeneric")) "defgeneric") ("m" (yas-expand-snippet (yas-lookup-snippet "cl-defmethod")) "defmethod")) #+END_SRC *** Common Lisp :PROPERTIES: :CUSTOM_ID: hydra-common-lisp :END: #+BEGIN_SRC emacs-lisp (my-defhydra my-common-lisp-hydra (:color blue) "Common Lisp" ("R" slime-connect "connect") ("h" my-common-lisp-help-hydra/body "Documentation") ("e" my-common-lisp-hydra-eval/body "Eval") ("E" toggle-debug-on-error "tdoe") ("Q" toggle-debug-on-quit "tdoq") ("r" slime "REPL") ("j" slime-edit-definition "Jump to definition") ("p" slime-repl-set-package "Set package") ("n" imenus "imenus") ("v" slime-inspect-presentation-at-point "inspect") ("t" my-run-cl-tests "test")) #+END_SRC **** eval :PROPERTIES: :CUSTOM_ID: eval-2 :END: #+BEGIN_SRC emacs-lisp (defhydra my-common-lisp-hydra-eval (:color blue) "Eval (CL)" ("b" slime-eval-buffer "buffer") ("e" slime-eval-defun "defun") ("r" slime-eval-region "region") ("l" slime-eval-last-expression "last expression") ("m" slime-macroexpand-1 "macroexpand")) #+END_SRC **** help :PROPERTIES: :CUSTOM_ID: help-1 :END: #+BEGIN_SRC emacs-lisp (defhydra my-common-lisp-help-hydra (:color blue) ("s" slime-documentation "slime") ("h" slime-documentation-lookup "CLHS") ("l" find-library "library") ("v" helpful-variable "variable") ("f" helpful-function "function") ("k" helpful-key "key") ("m" man "man page")) #+END_SRC *** Scheme :PROPERTIES: :CUSTOM_ID: hydra-scheme :END: **** eval :PROPERTIES: :CUSTOM_ID: hydra-scheme-eval :END: #+BEGIN_SRC emacs-lisp (defhydra cp-scm-eval (:color blue) ("b" geiser-eval-buffer "buffer") ("e" geiser-eval-definition "defun") ("l" geiser-eval-last-sexp "last sexp") ("E" toggle-debug-on-error "tdoe") ("Q" toggle-debug-on-quit "tdoq")) #+END_SRC **** CHICKEN Scheme :PROPERTIES: :CUSTOM_ID: hydra-chicken-scheme :END: #+BEGIN_SRC emacs-lisp (my-defhydra my-chicken-hydra (:color blue) "CHICKEN Scheme" ("e" cp-scm-eval/body "Eval") ("r" run-chicken "REPL") ("h" my-help-hydra/body "Help") ("E" toggle-debug-on-error "tdoe") ("Q" toggle-debug-on-quit "tdoq")) #+END_SRC **** Guile :PROPERTIES: :CUSTOM_ID: hydra-guile :END: #+BEGIN_SRC emacs-lisp (my-defhydra cp-guile (:color blue) "Guile" ("e" cp-scm-eval/body "Eval") ("r" run-guile "REPL") ("h" my-help-hydra/body "Help") ("E" toggle-debug-on-error "tdoe") ("Q" toggle-debug-on-quit "tdoq")) #+END_SRC *** Lilypond :PROPERTIES: :CUSTOM_ID: hydra-lilypond :END: #+BEGIN_SRC emacs-lisp (defun my-compile-ly (&optional prefix) (interactive) (my-compile-project "main.ly" prefix "./mkly dev")) (my-defhydra my-lilypond-hydra (:color blue) "Lilypond" ("b" my-compile-ly "Compile") ("B" (my-compile-ly t) "Compile") ("h" my-help-hydra/body "Help") ("E" toggle-debug-on-error "tdoe") ("Q" toggle-debug-on-quit "tdoq")) #+END_SRC *** Prolog :PROPERTIES: :CUSTOM_ID: hydra-prolog :END: #+BEGIN_SRC emacs-lisp (my-defhydra my-prolog-hydra (:color blue) "Prolog" ("r" ediprolog-dwim "REPL") ("E" toggle-debug-on-error "tdoe") ("Q" toggle-debug-on-quit "tdoq")) #+END_SRC *** my-dispatch-hydra :PROPERTIES: :CUSTOM_ID: my-dispatch-hydra :END: Don't try to check if there are files with a certain extension...it will lead to false positives. #+BEGIN_SRC emacs-lisp (defun my-dispatch-hydra () (interactive) (cond ((derived-mode-p 'emacs-lisp-mode 'inferior-emacs-lisp-mode 'debugger-mode) (my-elisp-hydra/body)) (;; (or (locate-dominating-file default-directory "build.scm") ;; (locate-dominating-file default-directory "main.ly")) (derived-mode-p 'LilyPond-mode) (my-lilypond-hydra/body)) ((and (featurep 'geiser) (bound-and-true-p geiser-mode)) (if (eq 'guile geiser-scheme-implementation) (cp-guile/body) (my-chicken-hydra/body))) ((derived-mode-p 'lisp-mode 'slime-repl-mode) (my-common-lisp-hydra/body)) ((derived-mode-p 'prolog-mode) (my-prolog-hydra/body)) ((derived-mode-p 'org-mode) (my-org-hydra/body)) ((derived-mode-p 'sh-mode) (my-shell-hydra/body)) ((derived-mode-p 'sql-mode) (my-sql-hydra/body)) ((derived-mode-p 'comint-mode) (my-comint-hydra/body)) ((derived-mode-p 'tex-mode) (my-tex-hydra/body)) ((derived-mode-p 'python-mode) (my-python-hydra/body)) (t (my-general-hydra/body)))) (use-package boon :bind (:map boon-command-map ("m" . my-dispatch-hydra) ("C-s" . my-dispatch-hydra))) (global-set-key (kbd "C-s") #'my-dispatch-hydra) #+END_SRC *** shell script :PROPERTIES: :CUSTOM_ID: shell-script :END: #+BEGIN_SRC emacs-lisp (my-defhydra my-shell-hydra (:color blue) "Shell" ("e" my-shell-eval-hydra/body "eval") ("h" my-help-hydra/body "Help")) (defhydra my-shell-eval-hydra (:color blue) "Evaluate" ("b" org-babel-execute-src-block "source block")) #+END_SRC *** SQL :PROPERTIES: :CUSTOM_ID: hydra-sql :END: #+BEGIN_SRC emacs-lisp (my-defhydra my-sql-hydra (:color blue) "SQL" ("r" sql-sqlite "REPL") ("e" my-sql-eval-hydra/body "eval") ("h" my-help-hydra/body "Help")) (defhydra my-sql-eval-hydra (:color blue) "Eval" ("e" sql-send-paragraph "paragraph") ("b" sql-send-buffer "buffer")) #+END_SRC *** comint :PROPERTIES: :CUSTOM_ID: hydra-comint :END: #+BEGIN_SRC emacs-lisp (my-defhydra my-comint-hydra (:color blue) "comint" ("v" my-comint-nav-hydra/body "Navigation") ("h" my-help-hydra/body "Help")) (defhydra my-comint-nav-hydra (:color red) "Navigation" ("c" comint-previous-prompt "previous") ("r" comint-next-prompt "next")) #+END_SRC *** LaTeX :PROPERTIES: :CUSTOM_ID: hydra-latex :END: #+BEGIN_SRC emacs-lisp (my-defhydra my-tex-hydra (:color blue) ("j" xref-find-definitions "Jump to definition") ("h" my-help-hydra/body "Help") ("b" (my-compile-project "Makefile") "compile") ("B" (my-compile-project "Makefile" t) "compile (prompt)")) #+END_SRC *** Python :PROPERTIES: :CUSTOM_ID: python :END: #+BEGIN_SRC emacs-lisp (my-defhydra my-python-hydra (:color blue) ("r" run-python "REPL")) #+END_SRC ** marginalia :PROPERTIES: :CREATED: 2022-01-16T00:28:51+0530 :CUSTOM_ID: marginalia :END: #+BEGIN_SRC emacs-lisp (use-package marginalia :ensure t :init (marginalia-mode)) #+END_SRC ** mode line :PROPERTIES: :CUSTOM_ID: mode-line :END: *** moody :PROPERTIES: :CUSTOM_ID: moody :END: nicked from https://github.com/tarsius/moody #+BEGIN_SRC emacs-lisp ;; (use-package solarized-theme ;; :config ;; (load-theme 'solarized-light t) ;; (let ((line (face-attribute 'mode-line :underline))) ;; (set-face-attribute 'mode-line nil :overline line) ;; (set-face-attribute 'mode-line-inactive nil :overline line) ;; (set-face-attribute 'mode-line-inactive nil :underline line) ;; (set-face-attribute 'mode-line nil :box nil) ;; (set-face-attribute 'mode-line-inactive nil :box nil) ;; (set-face-attribute 'mode-line-inactive nil :background "#f9f2d9"))) (use-package moody :disabled t :ensure t :config (setq x-underline-at-descent-line t) (moody-replace-mode-line-buffer-identification) (moody-replace-vc-mode)) #+END_SRC *** powerline :disabled: :PROPERTIES: :CUSTOM_ID: powerline :END: #+BEGIN_SRC emacs-lisp (use-package powerline :disabled t :ensure t) #+END_SRC *** doom-modeline :PROPERTIES: :CUSTOM_ID: doom-modeline :END: #+BEGIN_SRC emacs-lisp (use-package doom-modeline :ensure t :init (doom-modeline-mode)) #+END_SRC * Searching :PROPERTIES: :CUSTOM_ID: searching :END: ** isearch :PROPERTIES: :CUSTOM_ID: isearch :END: #+BEGIN_SRC emacs-lisp (use-package isearch :bind (:map isearch-mode-map ("C-c" . #'isearch-repeat-backward) ("C-r" . #'isearch-repeat-forward) ("M-c" . #'isearch-ring-retreat) ("M-r" . #'isearch-ring-advance))) #+END_SRC ** ag, the Silver Searcher :PROPERTIES: :CUSTOM_ID: ag :END: #+BEGIN_SRC emacs-lisp (use-package ag :ensure t :bind (" p" . ag) (" P" . ag-project-regexp) :config (setq ag-highlight-search t)) #+END_SRC ** Swiper :PROPERTIES: :CUSTOM_ID: swiper :END: #+BEGIN_SRC emacs-lisp (use-package swiper :disabled t :ensure t :bind (("C-s" . swiper) ("C-r" . swiper-backward)) (:map swiper-map ("C-c" . previous-line) ("C-r" . next-line)) :config (setq swiper-action-recenter t)) #+END_SRC ** wgrep :PROPERTIES: :CREATED: 2022-01-14T16:23:41+0530 :CUSTOM_ID: wgrep :END: #+BEGIN_SRC emacs-lisp (use-package wgrep :commands (wgrep-change-to-wgrep-mode)) #+END_SRC ** Recenter screen on isearch matches :PROPERTIES: :CUSTOM_ID: recenter-screen-on-isearch-matches :END: #+BEGIN_SRC emacs-lisp (add-hook 'isearch-mode-hook 'recenter) (add-hook 'isearch-update-post-hook 'recenter) (defadvice isearch-repeat-forward (after isearch-repeat-forward-recenter activate) (recenter)) (defadvice isearch-repeat-backward (after isearch-repeat-backward-recenter activate) (recenter)) (ad-activate 'isearch-repeat-forward) (ad-activate 'isearch-repeat-backward) (global-set-key (kbd "C-s") 'isearch-forward-regexp) (global-set-key (kbd "C-r") 'isearch-backward-regexp) #+END_SRC ** #+BEGIN_SRC emacs-lisp ;; (if (not (server-running-p)) (server-start)) (server-start) #+END_SRC ** grep :PROPERTIES: :CUSTOM_ID: grep :END: #+BEGIN_SRC emacs-lisp (use-package grep :bind (:map grep-mode-map ("c" . #'previous-error-no-select) ("r" . #'next-error-no-select))) #+END_SRC ** occur :PROPERTIES: :CUSTOM_ID: occur :END: #+BEGIN_SRC emacs-lisp (use-package replace :bind (:map occur-mode-map ("c" . #'occur-prev) ("r" . #'occur-next))) #+END_SRC * Editing :PROPERTIES: :CREATED: 2022-01-13T20:48:58+0530 :CUSTOM_ID: editing :END: #+BEGIN_SRC emacs-lisp (setq sentence-end-double-space nil) (general-define-key "C-h" 'backward-delete-char "C-w" 'backward-kill-word "C-u" 'cp-kill-line-0 "C-<" 'beginning-of-buffer "C->" 'end-of-buffer) (define-key isearch-mode-map (kbd "C-h") 'isearch-delete-char) (defun cp-kill-line-0 () (interactive) (kill-line 0)) (global-unset-key (kbd "C-x C-r")) (general-define-key :prefix "C-x C-r" "C-i" 'string-insert-rectangle "C-r" 'replace-rectangle "C-k" 'kill-rectangle) (defun cp/downcase-dwim (arg) "Like `downcase-word', but if region is active, run `downcase-region' instead. Unlike `downcase-region', rectangular regions are handled correctly as well.") ;; open-line should always move to the beginning of the current line ;; first, so one can run it anywhere. I also want it to indent it to ;; the next or the previous line... ;; These affect org-meta-return and org-insert-heading-respect-content ;; ;; (defadvice open-line ;; (before open-line-bol activate) ;; (beginning-of-visual-line)) ;; (defadvice open-line ;; (after open-line-indent activate) ;; (if (not (looking-at "\\** ")) ;; (indent-for-tab-command))) ;; (global-set-key (kbd "C-o") 'open-line) (defun cp/open-line () "The opposite of `cp/open-line-before'. Start a new line below the current line, regardless of where point is in the current line. Will not affect the content of the current line. Applies the correct indentation according to the mode." (interactive) (progn (end-of-line) (electric-indent-just-newline t) (indent-according-to-mode))) (defun cp/open-line-before (arg) "Like `open-line' but a bit more sane. In org-mode, run org-open-line. In other modes, insert a newline above the current line, regardless of where point is in current line. Will not affect content of current line. Applies the correct indentation according to the mode." (interactive "p") (if (and (equal major-mode 'org-mode) (org-at-table-p)) (org-open-line arg) (progn (beginning-of-line) (electric-indent-just-newline t) (forward-char -1) (indent-according-to-mode)))) (global-set-key (kbd "C-o") 'cp/open-line-before) ;; I wish you could press e.g. S-o in god-mode to get C-S-o. ;; 10/10 would make life better. (global-set-key (kbd "C-S-o") 'cp/open-line) (setq dired-bind-jump nil) (global-set-key (kbd "C-x C-j") 'join-line) ;; C-z (suspend-frame) is utterly useless and disruptive - good place to put universal-argument (global-unset-key (kbd "C-z")) (global-set-key (kbd "C-z") 'universal-argument) (global-set-key (kbd "C-x C-z") 'repeat) (global-set-key (kbd "C-x C-;") 'repeat-complex-command) ;; (defun cp-kill-line (&optional arg) ;; (interactive) ;; (if arg ;; (if mark-active ;; (kill-region arg) ;; (kill-line arg)) ;; (if mark-active ;; (kill-region) ;; (kill-line)))) ;; (global-set-key (kbd "C-k") 'cp-kill-line) ;; Chris Done (god-mode) recommends this, but Emacs' repeat command is almost useless... ;; (global-set-key (kbd "C-.") 'repeat) (defun my-delete-trailing-whitespace () (unless (derived-mode-p 'markdown-mode) (delete-trailing-whitespace))) (add-hook 'before-save-hook 'my-delete-trailing-whitespace) ;; BUG - (dired-get-marked-files) returns file name at point if none are marked ;; BUG - (dired-get-marked-files) always returns an alphabetically-sorted list, even when the Dired buffer is sorted by date (defun cp/marked-files->markup-links () "From a Dired buffer, insert the file at point or marked files as links into an Org or Markdown document." (interactive) (if (derived-mode-p 'dired-mode) (let* ((filenames (dired-get-marked-files 'no-dir)) (other-buffer (->> (window-list) (cadr) (window-buffer))) (other-buffer-mode (with-current-buffer other-buffer major-mode)) (two-window-op (and (= (length (window-list)) 2) (member other-buffer-mode '(org-mode markdown-mode)))) (output-buffer (if two-window-op other-buffer (read-buffer "Insert links in buffer: "))) (output-mode (with-current-buffer output-buffer major-mode)) (output-text (cl-case output-mode ('org-mode (cp/marked-files->markup-links-org filenames)) ('markdown-mode (cp/marked-files->markup-links-md filenames)) (t (error "Only Markdown and Org are currently supported."))))) (if two-window-op (other-window 1) (switch-to-buffer output-buffer)) (mapc #'insert output-text)) (user-error "Run this command from a Dired buffer with some marked files.")) (when markdown-inline-image-overlays (markdown-display-inline-images))) #+END_SRC #+BEGIN_SRC emacs-lisp (defun cp-insert-timestamp (&optional date-only-p) "Insert current date and time in ISO-8601 format in the current buffer." (interactive) (let ((timestamp (format-time-string (if date-only-p "%F" "%FT%T%z")))) (cond ((derived-mode-p 'emacs-lisp-mode) (insert "\"" timestamp "\"")) (t (insert timestamp))))) #+END_SRC ** text-mode :PROPERTIES: :CREATED: 2022-01-16T14:02:56+0530 :CUSTOM_ID: text-mode :END: #+BEGIN_SRC emacs-lisp (use-package text-mode :bind (:map text-mode-map ("M-c" . 'org-drag-line-backward) ("M-r" . 'org-drag-line-forward))) #+END_SRC ** undo-tree :PROPERTIES: :CUSTOM_ID: undo-tree :END: #+BEGIN_SRC emacs-lisp (use-package undo-tree :ensure t :commands global-undo-tree-mode :if (not (featurep 'evil)) :diminish undo-tree-mode :init (global-undo-tree-mode) :hook (wdired-mode . undo-tree-mode) :config (setq undo-tree-auto-save-history nil)) #+END_SRC ** ediff :PROPERTIES: :CUSTOM_ID: ediff :END: #+BEGIN_SRC emacs-lisp (use-package ediff :config (setq ediff-window-setup-function 'ediff-setup-windows-plain) ;; boon-like bindings (add-hook 'ediff-keymap-setup-hook (lambda () (define-key ediff-mode-map (kbd "c") 'ediff-previous-difference) (define-key ediff-mode-map (kbd "r") 'ediff-next-difference)))) #+END_SRC ** iedit :PROPERTIES: :CREATED: 2022-01-13T20:51:14+0530 :CUSTOM_ID: iedit :END: #+BEGIN_SRC emacs-lisp (use-package iedit :ensure t :bind (("C-;" . iedit-mode) ("C-:" . iedit-mode-toggle-on-function) (:map iedit-mode-keymap ("C-h" . backward-delete-char)))) #+END_SRC ** multiple cursors :PROPERTIES: :CREATED: 2022-01-13T20:52:14+0530 :CUSTOM_ID: multiple-cursors-1 :END: #+BEGIN_SRC emacs-lisp (use-package multiple-cursors :ensure t :bind (("C-M-l" . #'mc/mark-previous-like-this-symbol) ("C-M-;" . #'mc/mark-next-like-this-symbol) ("C-M-'" . #'mc/mark-all-symbols-like-this) ("C-M-," . #'mc/mark-previous-word-like-this) ("C-M-." . #'mc/mark-next-word-like-this) ("C-M-/" . #'mc/mark-all-words-like-this) ("C-M-<" . #'mc/unmark-previous-like-this) ("C-M->" . #'mc/unmark-next-like-this) ("C-M-]" . #'mc-hide-unmatched-lines-mode))) #+END_SRC ** transpose commands :PROPERTIES: :CREATED: 2022-01-13T20:50:03+0530 :CUSTOM_ID: transpose-commands :END: #+BEGIN_SRC emacs-lisp (global-unset-key (kbd "C-t")) (general-define-key :prefix "C-t" "C-t C-c" 'transpose-chars "C-t C-w" 'transpose-words "C-t C-l" 'transpose-lines "C-t C-s" 'transpose-sentences "C-t C-e" 'transpose-sexps "C-t C-p" 'transpose-paragraphs) #+END_SRC ** easy-kill :disabled: :PROPERTIES: :CUSTOM_ID: easy-kill :END: #+BEGIN_SRC emacs-lisp (use-package easy-kill :disabled :bind (("M-w" . easy-kill) ("M-d" . easy-kill-delete-region))) #+END_SRC ** atomic-chrome :PROPERTIES: :CUSTOM_ID: atomic-chrome :END: Doesn't work in my current main browser (Tor Browser), and thus barely used of late... #+BEGIN_SRC emacs-lisp (use-package atomic-chrome :ensure t :init (atomic-chrome-start-server) :config (setq atomic-chrome-url-major-mode-alist '(("wikisource" . mediawiki-mode)))) #+END_SRC ** Unicode keys :PROPERTIES: :CUSTOM_ID: unicode-keys :END: #+BEGIN_SRC emacs-lisp (general-auto-unbind-keys) (general-def :prefix "C-t" ;; currency "r" [?₹] "- L" [?£] "L -" [?£] "e" [?€] "." [?…] "- m" [?—] "- n" [?–] "m -" [?—] "n -" [?–] "t" [?™] ;; German "\" a" [?ä] "a \"" [?ä] "\" A" [?Ä] "A \"" [?Ä] "\" e" [?ë] "e \"" [?ë] "\" E" [?Ë] "E \"" [?Ë] "\" o" [?ö] "o \"" [?ö] "\" O" [?Ö] "O \"" [?Ö] "\" u" [?ü] "u \"" [?ü] "\" U" [?Ü] "U \"" [?Ü] "\" i" [?ï] "i \"" [?ï] "\" I" [?Ï] "I \"" [?Ï] ;; ;; " a/e/o/u are slow to type... ;; "a" [?ä] "A" [?Ä] ;; "e" [?ë] "E" [?Ë] ;; "o" [?ö] "O" [?Ö] ;; "u" [?ü] "U" [?Ü] ;; but "o" conflicts with "o o" for °, so... "u a" [?ä] "u A" [?Ä] "u e" [?ë] "u E" [?Ë] "u o" [?ö] "u O" [?Ö] "u u" [?ü] "u U" [?Ü] "s s" [?ß] ;; French ", c" [?ç] "c ," [?ç] ", C" [?ç] "C ," [?Ç] "' e" [?é] "e '" [?é] "' E" [?É] "E '" [?É] "e `" [?è] "` e" [?è] "E `" [?È] "` E" [?È] "e ^" [?ê] "^ e" [?ê] "E `" [?È] "` E" [?È] "' a" [?á] "a '" [?á] "' A" [?Á] "A '" [?Á] "a `" [?à] "` a" [?à] "A `" [?À] "` A" [?À] "a ^" [?â] "^ a" [?â] "A ^" [?Â] "^ A" [?Â] "~ n" [?ñ] "n ~" [?ñ] "~ N" [?Ñ] "N ~" [?Ñ] "e e" [?ə] "o e" [?œ] "O E" [?Œ] "o o" [?°] "x" [?×] "1 2" [?½] "1 3" [?⅓] "1 4" [?¼] "2 3" [?⅔] "2 5" [?⅖] "3 4" [?¾] "3 5" [?⅗] "4 5" [?⅘] "b" [?♭] "#" [?♯] "< -" [?←] "- >" [?→] "^ |" [?↑] "| ^" [?↑] "v |" [?↓] "| v" [?↓] "l" [?λ] "f" [?ƒ] "d q o" [?“] "d q c" [?”] "s q o" [?‘] "s q c" [?’]) (general-auto-unbind-keys t) (setq default-input-method "devanagari-itrans") #+END_SRC Suggestion by lampilelo for extending =iso-transl-ctl-x-8-map= (https://dpaste.com/BAQUXSDVL.txt) #+BEGIN_SRC emacs-lisp :tangle no (eval-after-load 'iso-transl '(let ((map (make-sparse-keymap))) (define-key map (kbd "c") [?č]) (define-key map (kbd "C") [?Č]) (define-key iso-transl-ctl-x-8-map (kbd "v") map))) #+END_SRC ** modal editing :PROPERTIES: :CUSTOM_ID: modal-editing :END: *** active boon config :PROPERTIES: :CUSTOM_ID: active-boon-config :END: #+BEGIN_SRC emacs-lisp (use-package boon :ensure t :commands (boon-mode) :quelpa (boon :fetcher github :repo "jyp/boon") :bind (:map boon-command-map ("C-l" . recenter-top-bottom) ("p" . consult-line) (". p" . my-consult-line) (". c" . #'isearch-backward-regexp) (". r" . #'isearch-forward-regexp) (", r" . #'iy-go-up-to-char) (", c" . #'iy-go-up-to-char-backward) ("I" . join-line) ;; QWERTY G ("U" . #'consult-yank-pop) ;; ("TAB" . 'company-indent-or-complete-common) ;; this works, ;; but also breaks unfolding in org mode :\ ("" . ;; 'company-indent-or-complete-common) ("J" . 'boon-toggle-comment) ("y" . nil) ;; QWERTY t ("y w" . 'transpose-words) ("y e" . 'transpose-sexps) ("y l" . 'transpose-lines) ("y p" . 'transpose-paragraphs) ("y c" . 'transpose-chars) ;; ("\\" . projectile-command-map) ;; error - define this in projectile's `use-package' ;; are these being buggy? ;; ("(" . boon-navigate-backward) ;; (")" . boon-navigate-forward) ("M" . consult-buffer) ("H" . consult-buffer) ;; these I prefer in their Dvorak positions rather than their QWERTY positions ("/" . undo-tree-undo) ("?" . undo-tree-redo) ("z" . boon-repeat-command) ("DEL" . #'my-buffer-switch) ("RET" . #'consult-buffer)) (:map boon-x-map ("," . 'write-file) ("o" . 'save-buffer) ("e" . 'dired-jump) ("." . 'eval-last-sexp) ("u" . 'find-file) ("j" . 'kill-emacs) ("x" . 'ibuffer) (")" . text-scale-adjust) ("(" . text-scale-adjust)) :config (require 'boon-dvorak) ;; (add-to-list 'boon-enclosures `(40 "(" ")")) ;; 40 = ( ;; (use-package boon-powerline) ;; (boon-powerline-theme) (mapc (lambda (mode) (add-to-list 'boon-special-mode-list mode)) '(emms-playlist-mode emms-browser-mode edebug-mode sldb-mode macrostep-mode view-mode slime-popup-buffer-mode finder-mode inferior-emacs-lisp-mode shell-mode)) (add-to-list 'boon-special-conditions '(or (bound-and-true-p edebug-mode) (bound-and-true-p view-mode) (bound-and-true-p macrostep-mode) (bound-and-true-p slime-popup-buffer-mode) (derived-mode-p 'comint-mode))) (setq hi-lock-auto-select-face t) ;; (define-key boon-command-map (kbd "x d f") nil) ;; :hook ;; ((text-mode . turn-on-boon-mode) ;; (prog-mode . turn-on-boon-mode) ;; (shell-mode . turn-on-boon-mode) ;; (helpful-mode . turn-on-boon-mode) ;; (fundamental-mode . turn-on-boon-mode)) :init (boon-mode)) #+END_SRC *** experimental boon+modalka config :disabled: :PROPERTIES: :CUSTOM_ID: experimental-boon+modalka-config :END: #+BEGIN_SRC emacs-lisp :load no (use-package boon :disabled :bind ("C-d" . 'boon-take-region) ("C-a" . 'boon-beginning-of-line) ("C-e" . 'boon-end-of-line) ("M-f" . 'boon-smarter-forward) ("M-b" . 'boon-smarter-backward)) #+END_SRC *** experimental Emacs-flavored-Boon config :disabled: :PROPERTIES: :CUSTOM_ID: experimental-emacs-flavored-boon-config :END: #+BEGIN_SRC emacs-lisp :load no (use-package boon :disabled :bind (:map boon-command-map ("n" . next-line) ("p" . previous-line) ("f" . 'forward-char) ("b" . 'backward-char) ("a" . 'boon-beginning-of-line) ("e" . 'boon-end-of-line) ("y" . 'boon-splice) ("r" . swiper) ("x l" . ido-mini) ("/" . undo-tree-undo) ("?" . undo-tree-redo)) (:map boon-x-map ("s" . #'save-buffer) ("d" . #'dired-jump)) :config (use-package boon-qwerty) (boon-mode)) #+END_SRC *** modalka :disabled: :PROPERTIES: :CUSTOM_ID: modalka :END: <2019-11-03> I'm pretty much using this to emulate `god-mode', which was great, but is no longer actively developed and had no support for non-Latin input methods. #+BEGIN_SRC emacs-lisp :load no (use-package modalka :disabled :bind (("" . #'modalka-mode) :map modalka-mode-map ("J" . #'join-line) ("P" . #'backward-paragraph) ("N" . #'forward-paragraph) ("M" . mark-sexp)) :config (let ((keybind-re (rx-to-string '(group-n 1 (or (and bow (1+ (char graph)) eow) (and (1+ (char graph)))))))) (mapc (lambda (actual) (let ((target (replace-regexp-in-string keybind-re "C-\\1" actual))) (modalka-define-kbd actual target))) ;; no "t", "x", or "c", because they are prefix keys used later '("`" "1" "2" "3" "4" "5" "6" "7" "8" "9" "0" "-" "=" "q" "w" "e" "r" "y" "u" "i" "o" "p" "[" "]" "\\" "a" "s" "d" "f" "h" "j" "k" "l" ";" "'" "z" "v" "b" "n" "m" "," "." "/" "<" ">" "?" "SPC" ;; (2019-11-03) It's a little unfortunate that these cannot be ;; elided by entering ("x" "C-x") ("t" "C-t") ("c" "C-c") just ;; once :\ (I did try) "x =" "x -" "x e" "x s" "x d" "x f" "x l" "x x" "x c" "x v" "x b" "t w" "t e" "t l" "c n" "c ," "c ." "c p" "c c n" "c h u" "c h m"))) (modalka-define-kbd "O" "C-S-o") (setq-default cursor-type '(bar . 1)) (setq modalka-cursor-type 'box) :hook ((text-mode . modalka-mode) (prog-mode . modalka-mode))) #+END_SRC *** god mode :disabled: :PROPERTIES: :CREATED: 2022-01-11T12:54:58+0530 :CUSTOM_ID: god-mode :END: TODO - make mode-line indicator prominent by placing it first, like in Evil #+BEGIN_SRC emacs-lisp (use-package god-mode :disabled t :bind ("" . god-mode-all) :init (god-mode-all) :config ;; from https://github.com/chrisdone/god-mode#change-modeline-color (defun c/god-mode-update-cursor () (cond (god-local-mode (progn (setq cursor-type 'box) (set-face-background 'mode-line "black") (set-face-background 'mode-line-inactive "black"))) (t (progn (setq cursor-type 'bar) (set-face-background 'mode-line "dark red") (set-face-background 'mode-line-inactive "dark red"))))) (add-hook 'god-mode-enabled-hook #'c/god-mode-update-cursor) (add-hook 'god-mode-disabled-hook #'c/god-mode-update-cursor) :custom (god-exempt-major-modes nil) (god-exempt-predicates nil)) ;; for helm ;; (define-key helm-map (kbd "") 'god-local-mode) (use-package god-mode-isearch :disabled t :bind ((:map isearch-mode-map ("" . god-mode-isearch-activate)) (:map god-mode-isearch-map ("" . god-mode-isearch-disable)))) #+END_SRC *** Evil mode :PROPERTIES: :CUSTOM_ID: evil-mode :END: #+BEGIN_SRC emacs-lisp (use-package evil :disabled :init (evil-mode 1) ;; Want something to switch between evil and god-mode, instead of this. ;; They say there's a plugin for it... :bind (("C-x e" evil-mode) ;;(global-set-key (kbd "g C-g") 'count-words-region) (:map evil-normal-state-map (;; ("" forward-button) ("zR" . 'yafolding-show-all) ("zM" . 'yafolding-hide-all) ("zo" . 'yafolding-show-element) ("zc" . 'yafolding-hide-element) ;; The story so far - in evil-normal-state-map, I set 'i' to ;; evil-previous-visual-line, 'k' to evil-next-visual-line, ;; 'j' to evil-backward-char. Note that I forgot to set 'h' ;; to insert at first...but that's not all - later I realized ;; this didn't affect Visual mode :p 4 more lines for that. ;; Next...d/c didn't work with ijkl. 4 more lines for ;; evil-operator-state-map. After that - you usually want ;; visual-line up/down movement, but _not_ when you hit d-j/k ;; (or d-i/k)! So, in evil-operator-state-map, i and k are ;; now vanilla evil-previous/next-line. Set gi/gk to ;; evil-previous/next-line in normal and visual states, and ;; to evil-previous/next-visual-line in operator state - 2 ;; new lines to each group of 4. ("gi" . evil-previous-visual-line) ("gk" . evil-next-visual-line) ( "i" . evil-previous-line) ( "k" . evil-next-line) ( "j" . evil-backward-char) ( "h" . evil-insert))) :map evil-visual-state-map ("gi" evil-previous-visual-line) ("gk" evil-next-visual-line) ("i" evil-previous-line) ("k" evil-next-line) ("j" evil-backward-char) ("h" evil-insert) :map evil-operator-state-map ("gi" evil-previous-visual-line) ("gk" evil-next-visual-line) ("i" evil-previous-line) ("k" evil-next-line) ("j" evil-backward-char) ("h" evil-insert) ;; evil-inner-tag ? ("hW" evil-inner-WORD) ("hw" evil-inner-word) ("h\(" evil-inner-paren) ("h\)" evil-inner-paren) ("h\{" evil-inner-curly) ("h\}" evil-inner-curly) ("h<" evil-inner-angle) ("h>" evil-inner-angle) ("h\[" evil-inner-bracket) ("h\]" evil-inner-bracket) ("hs" evil-inner-sentence) ("hp" evil-inner-paragraph) ("h`" evil-inner-back-quote) ("h\"" evil-inner-double-quote) ("h'" evil-inner-single-quote) :map evil-motion-state-map ("gi" evil-previous-visual-line) ("gk" evil-next-visual-line)) ;;;; set C-h to backspace when editing commands and searching (defun kill-start-of-line () "Kill from point to start of line." (interactive) (kill-line 0)) (define-key evil-insert-state-map (kbd "C-u") 'kill-start-of-line) ;;;; evil-snipe (with-eval-after-load 'evil-snipe (setq evil-snipe-count-scope 'letters) ;; This doesn't take two chars by default, which combined with ;; unimplemented evil-snipe-count-scope 'vertical makes it nearly ;; useless as a replacement for fFtT; (evil-snipe-replace-evil) (global-evil-snipe-mode 1))) #+END_SRC * Applications :PROPERTIES: :CUSTOM_ID: applications :END: Things not directly pertaining to text editing. #+BEGIN_SRC emacs-lisp (defun my-find-file* (&rest paths) (mapc #'find-file (mapcar #'expand-file-name paths))) #+END_SRC ** time tracking - chronometrist :creator: :PROPERTIES: :CUSTOM_ID: time-tracking :END: choice.el is required by =chronometrist-key-values= *** chronometrist :PROPERTIES: :CUSTOM_ID: chronometrist :END: #+BEGIN_SRC emacs-lisp (use-package chronometrist ;; :disabled t :load-path "/media/data/contrapunctus/Documents/Text Files/programming/elisp/chronometrist/elisp/" :hook (chronometrist-kv-read-mode . visual-line-mode) (chronometrist-sexp-mode . auto-revert-mode) (chronometrist-sexp-mode . visual-line-mode) (kill-emacs-query-functions . chronometrist-query-stop) :bind (("" . chronometrist) ("" . chronometrist) (:map chronometrist-mode-map ("c" . previous-line) ("r" . next-line) ("R" . chronometrist-report)) (:map chronometrist-report-mode-map ("h" . chronometrist-report-previous-week) ("s" . chronometrist-report-next-week)) (:map chronometrist-details-mode-map ("t" . #'chronometrist-details-previous-range) ("n" . #'chronometrist-details-next-range))) :config (chronometrist-goal-minor-mode) (chronometrist-spark-minor-mode) (setq chronometrist-debug-enable t chronometrist-active-backend :plist-group chronometrist-before-in-functions '() chronometrist-after-in-functions '(;; chronometrist-tags-add ;; chronometrist-kv-add my-start-project) chronometrist-before-out-functions '(;; my-before-project-stop ;; chronometrist-tags-add ;; chronometrist-tag-choice chronometrist-key-values-unified-prompt ;; chronometrist-kv-add ;; chronometrist-skip-query-reset ) chronometrist-after-out-functions '(my-after-project-stop) chronometrist-activity-indicator 'my-chronometrist-activity-indicator chronometrist-details-display-key-values #'contrapunctus-display-key-values-function chronometrist-details-schema `[("#" 3 t) ("Task" 20 t) ("Tags" 20 t) ("Details" 55 t) ("Duration" 20 t :right-align t :pad-right 3) ("Time" 10 t)])) #+END_SRC Some Chronometrist configuration I prefer to keep private. #+BEGIN_SRC emacs-lisp (require 'my-chronometrist) #+END_SRC **** activity-indicator :PROPERTIES: :CUSTOM_ID: activity-indicator :END: #+BEGIN_SRC emacs-lisp (defun my-chronometrist-activity-indicator () (--> (chronometrist-latest-record (chronometrist-active-backend)) (plist-put it :stop (chronometrist-format-time-iso8601)) (list it) (chronometrist-plists-to-durations it) (-reduce #'+ it) (truncate it) (chronometrist-format-duration it))) #+END_SRC **** find-two-files :PROPERTIES: :CUSTOM_ID: find-two-files :END: #+BEGIN_SRC emacs-lisp (defun contrapunctus-find-two-files (file-1 file-2) "Open FILE-1 and FILE-2 in new windows. FILE-1 will appear above FILE-2." (find-file-other-window file-2) (split-window-below) (find-file file-1)) #+END_SRC **** outline-open-heading :PROPERTIES: :CUSTOM_ID: outline-open-heading :END: #+BEGIN_SRC emacs-lisp (defun cp-outline-open-heading (n) (goto-char (point-min)) (outline-next-visible-heading n) (outline-show-subtree)) #+END_SRC **** commit-prompt :PROPERTIES: :CUSTOM_ID: commit-prompt :END: #+BEGIN_SRC emacs-lisp (autoload 'magit-anything-modified-p "magit") (defun contrapunctus-commit-prompt () "Prompt user if `default-directory' is a dirty Git repository. Return t if the user answers yes, if the repository is clean, or if there is no Git repository. Return nil (and run `magit-status') if the user answers no." (cond ((not (magit-anything-modified-p)) t) ((yes-or-no-p (format "You have uncommitted changes in %S. Really clock out? " default-directory)) t) (t (magit-status) nil))) #+END_SRC **** after-project-stop :PROPERTIES: :CUSTOM_ID: after-project-stop :END: FIXME - 1. instead of changing the last plist, change the last plist with name "OSM" and without tags or key-values (because a new plist may have been inserted in the meantime) When I clock out of the "OSM" task, this code stores changeset details in it. ***** Requirements :PROPERTIES: :CUSTOM_ID: requirements :END: #+BEGIN_SRC emacs-lisp (use-package request :ensure t) (use-package esxml :ensure t) (use-package esxml-query) #+END_SRC ***** my-get-changeset-comment :PROPERTIES: :CUSTOM_ID: my-get-changeset-comment :END: #+BEGIN_SRC emacs-lisp (defun my-get-changeset-comment (changeset) (->> (esxml-query "[k=comment]" changeset) (esxml-node-attributes) (alist-get 'v))) #+END_SRC ***** my-make-osm-url :PROPERTIES: :CUSTOM_ID: my-make-osm-url :END: #+BEGIN_SRC emacs-lisp (defun my-make-osm-url (id) (concat "https://www.openstreetmap.org/changeset/" id)) #+END_SRC ***** my-get-changeset-comment :PROPERTIES: :CUSTOM_ID: my-get-changeset-comment-1 :END: #+BEGIN_SRC emacs-lisp (defun my-get-saved-changeset-id (backend) (-let* (((&plist :changesets saved-changesets) (cl-loop for plist in ;; the very latest OSM plist is the one we just created - ;; we want the one before that (rest (chronometrist-to-list backend)) when (and (equal "OSM" (plist-get plist :name)) ;; ignore plists without a :changesets keyword (plist-get plist :changesets)) return plist)) (((&plist :osm-url last-url)) (last saved-changesets))) (when last-url (first (last (split-string last-url "/")))))) #+END_SRC ***** my-save-osm-details :PROPERTIES: :CUSTOM_ID: my-save-osm-details :END: #+BEGIN_SRC emacs-lisp (cl-defun my-save-osm-details (&key data &allow-other-keys) (-let* ((backend (chronometrist-active-backend)) (last-id (my-get-saved-changeset-id backend)) (response-changesets (esxml-node-children data)) (new-changesets (if last-id (cl-loop with id for changeset in response-changesets do (setq id (alist-get 'id (esxml-node-attributes changeset))) if (equal id last-id) return (reverse changesets) else collect (list :osm-url (my-make-osm-url id) :osm-comment (my-get-changeset-comment changeset)) into changesets) ;; no changeset information in file - just use ;; the latest changeset from the response (let* ((changeset (first response-changesets)) (id (alist-get 'id (esxml-node-attributes changeset))) (comment (my-get-changeset-comment changeset))) `((:osm-url ,(my-make-osm-url id) :osm-comment ,comment))))) (new-plist (chronometrist-plist-update (chronometrist-latest-record (chronometrist-active-backend)) `(:changesets ,new-changesets)))) ;; (message "Last changeset ID - %s" last-id) ;; don't update if the latest changeset ID was already recorded (when new-changesets (chronometrist-replace-last (chronometrist-active-backend) new-plist)))) #+END_SRC ***** my-save-osm-changeset-details :PROPERTIES: :CUSTOM_ID: my-save-osm-changeset-details :END: #+BEGIN_SRC emacs-lisp (defun my-save-osm-changeset-details () "Save OSM changeset details in the Chronometrist file." (request "https://api.openstreetmap.org/api/0.6/changesets" :params '(("display_name" . "contrapunctus")) :parser (lambda () (libxml-parse-xml-region (point) (point-max))) :success #'my-save-osm-details)) #+END_SRC ***** my-after-project-stop :PROPERTIES: :CUSTOM_ID: my-after-project-stop :END: #+BEGIN_SRC emacs-lisp (defun my-after-project-stop (project) (pcase project ("OSM" (delete-other-windows) ;; What should we do when there's no network connectivity? ;; Ideally - note the clock-out time, and retry every five ;; minutes. When connected, request the changesets, look for the ;; first changeset with a "created_at" which is less than the ;; clock-out time. (my-save-osm-changeset-details)) (_ (delete-other-windows)))) #+END_SRC **** display-key-values :PROPERTIES: :CUSTOM_ID: display-key-values :END: Schema I use for plists - values can be - 1. integer 2. ="string"= | =("list" "of" "strings")= 3. =(INTEGER . "string")= 4. mixed alist - elements can be either #1 or #2 5. a plist, or a list of plists Programming - =:project=, [ =:component=,] =:feature= What if... 1. =(a b c)= -> ="a, b, c"= 2. =(a . b)= -> ="a b"= (e.g. =(a (b . c) d)= -> ="a, b c, d"=) 3. sublist - recurse #+BEGIN_SRC emacs-lisp ;; (contrapunctus-objects-to-string " - " nil) => "" ;; (contrapunctus-objects-to-string " - " 1) => "1" (defun contrapunctus-objects-to-string (separator &rest args) "Return ARGS as a string, removing nil values." (mapconcat (lambda (elt) (format "%s" elt)) (flatten-list (seq-filter #'identity args)) separator)) (defun contrapunctus-display-key-values-helper (list) (cl-loop for elt in list collect (cond ((stringp elt) elt) ((chronometrist-pp-pair-p elt) (format "%s %s" (car elt) (cdr elt))) ((listp elt) (contrapunctus-display-key-values-function elt)) (t "")) into strings finally return (mapconcat #'identity (seq-filter #'identity strings) ", "))) (defun contrapunctus-display-key-values-function (plist) "Function used to print key-values in `chronometrist-details' buffers." (let ((values (seq-remove #'keywordp (chronometrist-plist-key-values plist)))) (contrapunctus-display-key-values-helper values))) #+END_SRC **** my-chronometrist-copy-exercise-data :PROPERTIES: :CUSTOM_ID: my-chronometrist-copy-exercise-data :END: #+BEGIN_SRC emacs-lisp (defun my-chronometrist-copy-exercise-data () "Add exercise data from OsmAnd tracks to Chronometrist." (interactive) ;; Date for which to get data (if-let* ((date-iso (read-string "Date (ISO-8601): " (format-time-string "%F"))) (date (parse-iso8601-time-string date-iso)) (dir (concat "/media/data/phone/contrapunctus/" "Nokia 6.1/Android/data/net.osmand.plus/files/tracks/rec/" (format-time-string "%+4Y-%m/" date))) (dir-check (file-exists-p dir)) (plists (cl-loop with activity for file in (directory-files dir) when (and (string-match-p (format-time-string "^%F_" date) file) (or (and (string-match-p "(walk)" file) (setq activity :walking)) (and (string-match-p "(run)" file) (setq activity :running)))) collect (-let* ((file-name file) ((from to) (progn (string-match (rx (group (+? num) "-" (= 2 num) "-" (= 2 num) "_" (= 2 num) "-" (= 2 num) "_" (= 3 (any alpha))) " " (group "(" (or "walk" "run") ")") " " (group (+? (any printing))) (group "\.gpx")) file-name) (split-string (match-string 3 file-name) " - ")))) (with-current-buffer (get-buffer-create (find-file-noselect (concat dir file))) (append '(:name "Exercise") (list activity (cons (floor (my-gpx-distance file)) "meters")) (if to (list :from from :to to) (list :location from)) (my-get-gpx-timestamps)) ;; (chronometrist-insert (chronometrist-active-backend)) )))) (string (mapconcat (lambda (plist) (chronometrist-plist-pp plist)) plists "\n"))) (progn (kill-new string) (message "Copied %s to kill ring" string)) (message "No suitable GPX files found"))) #+END_SRC ***** my-great-circle-distance :PROPERTIES: :CUSTOM_ID: my-great-circle-distance :END: https://stackoverflow.com/questions/365826/calculate-distance-between-2-gps-coordinates https://www.movable-type.co.uk/scripts/latlong.html #+BEGIN_SRC emacs-lisp (defun my-great-circle-distance (lat1 lon1 lat2 lon2) (let* ((earth-radius-km (* 6371 1000)) (dlat (degrees-to-radians (- lat2 lat1))) (dlon (degrees-to-radians (- lon2 lon1))) (lat1 (degrees-to-radians lat1)) (lat2 (degrees-to-radians lat2)) (a (+ (* (sin (/ dlat 2)) (sin (/ dlat 2))) (* (sin (/ dlon 2)) (sin (/ dlon 2)) (cos lat1) (cos lat2)))) (c (* 2 (atan (sqrt a) (sqrt (- 1 a)))))) (* earth-radius-km c))) ;; (my-great-circle-distance 51.5 0 38.8 -77.1) #+END_SRC ***** my-gpx-distance :PROPERTIES: :CUSTOM_ID: my-gpx-distance :END: #+BEGIN_SRC emacs-lisp (defun my-gpx-distance (file) "Return distance covered (in meters) by GPX track FILE." (with-current-buffer (get-buffer-create (find-file-noselect file)) (let* ((dom (libxml-parse-xml-region (point-min) (point-max)))) (cl-flet ((attr-as-num (key attributes) (string-to-number (alist-get key attributes)))) (loop with previous-attributes for point in (esxml-query-all "trkpt" dom) if previous-attributes sum (let* ((attributes (xml-node-attributes point)) (lat2 (attr-as-num 'lat attributes)) (lon2 (attr-as-num 'lon attributes)) (lat1 (attr-as-num 'lat previous-attributes)) (lon1 (attr-as-num 'lon previous-attributes))) (my-great-circle-distance lat1 lon1 lat2 lon2)) do (setq previous-attributes (xml-node-attributes point))))))) #+END_SRC *** goal :PROPERTIES: :CUSTOM_ID: goal :END: #+BEGIN_SRC emacs-lisp (use-package chronometrist-goal :commands (chronometrist-goal-minor-mode) :load-path "~/.emacs.d/contrapunctus/chronometrist-goal/" :hook (chronometrist-mode . chronometrist-goal-minor-mode) :config (setq alert-default-style 'libnotify)) #+END_SRC *** spark :PROPERTIES: :CUSTOM_ID: spark :END: #+BEGIN_SRC emacs-lisp (use-package chronometrist-spark :commands (chronometrist-spark-minor-mode) :load-path "/media/data/contrapunctus/Documents/Text Files/programming/elisp/chronometrist/elisp/" :hook (chronometrist-mode . chronometrist-spark-minor-mode) :config (setq chronometrist-spark-length (* 7 4))) #+END_SRC *** key-values :PROPERTIES: :CUSTOM_ID: key-values :END: #+BEGIN_SRC emacs-lisp (use-package chronometrist-key-values :ensure t :after chronometrist :load-path "/media/data/contrapunctus/Documents/Text Files/programming/elisp/chronometrist/elisp/") #+END_SRC **** my-get-gpx-timestamps :PROPERTIES: :CUSTOM_ID: my-get-gpx-timestamps :END: #+BEGIN_SRC emacs-lisp (defun my-get-gpx-timestamps () "Return ISO-8601 timestamps from GPX data as ISO-8601 timestamps in local time." (interactive) (let* ((dom (libxml-parse-xml-region (point-min) (point-max))) (trkseg (esxml-query "trkseg" dom)) (first-trkpt (third trkseg)) (first-iso (second (alist-get 'time first-trkpt))) (first-unix (parse-iso8601-time-string first-iso)) (last-trkpt (first (last trkseg))) (last-iso (second (alist-get 'time last-trkpt))) (last-unix (parse-iso8601-time-string last-iso))) (list :start (chronometrist-format-time-iso8601 first-unix) :stop (chronometrist-format-time-iso8601 last-unix)))) #+END_SRC **** my-copy-gpx-timestamps :PROPERTIES: :CUSTOM_ID: my-copy-gpx-timestamps :END: #+BEGIN_SRC emacs-lisp (defun my-copy-gpx-timestamps () "Copy ISO-8601 timestamps from GPX data as ISO-8601 timestamps in local time." (interactive) (let* ((plist (my-get-gpx-timestamps)) (string (apply #'format "%S %S\n%S %S" plist))) (kill-new string) (apply #'message "Copied %S to kill ring" plist))) #+END_SRC *** Third Time :PROPERTIES: :CUSTOM_ID: third-time :END: #+BEGIN_SRC emacs-lisp (use-package chronometrist-third :load-path "/media/data/contrapunctus/Documents/Text Files/programming/elisp/chronometrist/elisp/" :commands (chronometrist-third-minor-mode) :init (chronometrist-third-minor-mode)) #+END_SRC *** querying data :PROPERTIES: :CUSTOM_ID: querying-data :END: An example of querying the Chronometrist file data - finding out how much time I've spent on tasks matching a certain criteria. #+BEGIN_SRC emacs-lisp :tangle no :load no ;; XXX - day count is incorrect (cl-loop for plist in (chronometrist-to-list (chronometrist-active-backend)) with count = 0 when (and (equal (plist-get plist :name) "Composing") (equal (plist-get plist :song) "आदि काल से आज तलक")) sum (chronometrist-interval plist) into seconds and do (cl-incf count) finally return (unless (zerop seconds) (format "%s, in %s days." (format-seconds "%Y, %D, %H, %M, and %S%z" seconds) count))) #+END_SRC **** Intervals and durations for task Exercise :PROPERTIES: :CUSTOM_ID: intervals-and-durations-for-task-exercise :END: #+BEGIN_SRC emacs-lisp :tangle no :load no (cl-loop for plist in (chronometrist-to-list (chronometrist-active-backend)) with count = 0 when (equal (plist-get plist :name) "Exercise") collect (let* ((plist (copy-list plist)) (interval-seconds (chronometrist-interval plist)) (start (plist-get plist :start))) (plist-put plist :duration (ts-human-format-duration interval-seconds)) (plist-put plist :date (seq-take start 10)) (chronometrist-plist-remove plist :start :stop))) #+END_SRC **** Unique key-values for task "Exercise" :PROPERTIES: :CUSTOM_ID: unique-key-values-for-task-exercise :END: #+BEGIN_SRC emacs-lisp :tangle no :load no (cl-loop with task = "Exercise" for plist in (chronometrist-to-list (chronometrist-active-backend)) when (equal (plist-get plist :name) task) collect (chronometrist-plist-key-values plist) into key-values finally do (let ((buffer (get-buffer-create (generate-new-buffer-name "chronometrist-query")))) (with-current-buffer buffer (->> (cl-remove-duplicates key-values) (seq-filter #'identity) (format "%S") (insert)) (emacs-lisp-mode) (switch-to-buffer buffer)))) #+END_SRC **** Time spent running since the 13th of December :PROPERTIES: :CUSTOM_ID: time-spent-running-since-the-13th-of-december :END: #+BEGIN_SRC emacs-lisp :tangle no :load no (cl-loop with start = (parse-iso8601-time-string "2021-12-13") with buffer = (get-buffer-create "*chronometrist-query*") with output for plists being the hash-values of (chronometrist-to-hash-table (chronometrist-active-backend)) using (hash-keys date-iso) when ;; create a list of strings for each date on which I ran (cl-loop with first-line = t with date = (parse-iso8601-time-string date-iso) for plist in plists when (and (plist-get plist :running) (or (time-less-p start date) (time-equal-p start date))) collect (let* ((plist (copy-list plist)) (duration-seconds (chronometrist-interval plist)) (start (plist-get plist :start)) (distance (plist-get plist :running)) (distance-int (car distance)) (distance-km (/ (float distance-int) 1000)) (duration-hours (/ (float duration-seconds) 60 60)) (distance-string (format "% 4s %s" distance-int (cdr distance))) (speed-string (format "%.2f" (/ distance-km duration-hours)))) (format "%s%s in % 5s (% 5s kmph)\n" (if (not first-line) (make-string (+ 3 (length date-iso)) ?\s) (setq first-line nil) (concat date-iso " - ")) distance-string (format-seconds "%m:%02s" duration-seconds) speed-string))) collect it into lists ;; Add indices for each date, and indentation when there are >1 ;; instances of running in a date finally do (setq output (cl-loop for list in (reverse lists) count list into index concat (let* ((index-string (format "%2s. " index)) (indent (if (> (length list) 1) (make-string (length index-string) ?\s) ""))) (concat index-string (first list) indent (mapconcat #'identity (rest list) indent))))) (with-current-buffer buffer (erase-buffer) (insert output) (text-mode) (switch-to-buffer-other-window buffer))) #+END_SRC ** WISH emms :PROPERTIES: :CUSTOM_ID: emms :END: 1. [ ] make toggle command for emms-start/emms-stop 2. [ ] change mode line display - don't show the whole file path, just the name #+BEGIN_SRC emacs-lisp (use-package emms :ensure t :after hydra :init (setq emms-info-functions '(emms-info-tinytag)) :bind (" e" . #'contrapunctus-emms-hydra/body) (" E" . #'emms) (:map dired-mode-map ("E" . #'contrapunctus-emms-hydra/body)) :commands (emms-all emms emms-play-dired emms-add-dired) :config (emms-all) (setq emms-player-list '(emms-player-mpv) emms-player-mpv-parameters #'my-emms-mpv-parameters emms-info-tinytag-python-name "python3") ;; ;; This won't work for `emms-random', because it runs in a `save-excursion' ;; (add-to-list 'emms-playlist-selection-changed-hook 'emms-playlist-mode-center-current) ;; (--map (add-to-list 'emms-player-mpv-parameters it) ;; '("--fs")) (defun contrapunctus-emms-toggle-player () (interactive) (if emms-player-stopped-p (emms-start) (emms-stop))) :init (defhydra contrapunctus-emms-hydra () ("e" #'emms "EMMS") ("n" #'emms-next "Next") ("p" #'emms-previous "Previous") ("SPC" #'emms-pause "Pause") ("s" #'contrapunctus-emms-toggle-player "Start/Stop") ("0" #'emms-volume-raise) ("9" #'emms-volume-lower) ("" #'emms-volume-raise) ("" #'emms-volume-lower) ("" #'emms-seek-backward) ("" #'emms-seek-forward) ("l" #'emms-play-dired "Play file (dired)") ("a" #'emms-add-dired "Add file (dired)") ("A" #'emms-add-directory-tree "Add directory") ("u" #'emms-play-url))) (defun my-emms-mpv-parameters () (append '("--quiet" "--really-quiet" ;; "--vid=no" "--force-window=yes" ;; "--loop-file=inf" ) (let* ((dir (->> (emms-playlist-current-selected-track) (alist-get 'name) (file-name-directory))) (subs-in-dir (f-glob "*.srt" dir)) (subs-in-subdir (f-glob "*/*.srt" dir))) (->> (append subs-in-dir subs-in-subdir) (-interpose ":") (append '("--sub-files=")) (apply #'concat) (list))))) (use-package emms-playlist-mode :bind (:map emms-playlist-mode-map ("0" . #'emms-volume-raise) ("9" . #'emms-volume-lower) ("" . #'emms-volume-raise) ("" . #'emms-volume-lower) ("" . #'emms-seek-backward) ("" . #'emms-seek-forward) ("r" . #'next-line) ("c" . #'previous-line) ("R" . #'emms-next) ("C" . #'emms-previous) ("C-r" . #'emms-toggle-random-playlist) ("M-r" . #'emms-random) ("C-c" . #'emms-playlist-mode-center-current) ("SPC" . #'emms-pause) ("K" . #'emms-playlist-clear)) :config (setq emms-playlist-buffer-name "EMMS Playlist")) #+END_SRC ** Internet :PROPERTIES: :CUSTOM_ID: internet :END: *** eww :viewer: :PROPERTIES: :CUSTOM_ID: eww :END: #+BEGIN_SRC emacs-lisp (use-package eww :config (setq shr-image-animate nil) :bind ;; start boon-specific config (:map shr-map ("v" . nil)) (:map shr-image-map ("i" . nil) ("v" . nil)) (:map eww-link-keymap ("i" . nil) ("v" . nil)) (:map eww-mode-map ("h" . #'eww-back-url) ([mouse-3] . eww-back-url) ("s" . #'eww-forward-url) ("c" . #'shr-previous-link) ("r" . #'shr-next-link) ("v" . nil)) (:map eww-bookmark-mode-map ("c" . #'previous-line) ("r" . #'next-line)) ;; end boon-specific config ) (defun my-eww () (interactive) (cond ((derived-mode-p 'dired-mode) (eww-open-file (dired-file-name-at-point))) ((derived-mode-p 'html-mode) (eww-open-file (buffer-file-name))) (t (call-interactively #'eww)))) #+END_SRC *** url-cookie :PROPERTIES: :CUSTOM_ID: url-cookie :END: Ask for confirmation before saving cookies. I'd rather just disallow them all though 🤔 #+BEGIN_SRC emacs-lisp (use-package url-cookie :config (setq url-cookie-confirmation t)) #+END_SRC *** elpher :viewer: :PROPERTIES: :CUSTOM_ID: elpher :END: #+BEGIN_SRC emacs-lisp (use-package elpher :ensure t :bind (:map elpher-mode-map ("r" . elpher-next-link) ("c" . elpher-prev-link) ("h" . elpher-back) ("s" . push-button) ("w" . elpher-copy-current-url) ("W" . elpher-copy-link-url) ("g" . elpher-reload))) #+END_SRC *** elfeed :PROPERTIES: :CUSTOM_ID: elfeed :END: #+BEGIN_SRC emacs-lisp (use-package elfeed :ensure t :bind (:map elfeed-show-mode-map ("v" . nil)) :config (add-to-list 'boon-special-mode-list 'elfeed-show-mode) (add-to-list 'boon-special-mode-list 'elfeed-search-mode)) #+END_SRC *** jabber :disabled:communication: :PROPERTIES: :CUSTOM_ID: jabber :END: #+BEGIN_SRC emacs-lisp (use-package jabber :disabled t :load-path "~/.emacs.d/elisp-git/emacs-jabber-wgreenhouse/" :commands jabber-connect :config (global-unset-key (kbd "C-x C-j")) (global-set-key (kbd "C-x C-j") 'join-line) (setq jabber-history-enabled t jabber-history-muc-enabled t jabber-alert-presence-message-function nil jabber-account-list '(("contrapunctus@jabjab.de"))) (add-to-list 'jabber-post-connect-hooks 'jabber-enable-carbons)) #+END_SRC ** sxiv :viewer: :PROPERTIES: :CUSTOM_ID: sxiv :END: #+BEGIN_SRC emacs-lisp (use-package sxiv :load-path "/media/data/contrapunctus/Documents/Text Files/programming/elisp/sxiv/" :config (setq sxiv-exclude-strings '("meh" "\\.NEF$")) :bind (" s" . sxiv) (:map dired-mode-map ("I" . sxiv))) #+END_SRC ** TODO emacsshot :creator: :PROPERTIES: :CUSTOM_ID: emacsshot :END: PR ideas 1. [ ] create directories in save path if they don't exist 2. [ ] grammar - "written /path/to/file" #+BEGIN_SRC emacs-lisp (use-package emacsshot :ensure t :bind (" " . emacsshot-snap-window-filename) (" " . emacsshot-snap-frame-filename) (" " . emacsshot-snap-mouse-filename) :config (setq emacsshot-with-timestamp t emacsshot-snap-window-filename "/media/data/contrapunctus/Pictures/screenshots/emacsshot/emacsshot.png")) #+END_SRC ** dired :file: :PROPERTIES: :CUSTOM_ID: dired :END: #+BEGIN_SRC emacs-lisp (use-package dired :init (add-hook 'dired-mode-hook 'turn-on-launch-mode) :config (setq dired-listing-switches ;; by date ;; "-cgGhlt --group-directories-first --time-style=long-iso" ;; by name "-Achl --group-directories-first --time-style=long-iso" ;; no -h ;; "-cgGl --group-directories-first --time-style=long-iso" ;; by date, no --group-directories-first ;; "-cgGhlt --time-style=long-iso" dired-bind-info nil) :bind (:map dired-mode-map ("W" . wdired-change-to-wdired-mode) ("e" . #'cp/dired-do-ediff) ;; after learning that this copies whole paths with null ;; argument, this became a whole lot more useful ("C-w" . dired-copy-filename-as-kill) ("C-c C-f" . my-corresponding-text-file) ("h" . dired-hide-dotfiles-mode) ([mouse-2] . cp/dired-launch-or-open) ("C-j" . launch-files-dired) ("j" . launch-files-dired) ("M-s r" . dired-do-query-replace-regexp) ("M-s s" . dired-do-isearch-regexp) ("P" . emms-play-dired) ("X" . dired-do-flagged-delete) ("M-n" . dired-next-marked-file) ("M-p" . dired-prev-marked-file) ("I" . sxiv) ("c" . dired-previous-line) ("r" . dired-next-line)) :hook (dired-mode . (lambda () (dired-hide-details-mode t))) (dired-mode . auto-revert-mode)) (use-package dired-async :init (dired-async-mode 1)) (use-package dired-x :commands (dired-jump dired-omit-mode) :bind ("C-x C-d" . dired-jump) (:map dired-mode-map ("H" . dired-omit-mode) ("I" . sxiv))) #+END_SRC #+BEGIN_SRC emacs-lisp ;; TODO - make launch-file suggest the path at point by default (use-package launch :commands turn-on-launch-mode launch-files-dired :bind ("s-l" . launch-file)) #+END_SRC *** my-open-random-file :command: :PROPERTIES: :CUSTOM_ID: my-open-random-file :END: #+BEGIN_SRC emacs-lisp (defun my-open-random-file (&optional find-args dir cmd) "Open a random file in DIR, prompting the user for it if not supplied." (interactive) (let* ((find-args (if find-args find-args " -type f ")) (dir (if dir dir (read-directory-name "Directory: " (if file-name-history (car file-name-history) default-directory) nil t))) (file-name (--> (expand-file-name dir) (concat "find " "\"" it "\" " find-args " | shuf | sed 1q") (shell-command-to-string it) (replace-regexp-in-string "\n" "" it)))) (if cmd (async-shell-command (concat cmd " \"" file-name "\"")) (find-file file-name)))) #+END_SRC #+BEGIN_SRC emacs-lisp ;; (with-eval-after-load 'project-explorer ;; (global-set-key (kbd " e") 'project-explorer-toggle)) (use-package dired-hide-dotfiles :hook (dired-mode . (lambda () (dired-hide-dotfiles-mode)))) ;; (require 'sudo-edit) (defun cp/dired-do-ediff (&optional format) "Ediff (first two or three) marked files." (interactive) (let* ((files (dired-get-marked-files t)) (file-1 (car files)) (files-dir default-directory)) ;; 2018-04-08T11:31:20+0530 TODO - if there is only one marked ;; file, check the other window for a marked file and ediff with ;; that. (cl-case (length files) (1 (progn (other-window 1) (let ((files2 (dired-get-marked-files t)) (files2-dir default-directory)) (if files2 (ediff (expand-file-name (concat files-dir (car files))) (expand-file-name (concat files2-dir (car files2)))))))) (2 (ediff file-1 (cadr files))) (t (ediff3 file-1 (cadr files) (elt files 2)))))) (defun cp/change-all-units () "for fdupes output" (interactive) (while (re-search-forward "^[0-9]+" nil t) (shell-command-on-region (point-at-bol) (point) "numfmt --to=iec-i --suffix=B" nil t) (forward-word) (delete-region (point) (progn (forward-word) (point))))) (defun cp/launch-file-archive () (interactive) (launch-file (concat default-directory (aref (archive-get-descr) 0)))) ;; (define-key archive-mode-map (kbd "j") 'cp/launch-file-archive) ;; 2018-02-28T21:00:57+0530 (defun my-corresponding-text-file () (interactive) (save-excursion (end-of-line) (if (derived-mode-p 'dired-mode) ;; 2018-08-05T02:01:26+0530 - support directories too (let* ((file-or-dir (dired-file-name-at-point)) (file (if (file-directory-p file-or-dir) (replace-regexp-in-string "/$" "" file-or-dir) file-or-dir))) (find-file (concat file ".txt")))))) ;; 2018-07-09T23:22:17+0530 ;; a little buggy wrt clicks (defun cp/dired-launch-or-open (event) (interactive "e") ;; if point is on a folder, open it with dired ;; otherwise, call launch-files-dired (if (directory-name-p (dired-file-name-at-point)) (dired-find-file) (launch-files-dired nil (dired-get-marked-files)))) (defun my-delete-file-at-point (&optional prefix) "In text buffers, delete the file corresponding to the path on the current line." (interactive "P") (let ((file (buffer-substring (point-at-bol) (point-at-eol)))) (if (file-exists-p file) (progn (delete-file file) (if prefix ;; delete current line (delete-region (point-at-bol) (1+ (point-at-eol))) ;; delete current paragraph (mark-paragraph) (delete-active-region) (forward-line 2)) (message "Deleted %s" file)) (error "File %s does not exist!" file)))) (defun my-file-at-point-exists-p () "In text buffers, check if the file corresponding to the path on the current line exists." (interactive) (let ((file (buffer-substring (point-at-bol) (point-at-eol)))) (if (and (not (string-empty-p file)) (file-exists-p file)) (message "%s" t) (error "File %S does not exist!" file)))) #+END_SRC ** peep-dired :disabled:file:ui: :PROPERTIES: :CREATED: 2022-01-14T18:57:56+0530 :CUSTOM_ID: peep-dired :END: #+BEGIN_SRC emacs-lisp (use-package peep-dired :disabled ;; ;; didn't work too well 🤔 ;; :config ;; (setq peep-dired-cleanup-eagerly t) :hook (dired-mode . peep-dired)) #+END_SRC *** my-rename-this-file :command: :PROPERTIES: :CUSTOM_ID: my-rename-this-file :END: #+BEGIN_SRC emacs-lisp (defun my-rename-this-file () (interactive) (rename-file ;; message "will rename %s to %s" (buffer-file-name (current-buffer)) (read-file-name "New name: " nil nil nil (file-name-nondirectory (buffer-file-name (current-buffer))))) ;; (rename-file (buffer-file-name (current-buffer)) new-name) ) #+END_SRC *** my-delete-this-file :command: :PROPERTIES: :CUSTOM_ID: my-delete-this-file :END: #+BEGIN_SRC emacs-lisp (defun my-delete-this-file () (interactive) (when (y-or-n-p "Delete this file?") (delete-file (buffer-file-name)) (image-next-file))) #+END_SRC #+END_SRC ** backup configuration :PROPERTIES: :CUSTOM_ID: backup-configuration :END: #+BEGIN_SRC emacs-lisp (setq backup-by-copying t backup-directory-alist '(("." . "~/.emacs.d/saves/")) kept-new-versions 50 kept-old-versions 50 version-control t delete-old-versions t) #+END_SRC ** async-backup (backup on save) :file:vc: :PROPERTIES: :CREATED: 2022-01-14T16:38:38+0530 :CUSTOM_ID: async-backup :END: #+BEGIN_SRC emacs-lisp (use-package async-backup :ensure t :config (setq async-backup-location "/media/data/contrapunctus/backups/emacs-async-backup/")) #+END_SRC ** mail :communication: :PROPERTIES: :CUSTOM_ID: mail :END: #+BEGIN_SRC emacs-lisp (use-package emacs :config (setq user-mail-address "contrapunctus@disroot.org" user-full-name "contrapunctus" smtpmail-smtp-user "contrapunctus" auth-source-debug 'trivia ;; Use system hostname to prevent "Sending failed: ...: Helo ;; command rejected: need fully-qualified hostname" error when ;; sending mail mail-host-address nil)) #+END_SRC *** wanderlust :disabled: :PROPERTIES: :CREATED: 2022-01-14T18:58:32+0530 :CUSTOM_ID: wanderlust :END: #+BEGIN_SRC emacs-lisp (use-package wl :disabled :config (setq wl-icon-directory "~/.emacs.d/wl/icons" wl-smtp-posting-server "disroot.org")) #+END_SRC *** mew :disabled: :PROPERTIES: :CREATED: 2022-01-14T18:58:53+0530 :CUSTOM_ID: mew :END: #+BEGIN_SRC emacs-lisp (use-package mew :disabled :config (setq mew-user "contrapunctus" mew-name mew-user mew-mail-domain "disroot.org" mew-proto "%" mew-smtp-server mew-mail-domain mew-imap-server mew-mail-domain)) #+END_SRC *** gnus :disabled: :PROPERTIES: :CREATED: 2022-01-14T18:58:56+0530 :CUSTOM_ID: gnus :END: #+BEGIN_SRC emacs-lisp (use-package gnus :disabled t :init ;; I composed an email without loading Gnus, and it wasn't ;; saved in my sent folder; here's hoping that changing :config to ;; :init will do it. (setq gnus-select-method '(nnnil "") gnus-secondary-select-methods '((nnml "") (nnimap "disroot.org") ;; (nnimap "imap.gmail.com" ;; (nnimap-stream starttls)) ) gnus-mime-display-multipart-related-as-mixed t smiley-style 'medium message-send-mail-function 'smtpmail-send-it ;; gnus-message-archive-method '(nnimap "disroot.org") gnus-message-archive-group "nnimap+disroot.org:Sent")) (use-package message :hook (message-mode . (lambda () (auto-fill-mode -1))) (message-mode . visual-fill-column-mode)) #+END_SRC *** mu4e :PROPERTIES: :CUSTOM_ID: mu4e :END: #+BEGIN_SRC emacs-lisp (use-package mu4e :bind (:map mu4e-headers-mode-map ("c" . #'mu4e-headers-prev) ("r" . #'mu4e-headers-next) ("m" . #'mu4e-headers-mark-for-something) ("u" . #'mu4e-headers-mark-for-unmark) ("C-c C-c" . #'mu4e-mark-execute-all) ("+" . #'my-mu4e-mkdir) ("t" . #'mu4e-headers-prev-unread) ("n" . #'mu4e-headers-next-unread)) (:map mu4e-view-mode-map ("c" . #'mu4e-view-headers-prev) ("r" . #'mu4e-view-headers-next)) (:map gnus-mime-button-map ("c" . nil) ("r" . nil)) :init (mu4e t) :config (setq mu4e-get-mail-command "mbsync -aV" mu4e-drafts-folder "/Drafts/" mu4e-sent-folder "/Sent/" mu4e-trash-folder "/Trash/" ;; Use vertico, not ido. mu4e-completing-read-function #'completing-read shr-use-colors nil mu4e-use-fancy-chars t)) (defun my-mu4e-mkdir () "Make new maildir." (interactive) (start-process "mu-mkdir" (generate-new-buffer-name "mu-mkdir") "mu" "mkdir" (expand-file-name (read-file-name "Make maildir: " (mu4e-root-maildir))))) #+END_SRC **** mu4e-alert :PROPERTIES: :CUSTOM_ID: mu4e-alert :END: Even the patched version of =mu4e-alert= I'm using is not completely bug-free - many a times I get new mail, =imapnotify= detects it and runs =mbsync=, the =mu4e-index-updated-hook= is run (since the alert in =my-mail-alert= is displayed), and it contains =mu4e-alert-notify-unread-mail-async=...but I don't get an alert from =mu4e-alert= 🙁 #+BEGIN_SRC emacs-lisp (defun my-mail-alert () (alert "You've got mail!")) (add-hook 'mu4e-index-updated-hook #'my-mail-alert) (use-package mu4e-alert :quelpa (mu4e-alert :fetcher github :repo "xzz53/mu4e-alert") :init (mu4e-alert-enable-notifications) :config (setq alert-fade-time 15)) #+END_SRC ** doc-view :viewer: :PROPERTIES: :CUSTOM_ID: doc-view :END: #+BEGIN_SRC emacs-lisp (use-package doc-view :config (setq doc-view-resolution 300)) #+END_SRC ** pdf-tools :viewer: :PROPERTIES: :CUSTOM_ID: pdf-tools :END: #+BEGIN_SRC emacs-lisp (use-package pdf-tools :ensure t :init (pdf-tools-install) :hook (pdf-view-mode . auto-revert-mode) (pdf-view-mode . pdf-view-midnight-minor-mode) :bind (:map pdf-view-mode-map ("h" . pdf-history-backward) ("s" . pdf-history-forward) ("c" . pdf-view-scroll-down-or-previous-page) ("r" . pdf-view-scroll-up-or-next-page) ("C" . #'pdf-view-previous-page) ("R" . #'pdf-view-next-page) ("C-s" . isearch-forward) ("g" . pdf-view-first-page) ("l" . pdf-view-last-page) ("i" . pdf-view-revert-buffer)) ;; QWERTY g :config (setq-default pdf-view-display-size 'fit-width)) (use-package pdf-history :bind (:map pdf-history-minor-mode-map ("r" . nil))) #+END_SRC ** nov.el :viewer: :PROPERTIES: :CUSTOM_ID: nov.el :END: #+BEGIN_SRC emacs-lisp (use-package nov :ensure t :mode ("\\.epub\\'" . nov-mode) :hook (nov-mode . visual-line-mode) :bind (:map nov-mode-map ;; make (Boon) c and r work even when point is in a link ("c" . nil) ("r" . nil) ("y" . nov-copy-url) ("c" . nov-previous-document) ("r" . nov-next-document) ("h" . nov-history-back) ("s" . nov-history-forward) ("T" . nov-goto-toc) ("m" . my-dispatch-hydra)) (:map nov-button-map ("c" . nil) ("r" . nil))) #+END_SRC ** proced :PROPERTIES: :CUSTOM_ID: proced :END: #+BEGIN_SRC emacs-lisp (use-package proced :config (setq-default proced-auto-update-flag t)) #+END_SRC ** webpaste :PROPERTIES: :CUSTOM_ID: webpaste :END: #+BEGIN_SRC emacs-lisp (use-package webpaste :ensure t :config (setq webpaste-provider-priority (delete "ix.io" (mapcar #'car webpaste-providers-alist)))) #+END_SRC ** eshell :shell: :PROPERTIES: :CUSTOM_ID: eshell :END: #+BEGIN_SRC emacs-lisp (use-package eshell :config (setq eshell-history-size 999)) #+END_SRC ** term :PROPERTIES: :CUSTOM_ID: term :END: #+BEGIN_SRC emacs-lisp (use-package term :bind (:map term-mode-map ("M-c" . #'term-previous-input) ("M-r" . #'term-next-input))) #+END_SRC ** comint :shell: :PROPERTIES: :CUSTOM_ID: comint :END: #+BEGIN_SRC emacs-lisp (use-package comint :bind (:map comint-mode-map ("M-p" . comint-history-isearch-backward-regexp) ;; QWERTY "r" ("M-c" . comint-previous-matching-input-from-input) ("M-r" . comint-next-matching-input-from-input) ("C-c C-s" . comint-next-prompt) ("C-c C-h" . comint-previous-prompt))) #+END_SRC ** info :viewer: :PROPERTIES: :CUSTOM_ID: info :END: #+BEGIN_SRC emacs-lisp (use-package info :config (info-initialize) (cl-loop for dir in '("~/.emacs.d/info/" "~/.emacs.d/elisp-git/geiser/doc/" "~/lilypond/usr/share/info/") do (add-to-list #'Info-directory-list dir)) :bind (:map Info-mode-map ("b" . Info-history-back) ("f" . Info-history-forward))) #+END_SRC ** image-mode :viewer: :PROPERTIES: :CUSTOM_ID: image-mode :END: #+BEGIN_SRC emacs-lisp (use-package image-mode :bind (:map image-map ("c" . nil) ("r" . nil) ("o" . nil)) (:map image-mode-map ("c" . image-previous-file) ("r" . image-next-file) ("W" . image-transform-fit-to-width) ("H" . image-transform-fit-to-height) ("o" . nil) ("R" . my-rename-this-file) ("D" . my-delete-this-file)) :config (setq image-use-external-converter t)) #+END_SRC ** TODO magit [0%] :vc: :PROPERTIES: :CUSTOM_ID: magit :END: 1. [ ] It'd be really cool to =(recenter 3)= when you /open/ a section, and =(recenter)= when you close a section 2. [ ] binding "c" to magit-section-backward and "j" to magit-commit means I can't nonchalantly hit "c c" to commit like before...it becomes "j c" :\ #+BEGIN_SRC emacs-lisp (use-package magit :ensure t :bind (;; boon-like keys :map magit-mode-map ("r" . magit-section-forward) ("c" . magit-section-backward) ("C" . magit-commit) ("R" . magit-rebase) :map magit-diff-section-base-map ("C" . magit-commit) ("R" . magit-rebase) :map magit-status-mode-map ;; ([mouse-3] . 'magit-section-toggle) ([down-mouse-3] . 'mouse-set-point) ([up-mouse-3] . 'magit-section-toggle) ("C" . magit-commit)) :commands magit-status :hook (magit-mode . visual-line-mode) (magit-post-stage . (lambda () (recenter))) :config (cl-loop for symbol in '(magit-section-toggle magit-section-forward magit-section-backward) do (advice-add symbol :after (lambda (&rest r) (recenter 3)))) ;; (advice-add 'magit-unstage-item :after (lambda (&rest r) (next-line))) ) #+END_SRC ** git-commit :vc: :PROPERTIES: :CREATED: 2022-01-14T16:36:44+0530 :CUSTOM_ID: git-commit :END: #+BEGIN_SRC emacs-lisp (use-package git-commit :ensure t :bind (:map git-commit-mode-map ("M-c" . git-commit-prev-message) ("M-r" . git-commit-next-message))) #+END_SRC * Completion :PROPERTIES: :CREATED: 2022-01-13T20:39:41+0530 :CUSTOM_ID: completion :END: The =initials= completion style is excellent! Instead of typing =(w-o-t-t|)= to get =(with-output-to-temp-buffer|)=, you can just type =(wott|)=. But with =initials=, the desired completion is often buried in the results. That is where =prescient= shines - it remembers your input text and ranks previously-selected suggestions higher. #+BEGIN_SRC emacs-lisp (use-package emacs :config (setq tags-add-tables nil ;; don't prompt me for tag tables completion-styles '(basic partial-completion emacs22 initials orderless substring) completion-cycle-threshold t)) #+END_SRC =intials= is great for code (or at least Lisp code), but I prefer =orderless= for command names. It is basically the same as Ivy/Counsel completion, as far as I can make out. ** orderless :PROPERTIES: :CREATED: 2022-01-13T17:31:53+0530 :CUSTOM_ID: orderless :END: #+BEGIN_SRC emacs-lisp (use-package orderless) #+END_SRC ** company :PROPERTIES: :CREATED: 2022-01-05T20:16:03+0530 :CUSTOM_ID: company :END: #+BEGIN_SRC emacs-lisp (use-package company :ensure t :diminish company-mode :hook (prog-mode . company-mode) (tex-mode . company-mode) (special-mode . company-mode) (comint-mode . company-mode) (slime-repl-mode . company-mode) :bind ("TAB" . #'company-indent-or-complete-common) ("C-i" . #'company-indent-or-complete-common) (:map company-active-map ("C-c" . company-select-previous) ("C-r" . company-select-next) ("C-h" . backward-delete-char) ("C-w" . backward-kill-word) ;; the following is necessary to shadow the window-switching ;; keybindings on the same keys ("C-1" . company-complete-quick-access) ("C-2" . company-complete-quick-access) ("C-3" . company-complete-quick-access) ("C-4" . company-complete-quick-access) ("C-5" . company-complete-quick-access) ("C-6" . company-complete-quick-access) ("C-7" . company-complete-quick-access) ("C-8" . company-complete-quick-access) ("C-9" . company-complete-quick-access) ("C-0" . company-complete-quick-access) ("" . #'company-complete-common-or-cycle) ("TAB" . #'company-complete-common-or-cycle) ("C-i" . #'company-complete-common-or-cycle)) ;; Error (use-package): company/:catch: Symbol’s value as variable is void: c-mode-map ;; (:map c-mode-map ;; ("TAB" . company-indent-or-complete-common) ;; ("C-i" . company-indent-or-complete-common)) :config (add-to-list 'company-backends 'company-irony) (setq company-show-quick-access t company-idle-delay nil) ;; do not pop up suggestions automatically (customize-set-variable company-quick-access-modifier 'control)) #+END_SRC ** company-emoji :PROPERTIES: :CUSTOM_ID: company-emoji :END: #+BEGIN_SRC emacs-lisp (defun --set-emoji-font (frame) "Adjust the font settings of FRAME so Emacs can display emoji properly." (set-fontset-font t 'symbol (font-spec :family "Symbola") frame 'prepend)) (defun my-company-idle () "Enable `company-idle-delay' in the current buffer." ;; (make-local-variable 'company-idle-delay) (with-current-buffer (current-buffer) (setq-local company-idle-delay 0.2))) (use-package company-emoji :ensure t :hook (text-mode . company-mode) (text-mode . my-company-idle) :init (company-emoji-init) (--set-emoji-font nil) ;; For when Emacs is started in GUI mode: (add-hook 'after-make-frame-functions '--set-emoji-font)) ;; Hook for when a frame is created with emacsclient #+END_SRC ** company-prescient :PROPERTIES: :CREATED: 2022-01-13T22:42:28+0530 :CUSTOM_ID: company-prescient :END: #+BEGIN_SRC emacs-lisp (use-package company-prescient :ensure t :init (company-prescient-mode) (prescient-persist-mode)) #+END_SRC ** yasnippet :PROPERTIES: :CREATED: 2022-01-05T20:16:31+0530 :CUSTOM_ID: yasnippet :END: #+BEGIN_SRC emacs-lisp (use-package yasnippet :disabled t :ensure t :commands (yas-expand-snippet) :hook (prog-mode . yas-minor-mode-on) (comint-mode . yas-minor-mode-on) (slime-repl-mode . yas-minor-mode-on) :bind (:map yas-minor-mode-map ("" . nil) ("TAB" . nil) ;; use TAB only for completion ("C-n" . #'yas-expand)) (:map yas-keymap ("C-n" . #'yas-next-field-or-maybe-expand))) #+END_SRC ** tempel :PROPERTIES: :CUSTOM_ID: tempel :END: #+BEGIN_SRC emacs-lisp (use-package tempel :bind ("M-+" . tempel-complete) ;; Alternative tempel-expand ("M-*" . tempel-insert) ("C-n" . tempel-expand) (:map tempel-map ("" . tempel-next) ("" . tempel-previous))) #+END_SRC ** vertico :PROPERTIES: :CREATED: 2022-01-15T18:44:49+0530 :CUSTOM_ID: vertico :END: #+BEGIN_SRC emacs-lisp (use-package vertico :ensure t :init (vertico-mode) (vertico-indexed-mode) (vertico-multiform-mode) (vertico-mouse-mode) ;; Different scroll margin ;; (setq vertico-scroll-margin 0) ;; Show more candidates ;; (setq vertico-count 20) ;; Grow and shrink the Vertico minibuffer ;; (setq vertico-resize t) :bind (:map vertico-map ("C-h" . #'vertico-directory-delete-char) ("" . #'vertico-directory-delete-char) ("C-w" . #'vertico-directory-delete-word) ("" . #'vertico-directory-delete-word) ("" . #'vertico-scroll-down) ("" . #'vertico-scroll-up) ("C-RET" . #'vertico-exit-input) ("" . #'vertico-exit-input)) :config (setq completion-category-defaults nil completion-category-overrides nil vertico-multiform-commands '((chronometrist-toggle-task (vertico-sort-function . nil)) (chronometrist-toggle-task-no-hooks (vertico-sort-function . nil)) (chronometrist-toggle-task-button (vertico-sort-function . nil)) (chronometrist-key-values-unified-prompt (vertico-sort-function . nil)) (consult-line (vertico-sort-function . nil)) (my-consult-line (vertico-sort-function . nil))))) #+END_SRC #+BEGIN_SRC emacs-lisp ;; Persist history over Emacs restarts. Vertico sorts by history position. (use-package savehist :init (savehist-mode)) #+END_SRC #+BEGIN_SRC emacs-lisp ;; A few more useful configurations... (use-package emacs :init ;; Add prompt indicator to `completing-read-multiple'. ;; Alternatively try `consult-completing-read-multiple'. (defun crm-indicator (args) (cons (concat "[CRM] " (car args)) (cdr args))) (advice-add #'completing-read-multiple :filter-args #'crm-indicator) ;; Do not allow the cursor in the minibuffer prompt (setq minibuffer-prompt-properties '(read-only t cursor-intangible t face minibuffer-prompt)) (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode) ;; Emacs 28: Hide commands in M-x which do not work in the current mode. ;; Vertico commands are hidden in normal buffers. ;; (setq read-extended-command-predicate ;; #'command-completion-default-include-p) ;; Enable recursive minibuffers (setq enable-recursive-minibuffers t)) #+END_SRC ** consult :PROPERTIES: :CREATED: 2022-01-15T21:32:45+0530 :CUSTOM_ID: consult :END: #+BEGIN_SRC emacs-lisp (use-package consult :ensure t) (defun my-consult-line () "Like `consult-line', but use symbol at point or active region." (interactive) (let ((string (cond ((region-active-p) (buffer-substring-no-properties (region-beginning) (region-end))) ((or (thing-at-point 'symbol) (thing-at-point 'url) (thing-at-point 'word))) (t nil)))) (consult-line string))) #+END_SRC ** counsel :disabled: :PROPERTIES: :CUSTOM_ID: counsel :END: #+BEGIN_SRC emacs-lisp (use-package counsel :disabled t :ensure t :bind ("M-x" . counsel-M-x) :config (setq counsel-find-file-ignore-regexp "\\`\\.")) #+END_SRC ** ivy :disabled: :PROPERTIES: :CUSTOM_ID: ivy :END: #+BEGIN_SRC emacs-lisp (use-package ivy :disabled t :ensure t :commands ivy-mode :init (ivy-rich-mode) ;; display command docstrings in `counsel-M-x' :bind (:map ivy-minibuffer-map ("C-h" . ivy-backward-delete-char) ("C-c" . previous-line) ("C-r" . next-line) ("M-c" . ivy-previous-history-element) ("M-r" . ivy-next-history-element)) :config (setq ivy-re-builders-alist '((t . ivy--regex-ignore-order)))) #+END_SRC ** ivy-xref :disabled: :PROPERTIES: :CUSTOM_ID: ivy-xref :END: #+BEGIN_SRC emacs-lisp (use-package ivy-xref :disabled t :ensure t :config (setq xref-show-definitions-function #'ivy-xref-show-defs)) #+END_SRC ** flx-ido :disabled: :PROPERTIES: :CUSTOM_ID: flx-ido :END: #+BEGIN_SRC emacs-lisp (use-package flx-ido :disabled :init (flx-ido-mode 1) (setq ido-enable-flex-matching t ido-use-faces nil)) #+END_SRC ** flx-isearch :disabled: :PROPERTIES: :CUSTOM_ID: flx-isearch :END: #+BEGIN_SRC emacs-lisp (use-package flx-isearch :disabled :bind ("C-s" . #'flx-isearch-forward) ("C-r" . #'flx-isearch-backward)) ;; (use-package flycheck ;; :init (global-flycheck-mode)) ;; (use-package flycheck-elsa ;; :hook (emacs-lisp-mode . flycheck-elsa-setup)) #+END_SRC ** flex-isearch :disabled: :PROPERTIES: :CREATED: 2022-01-13T20:50:41+0530 :CUSTOM_ID: flex-isearch :END: #+BEGIN_SRC emacs-lisp (use-package flex-isearch :disabled t :init (flex-isearch-mode 1) :bind ("C-M-s" . flex-isearch-forward) ("C-M-r" . flex-isearch-backward)) #+END_SRC ** bookmarks :PROPERTIES: :CUSTOM_ID: bookmarks :END: #+BEGIN_SRC emacs-lisp (use-package bookmark :config (setq bookmark-save-flag 1)) #+END_SRC ** recentf :PROPERTIES: :CUSTOM_ID: recentf :END: #+BEGIN_SRC emacs-lisp (use-package recentf :init ;; default is to cleanup when `recentf-mode' is enabled, which we don't want (setq recentf-auto-cleanup 'never) (recentf-mode 1) :bind ("C-x C-r C-o" . recentf-open-files) :config (setq recentf-max-menu-items 500 recentf-max-saved-items 1000 recentf-save-file (locate-user-emacs-file "recentf") recentf-exclude '("\\.html\\(\\.orig\\)?$" "\\.jpe?g$" "\\.png$" "\\.mp4$" "\\.etc" "\\.umstuff")) (advice-add 'recentf-save-list :before (lambda (&rest args) (async-backup recentf-save-file))) ;; I'd rather run cleanup manually, if ever required; running ;; cleanup automatically is a recipe for periodically losing your ;; recentf entries when your disk happens to be unmounted. ;; :hook (kill-emacs . recentf-cleanup) ) #+END_SRC * misc keybindings :PROPERTIES: :CUSTOM_ID: misc-keybindings :END: #+BEGIN_SRC emacs-lisp (global-set-key (kbd "M-w") 'kill-ring-save) (define-key emacs-lisp-mode-map (kbd "M-w") nil) ;; ;; M-d is useful in the minibuffer ;; (define-key emacs-lisp-mode-map (kbd "M-d") nil) ;; (global-set-key (kbd "M-d") 'easy-kill-delete-region) #+END_SRC * environment variables :PROPERTIES: :CUSTOM_ID: environment-variables :END: #+BEGIN_SRC emacs-lisp (setenv "PATH" (concat "~/bin:" (getenv "PATH"))) (setenv "EDITOR" "emacsclient") ;; what on earth is this message after every init - ;; ad-handle-definition: `tramp-read-passwd' got redefined ;; ;; (profiler-start 'cpu) ;; (toggle-debug-on-error) ;; ;; (toggle-debug-on-quit) #+END_SRC * Linewrapping :PROPERTIES: :CUSTOM_ID: linewrapping :END: #+BEGIN_SRC emacs-lisp (add-hook 'erc-mode-hook 'visual-line-mode) (add-hook 'text-mode-hook 'visual-line-mode) (use-package visual-fill-column :ensure t :hook (markdown-mode . visual-fill-column-mode) (markdown-mode . visual-line-mode) (org-mode . visual-line-mode) ;; (org-mode . visual-fill-column-mode) (message-mode . visual-fill-column-mode)) (use-package adaptive-wrap :ensure t :hook (markdown-mode . adaptive-wrap-prefix-mode)) #+END_SRC * Navigation :PROPERTIES: :CUSTOM_ID: navigation :END: #+BEGIN_SRC emacs-lisp ;; (require 'view) ;; (cp-set-keys ;; :bindings ;; `((,(kbd "C-v") View-scroll-half-page-forward) ;; (,(kbd "M-v") View-scroll-half-page-backward))) ;; ;; (cp-set-keys ;; ;; :bindings ;; ;; `((,(kbd "C-v") scroll-up) ;; ;; (,(kbd "M-v") scroll-down))) ;;;; Need to make maps for ;;;; mark-* commands (-sexp, -page, -word, etc) ;;;; mark ring ;;;; Other custom keys (bind-keys ("C-x C-1" . delete-other-windows) ("C-x C-2" . split-window-below) ("C-x C-3" . split-window-right) ("C-x C-0" . delete-window) ("C-x C-4 C-f" . find-file-other-window) ;; ("C-`" . point-to-register) ;; ("C-'" . jump-to-register) ("C-x C-d" . dired-jump) ;; I usually keep Super for the window manager and global hotkeys... ("s-i" . imenu) ;; keypad ;; set this to run whatever command is bound to C-c C-c, or maybe just C-c ;; see (info "(elisp)Translation Keymaps") ) (with-eval-after-load 'dired (define-key dired-mode-map (kbd "b") 'dired-up-directory) ;; (add-hook 'dired-mode-hook 'visual-line-mode) ;(global-set-key (kbd "C-,") 'string-rectangle 'TAB) ) (global-unset-key (kbd "C-x d")) ;; (require 'dired-toggle-sudo) ;; (define-key dired-mode-map (kbd "C-c C-s") 'dired-toggle-sudo) ;; (eval-after-load 'tramp ;; '(progn ;; ;; Allow to use: /sudo:user@host:/path/to/file ;; (add-to-list 'tramp-default-proxies-alist ;; '(".*" "\\`.+\\'" "/ssh:%h:")))) (global-set-key (kbd "C-x df") 'delete-frame) ;;;; make-frame key is after Helm's config ;(setq compilation-read-command nil) (defun cp-open-init () "Open the init.el file." (interactive) (find-file "~/.emacs.d/init.el")) ;; todo - store current file name → kill the buffer → ;; find-file-literally with the stored filename. also, find out if you ;; can open it without modes but with the proper encoding. (defun cp-fcf-literally () "Find the current file literally. Like find-file-literally but does not prompt for a file name." (interactive) (find-file-literally (buffer-file-name))) (defun cp-kill-buffer () "Kill the current buffer without prompting." (interactive) (kill-buffer nil)) ;; (require 'win-switch) ;; (global-set-key "\C-xo" 'win-switch-dispatch) ;; (win-switch-setup-keys-ijkl) ;; (setq win-switch-idle-time 0.5) ;; (setq win-switch-other-window-first nil) ;; ;(setq win-switch-other-window-first nil) (defun cp-maximize-window () "Run maximize-window if more than one window is present." (interactive) (if (> (length (window-list)) 1) (maximize-window))) (defun cp-minimize-window () "Run minimize-window if more than one window is present." (interactive) (if (> (length (window-list)) 1) (minimize-window))) (with-eval-after-load 'doc-view (define-key doc-view-mode-map (kbd "=") 'doc-view-fit-height-to-window)) (use-package view-mode :bind (:map view-mode-map ("x" . nil) ("" . scroll-down-command) ("SPC" . scroll-up-command))) (use-package avy :ensure t :config (setq avy-case-fold-search nil)) #+END_SRC ** window-numbering :PROPERTIES: :CUSTOM_ID: window-numbering :END: #+BEGIN_SRC emacs-lisp (use-package window-numbering :ensure t :commands (window-numbering-mode) :init (window-numbering-mode 1) :bind (:map window-numbering-keymap ("M-1" . nil) ("M-2" . nil) ("M-3" . nil) ("M-4" . nil) ("M-5" . nil) ("M-6" . nil) ("M-7" . nil) ("M-8" . nil) ("M-9" . nil) ("C-1" . select-window-1) ("C-2" . select-window-2) ("C-3" . select-window-3) ("C-4" . select-window-4) ("C-5" . select-window-5) ("C-6" . select-window-6) ;; for one-handed operation with the right hand ("C-7" . select-window-1) ("C-8" . select-window-2) ("C-9" . select-window-3))) #+END_SRC ** ido-mini :disabled:buffer:file: :PROPERTIES: :CUSTOM_ID: ido-mini :END: This needs to be before =boon=/=exwm=, or you get a "failed to define function ido-mini" error when you press the keybinding. #+BEGIN_SRC emacs-lisp (use-package ido-mini :disabled t :demand :load-path "~/.emacs.d/contrapunctus/ido-mini/" :bind ("C-x C-l" . ido-mini)) #+END_SRC ** outline-minor-mode :PROPERTIES: :CUSTOM_ID: outline-minor-mode :END: #+BEGIN_SRC emacs-lisp (use-package outline :hook (adoc-mode . outline-minor-mode) (gemini-mode . outline-minor-mode) ;; Does this cause this error? ;; Polymode error (pm--mode-setup ’org-mode): ‘recenter’ing a window that does not display current-buffer. ;; (outline-view-change . (lambda () (recenter 3))) :bind (:map outline-minor-mode-map ("TAB" . contrapunctus-outline-indent-or-toggle-children) ;; this (rather than `outline-show-children') seems to ;; be the equivalent of `org-show-subtree' ("C-c C-k" . outline-show-branches) ;; wow, in adoc-mode these also replace the first letter of the heading with an "e"... 😒 ;; ("C-c ," . outline-promote) ;; ("C-c ." . outline-demote) ) ("C-c ," . adoc-demote) ("C-c ." . adoc-promote) ("C-c C-c" . outline-previous-heading) ("C-c C-r" . outline-next-heading) ("C-c C-h" . nil) ("C-c C-h" . outline-up-heading)) #+END_SRC #+BEGIN_SRC emacs-lisp (defun contrapunctus-outline-indent-or-toggle-children () (interactive) (if (save-excursion (goto-char (point-at-bol)) (looking-at-p outline-regexp)) (outline-toggle-children) (indent-for-tab-command))) #+END_SRC ** outshine :PROPERTIES: :CUSTOM_ID: outshine :END: #+BEGIN_SRC emacs-lisp (use-package outshine :commands (outshine-mode outshine-cycle) :hook (texinfo-mode . outshine-mode)) #+END_SRC * scrolling :PROPERTIES: :CUSTOM_ID: scrolling :END: While we're at it, let's add that to next-error as well (this affects jumping to match from =M-x grep= , too) #+BEGIN_SRC emacs-lisp (add-hook 'next-error-hook 'recenter) #+END_SRC * Buffer management :PROPERTIES: :CUSTOM_ID: buffer-management :END: #+BEGIN_SRC emacs-lisp (defun my-buffer-switch () (interactive) (let ((buffers (remove-if (lambda (buffer) (or (string-match-p "^ " (buffer-name buffer)) (get-buffer-window-list buffer))) (buffer-list)))) (switch-to-buffer (first buffers)))) #+END_SRC ** ibuffer :buffer: :PROPERTIES: :CUSTOM_ID: ibuffer :END: #+BEGIN_SRC emacs-lisp (use-package ibuffer :bind (("C-x C-b" . ibuffer) :map ibuffer-mode-map ("m" . nil) ("U" . nil) ("m f" . ibuffer-mark-by-file-name-regexp) ("m m" . ibuffer-mark-by-mode-regexp) ("m n" . ibuffer-mark-by-name-regexp) ("M" . ibuffer-mark-forward) ("r" . ibuffer-do-replace-regexp) ("U" . ibuffer-unmark-all) ("X" . 'ibuffer-do-kill-on-deletion-marks)) ;; Boon hijacks the x key.ibuffer-set-filter-groups-by-mode :config (setq ibuffer-show-empty-filter-groups nil) (setq-default ibuffer-current-format 2)) #+END_SRC #+BEGIN_SRC emacs-lisp (general-define-key "C-x k" 'cp-kill-buffer "C-`" 'shell "M-`" 'eshell "M-" 'compile "M-" 'run-chicken "M-" 'run-lisp "M-" 'ielm) #+END_SRC #+BEGIN_SRC emacs-lisp (general-define-key :prefix "" "M" 'describe-mode "m" 'man "l" 'find-library) #+END_SRC #+BEGIN_SRC emacs-lisp (general-define-key :prefix "" ;; "" 'imenu "" 'xref-find-definitions "r" 'xref-find-references "m" 'imenu ;; "p" 'grep "o" 'find-grep "i" 'find-dired "h" 'proced) #+END_SRC #+BEGIN_SRC emacs-lisp (general-define-key :prefix "" "" 'eval-buffer "i" 'cp-open-init "v" 'visual-line-mode "f" 'cp-fcf-literally "f" 'fundamental-mode "t" 'text-mode "T" 'cp-insert-timestamp "d" 'cp-insert-date "c" 'calendar "p" 'list-packages) #+END_SRC #+BEGIN_SRC emacs-lisp (general-define-key ;; [down-mouse-1] 'mouse-set-point ;; [up-mouse-1] 'er/expand-region [s-mouse-3] 'bury-buffer [mouse-8] 'delete-window ;; (kbd "") 'keyboard-quit ;; [mouse-9] 'buffer-menu [mouse-9] 'ibuffer [C-mouse-9] 'recentf-open-files [M-mouse-4] 'next-buffer [M-mouse-5] 'previous-buffer [M-mouse-8] 'split-window-right [M-mouse-9] 'split-window-below ;; quitting from helm-mini - whether with keyboard-quit or ;; keyboard-escape-quit - "banishes" the mouse pointer to the ;; top-right corner!? wtf, helm. ;; (kbd "s-") 'helm-mini ) #+END_SRC ** ibuffer-sidebar :PROPERTIES: :CUSTOM_ID: ibuffer-sidebar :END: #+BEGIN_SRC emacs-lisp (use-package ibuffer-sidebar :ensure t :bind (:map ibuffer-name-map ([(mouse-1)] . ibuffer-mouse-visit-buffer) ([down-mouse-3] . nil) ([(mouse-3)] . ibuffer-mouse-toggle-mark)) :config (setq ibuffer-sidebar-width 25)) #+END_SRC ** midnight-mode (automatic buffer cleanup) :PROPERTIES: :CUSTOM_ID: midnight-mode-(automatic-buffer-cleanup) :END: #+BEGIN_SRC emacs-lisp (use-package midnight :init (midnight-mode) :config (setq clean-buffer-list-kill-regexps '("") clean-buffer-list-delay-general 1 clean-buffer-list-delay-special (* 60 60 12)) (add-to-list 'clean-buffer-list-kill-never-buffer-names "Gajim") :hook (midnight . clean-buffer-list)) #+END_SRC * browse-url :PROPERTIES: :CUSTOM_ID: browse-url :END: SLIME opens CLHS links in Firefox, but I'd rather open them in Tor Browser; Tor Browser, however, does not permit other applications to open tabs in a running instance. So I wrote this to copy the links automatically instead. #+BEGIN_SRC emacs-lisp (use-package browse-url :config (defun cp-copy-url (url &rest args) (with-temp-buffer (insert url) (kill-ring-save (point-min) (point-max))) (message "Copied %s to kill ring" url)) (setq browse-url-browser-function #'cp-copy-url)) #+END_SRC * Markup :PROPERTIES: :CUSTOM_ID: markup :END: ** Org :PROPERTIES: :CUSTOM_ID: org :END: #+BEGIN_SRC emacs-lisp (use-package org :quelpa (org :fetcher git :url "https://git.savannah.gnu.org/git/emacs/org-mode.git" :files (:defaults "lisp")) :hook (org-mode . contrapunctus-disable-nameless-key) (org-mode . auto-revert-mode) :commands (org-drag-line-backward org-drag-line-forward) :bind (("C-c C-h" . nil) :map org-mode-map ("C-c C-c" . org-previous-visible-heading) ("C-c C-r" . org-next-visible-heading) ("C-c C-s" . org-forward-heading-same-level) ;; does not work... ("C-c C-h" . org-backward-heading-same-level) ("C-c C-j" . org-ctrl-c-ctrl-c) ;; "c c" in boon/dvorak ([mouse-1] . org-cycle) ("" . org-global-cycle)) :config (general-def " o" 'org-mode) (general-def org-mode-map "C-," 'nil "M-r" 'org-metadown "M-c" 'org-metaup "C-c C--" 'org-ctrl-c-minus "C-c C-," 'org-metaleft "C-c C-." 'org-metaright ;; "C-j" 'org-return ;; "C-m" 'org-return-indent "C-c C-9" 'org-mark-ring-goto "C-c C-/" 'org-sparse-tree "C-M-x" 'cp/eval-sexp "C-c C-o" ;; 'my-org-open 'org-open-at-point ;; boon "C-c C--" 'org-ctrl-c-minus "C-c ," 'org-metaleft "C-c ." 'org-metaright "M-h" 'default-indent-new-line) (mapc (lambda (pair) (add-to-list 'org-file-apps pair)) '(("txt" . emacs) ("org" . emacs) ("pdf" . emacs) ;; (t . "xdg-open %s") )) (setq org-todo-keywords '((sequence "TODO" "RESEARCH" "STARTED" "DONE")) org-image-actual-width 400 org-cycle-include-plain-lists 'integrate org-link-search-must-match-exact-headline nil org-html-head "" org-html-self-link-headlines t org-export-default-inline-image-rule `(("https" . ,(format "\\.%s\\'" (regexp-opt '("png" "jpeg" "jpg" "gif" "tiff" "tif" "xbm" "xpm" "pbm" "pgm" "ppm" "webp") t))) ("file" . ,(format "\\.%s\\'" (regexp-opt '("png" "jpeg" "jpg" "gif" "tiff" "tif" "xbm" "xpm" "pbm" "pgm" "ppm" "webp") t)))) org-link-file-path-type 'relative ;; https://emacs.stackexchange.com/questions/18877/how-to-indent-without-the-two-extra-spaces-at-the-beginning-of-code-blocks-in-or org-edit-src-content-indentation 0 org-src-fontify-natively t org-src-window-setup 'current-window org-src-strip-leading-and-trailing-blank-lines t org-src-preserve-indentation t org-src-tab-acts-natively t ;; https://emacs.stackexchange.com/questions/20759/all-org-subheadings-in-imenu ;; I ended up using consult-org-heading instead org-goto-interface 'outline-path-completion org-outline-path-complete-in-steps nil) (org-link-set-parameters "gemini" :export #'my-export-gemini-link) (org-link-set-parameters "xmpp" :export #'my-export-xmpp-link)) (defun contrapunctus-disable-nameless-key () (define-key nameless-mode-map (kbd "C-c C--") nil)) #+END_SRC *** gemini: and xmpp: links :PROPERTIES: :CUSTOM_ID: gemini-and-xmpp-links :END: https://lists.gnu.org/archive/html/emacs-orgmode/2018-02/msg00082.html #+BEGIN_SRC emacs-lisp (defun my-export-link-helper (link desc format) (cond ((eq format 'html) (format "%s" link desc)) ((eq format 'latex) (if desc (format "\\href{%s}{%s}" link desc) (format "\\href{%s}" link))) (t ;; `ascii', `md', `hugo', etc. (format "[%s](%s)" desc link)))) #+END_SRC #+BEGIN_SRC emacs-lisp (defun my-export-gemini-link (link desc format) "Create export version of LINK and DESC to FORMAT." (let ((link (concat "gemini:" link))) (my-export-link-helper link desc format))) #+END_SRC #+BEGIN_SRC emacs-lisp (defun my-export-xmpp-link (link desc format) "Create export version of LINK and DESC to FORMAT." (let ((link (concat "xmpp:" link))) (my-export-link-helper link desc format))) #+END_SRC *** org-emms :PROPERTIES: :CUSTOM_ID: org-emms :END: #+BEGIN_SRC emacs-lisp (use-package org-emms :after org) #+END_SRC *** my-org-insert-block :PROPERTIES: :CUSTOM_ID: my-org-insert-block :END: #+BEGIN_SRC emacs-lisp (defun my-org-insert-block (type &optional lang header-args ask) "Insert block of TYPE at point, or at beginning and end of region. TYPE should be an Org block type, e.g. SRC, QUOTE, etc. If TYPE is SRC, LANG should be the name of the language as a string, e.g. \"emacs-lisp\". If TYPE is EXPORT, LANG should be the name of the export format as a string, e.g. \"html\". If LANG is not supplied, use the value of `my-org-src-default-lang'. If `my-org-src-default-lang' is nil, or with ASK, prompt the user for a language." (let* ((src-block-p (equal type "SRC")) (export-block-p (equal type "EXPORT")) (ask-lang-p (or (not lang) ask)) (lang (cond ((stringp lang) lang) ((and (bound-and-true-p my-org-src-default-lang) (stringp my-org-src-default-lang) (not ask)) my-org-src-default-lang) ((and src-block-p ask-lang-p) (completing-read "Source block language: " (mapcar #'cl-first org-src-lang-modes))) ((and export-block-p ask-lang-p) (completing-read "Export block format: " (mapcar #'symbol-name org-export-backends))) (t nil))) (column (- (point) (point-at-bol))) (indent (make-string column ?\s)) (start-string (format "#+BEGIN_%s %s%s\n" type (if (or src-block-p export-block-p) lang "") (if (and src-block-p header-args) (format " %s" header-args) ""))) (end-string (format "%s#+END_%s\n" indent type))) ;; create a block around a region - preserve position of point (cond ((region-active-p) (let ((region-start (region-beginning)) (region-end (region-end))) (save-excursion ;; inserting at region-start would make region-end ;; invalid, so we insert at the end first (goto-char region-end) (insert end-string "\n") (goto-char region-start) (insert start-string)))) ;; new empty block - insert the block syntax and place point ;; inside the block (t (insert start-string) (let ((point-inside-block (point))) (insert "\n" end-string) (goto-char point-inside-block) (insert indent)))))) (add-to-list 'org-src-lang-modes '("lisp" . lisp-mode)) #+END_SRC #+BEGIN_SRC emacs-lisp ;; (use-package org-src-mode ;; :hook (org-src-mode . (lambda () (when (derived-mode-p 'emacs-lisp-mode))))) ;; (defun my-org-hydra-expand-all () ;; (interactive) ;; ;; todo - define inner recursive function ;; (beginning-of-buffer) ;; ;; todo - check if we are on a heading ;; (org-forward-heading-same-level) ;; ()) #+END_SRC *** my-org-open :PROPERTIES: :CUSTOM_ID: my-org-open :END: #+BEGIN_SRC emacs-lisp ;; I dislike having to navigate within a line to reach a link - with ;; this command I just need to be on the same line as the link. (defun my-org-open (&optional arg reference-buffer) (interactive "P") (beginning-of-visual-line) (unless (looking-at-p (rx-to-string '(or "http" "["))) (org-next-link)) (org-open-at-point)) #+END_SRC *** my-org-table-convert-tsv :PROPERTIES: :CUSTOM_ID: my-org-table-convert-tsv :END: #+BEGIN_SRC emacs-lisp (defun my-org-table-convert-tsv () (interactive) (with-output-to-temp-buffer "my-org-table-convert-tsv" (->> (buffer-substring-no-properties (region-beginning) (region-end)) (replace-regexp-in-string "^| *" "") (replace-regexp-in-string " *| *" " ") (replace-regexp-in-string "^-.*$" ""))) (with-current-buffer "my-org-table-convert-tsv" (remove-hook 'before-save-hook 'delete-trailing-whitespace) (write-file (read-from-minibuffer "Output filename: ")))) #+END_SRC *** my-org-set-tags - using completing-read-multiple for setting tags :PROPERTIES: :CUSTOM_ID: my-org-set-tags :END: #+BEGIN_SRC emacs-lisp (defun my-org-set-tags () (interactive) (let ((all-tags (org-get-buffer-tags)) (current-tags (org-get-tags))) (save-excursion (org-back-to-heading) (org-set-tags (completing-read-multiple "Tag: " all-tags nil 'confirm (mapconcat #'identity current-tags ",") 'org-tags-history))))) #+END_SRC *** #+BEGIN_SRC emacs-lisp (defun cp/marked-files->markup-links-org (filenames) (mapcar (lambda (filename) (let ((link-pre "[[file:") (link-post "][]]\n")) (concat link-pre filename link-post))) filenames)) #+END_SRC *** my-org-insert-timestamp :PROPERTIES: :CUSTOM_ID: my-org-insert-timestamp :END: #+BEGIN_SRC emacs-lisp (defun my-org-insert-timestamp () (save-excursion (org-set-property "CREATED" (format-time-string "%FT%T%z")))) (use-package org-indent :hook (org-mode . org-indent-mode)) (with-eval-after-load 'ox (require 'ox-texinfo)) ;; ideally, I'd load this in :before advice for org-export... #+END_SRC *** load org-babel languages on demand :PROPERTIES: :CUSTOM_ID: load-org-babel-languages-on-demand :END: From https://emacs.stackexchange.com/questions/20577/org-babel-load-all-languages-on-demand #+BEGIN_SRC emacs-lisp (defadvice org-babel-execute-src-block (around load-language nil activate) "Load language if needed" (let ((language (org-element-property :language (org-element-at-point)))) (unless (cdr (assoc (intern language) org-babel-load-languages)) (add-to-list 'org-babel-load-languages (cons (intern language) t)) (org-babel-do-load-languages 'org-babel-load-languages org-babel-load-languages)) ad-do-it)) #+END_SRC *** my-org-fix-newlines :PROPERTIES: :CUSTOM_ID: my-org-fix-newlines :END: #+BEGIN_SRC emacs-lisp (defun my-org-fix-newlines () "Insert newlines around Org headings. Newlines are inserted before headings (unless immediately preceded by another heading) and after headings." (interactive) (unless (org-at-heading-p) (outline-next-heading)) (org-map-tree (lambda () (save-excursion (forward-line -1) (goto-char (point-at-bol)) (unless (or (looking-at-p "^$") (org-at-heading-p)) (goto-char (point-at-eol)) (insert "\n"))) (save-excursion (forward-line) (while (org-at-drawer-p) (forward-line)) (goto-char (point-at-bol)) (when (looking-at-p "^$") (join-line))))) (outline-next-visible-heading 1)) #+END_SRC *** org-html-themify :PROPERTIES: :CUSTOM_ID: org-html-themify :END: #+BEGIN_SRC emacs-lisp (use-package org-html-themify :disabled t :load-path "~/.emacs.d/elisp-git/org-html-themify/" :load-path "~/.emacs.d/elisp-git/hexrgb/" :hook (org-mode . org-html-themify-mode) :config (setq org-html-themify-themes '((dark . doom-molokai)))) #+END_SRC *** ox-publish :PROPERTIES: :CUSTOM_ID: ox-publish :END: #+BEGIN_SRC emacs-lisp (use-package ox-publish :config (require 'ox-gemini) (setq org-publish-project-alist '(("tilde.team-html" :base-directory "/media/data/contrapunctus/Documents/Text Files/homepage/contrapunctus/org/" :publishing-directory "/ssh:contrapunctus@tilde.team:/home/contrapunctus/public_html/" :recursive t :publishing-function org-html-publish-to-html) ("tilde.team-gmi" :base-directory "/media/data/contrapunctus/Documents/Text Files/homepage/contrapunctus/org/" :publishing-directory "/ssh:contrapunctus@tilde.team:/home/contrapunctus/public_gemini/" :recursive t :publishing-function org-gemini-publish-to-gemini)))) (defun my-org-publish-html (plist filename pub-dir) ) #+END_SRC *** org-superstar :PROPERTIES: :CUSTOM_ID: org-superstar :END: #+BEGIN_SRC emacs-lisp (use-package org-superstar :hook (org-mode . org-superstar-mode)) #+END_SRC *** auto-id :PROPERTIES: :CUSTOM_ID: auto-id :END: #+BEGIN_SRC emacs-lisp (use-package auto-id :load-path "/media/data/contrapunctus/Documents/Text Files/programming/elisp/auto-id/" :commands (auto-id-mode)) #+END_SRC ** markdown-mode :editing: :PROPERTIES: :CUSTOM_ID: markdown-mode :END: #+BEGIN_SRC emacs-lisp (use-package markdown-mode :ensure t :mode "\\.md\\'" :hook (markdown-mode . (lambda () (add-hook 'before-save-hook 'markdown-cleanup-list-numbers 0 t))) (markdown-mode . markdown-display-inline-images) :config (setq markdown-command "cmark" markdown-css-paths '("style.css") markdown-display-remote-images t markdown-max-image-size '(500 . 500) ;; reflows text to suit different screens markdown-xhtml-header-content (concat "")) (when (featurep 'boon) (general-def markdown-mode-map "C-c ," 'markdown-promote "C-c ." 'markdown-demote "C-c C-e" 'markdown-export)) (setq-default ;; markdown-hide-markup t ;; has a bug with heading cycling markdown-hide-urls t) :bind (:map markdown-mode-map ;; ("M-n" . org-drag-element-forward) ;; ("M-p" . org-drag-element-backward) ;; ("C-c C-o" . markdown-follow-link-at-point) ("M-r" . markdown-move-down) ("M-c" . markdown-move-up) ("C-c C--" . org-cycle-list-bullet) ([mouse-1] . markdown-cycle) ("C-c C-x C-n" . markdown-next-link) ("C-c C-x C-p" . markdown-previous-link) ("C-c C-h C-u" . #'markdown-toggle-url-hiding) ("C-c C-h C-m" . #'markdown-toggle-markup-hiding) ("C-c C-h C-i" . #'markdown-toggle-inline-images) ("C-c C-r" . #'reverse-region) ("M-w" . cp/copy-md-link) ("TAB" . markdown-cycle) ("C-c C-." . markdown-demote) ("C-c C-," . markdown-promote) ("C-c C-l" . markdown-insert-link))) (defun cp/copy-line-or-md-link (prefix-arg) (interactive "P") (save-excursion (beginning-of-line) (if (looking-at-p ".*http") (progn (cp/re-search-line "http") (forward-char -4) (kill-new (thing-at-point 'url)))))) (with-eval-after-load 'markdown-mode (defun cp/copy-md-link (prefix-arg) "Copy address of Markdown link after point in the current line. If there is no link in the current line, or if the region is active, or with a prefix arg - call whole-line-or-region-kill-ring-save instead." (interactive "P") (save-excursion (if (or (use-region-p) prefix-arg (not (cp/re-search-line ;; 2018-03-21T22:47:55+0530 - fix bug where a line with parenthesized text would not be copied ;; "\(" ;; 2018-07-22T10:20:03+0530 ;; "\\[.*?\\](.*?)" "\\[.*?\\](" ))) (whole-line-or-region-kill-ring-save prefix-arg) (let ((point-a (point))) (forward-char -1) (forward-sexp) (copy-region-as-kill point-a (- (point) 1))))))) (defun cp/copy-bus-entry () "For personal use, when working between Markdown and the OSM wiki." (interactive) (let ((point-a (region-beginning)) (point-b (region-end)) (point-b-line (line-number-at-pos))) (query-replace "[ ]" "☐" nil point-a point-b) (query-replace "[x]" "☑" nil point-a point-b) (query-replace-regexp "^[\\*-] " "::" nil point-a point-b) (query-replace-regexp "^### " ":" nil point-a point-b) (goto-char (point-min)) (forward-line (- point-b-line 1)) (copy-region-as-kill point-a (point-at-bol)))) ;; 2018-08-21T03:41:47+0530 (defun cp/copy-md-link (prefix-arg) "Copy address of Markdown link after point in the current line. If there is no link in the current line, or if the region is active, or with a prefix arg - call whole-line-or-region-kill-ring-save instead." (interactive "P") (save-excursion (cond ((or (use-region-p) prefix-arg) (whole-line-or-region-kill-ring-save prefix-arg)) ((cp/re-search-line "\\[.*?\\](") (let ((point-a (point))) (forward-char -1) (forward-sexp) (copy-region-as-kill point-a (- (point) 1)))) ((cp/re-search-line "http") (kill-new (thing-at-point 'url))) (t (whole-line-or-region-kill-ring-save prefix-arg))))) (defun cp/marked-files->markup-links-md (filenames) (mapcar (lambda (filename) (if (member (downcase (file-name-extension filename)) image-file-name-extensions) (let ((link-pre "![](") (link-post ")\n")) (concat link-pre filename link-post)) (let ((link-pre "[](") (link-post ")\n")) (concat link-pre filename link-post)))) filenames)) #+END_SRC ** #+BEGIN_SRC emacs-lisp (add-hook 'erc-mode-hook 'visual-line-mode) #+END_SRC ** #+BEGIN_SRC emacs-lisp (add-hook 'comint-mode-hook 'visual-line-mode) ;; commented out on 2018-03-19T14:18:34+0530 ;; (add-hook 'markdown-mode-hook 'auto-fill-mode) ;; (add-hook 'text-mode-hook 'auto-fill-mode) ;; (add-hook 'paredit-mode-hook 'auto-fill-mode) ;;;; Tab settings ;; (setq default-tab-width 4) #+END_SRC ** #+BEGIN_SRC emacs-lisp (setq tab-width 4) ;(define-key text-mode-map (kbd "TAB") 'self-insert-command) #+END_SRC ** #+BEGIN_SRC emacs-lisp (setq-default indent-tabs-mode nil) #+END_SRC ** mediawiki-mode :PROPERTIES: :CUSTOM_ID: mediawiki-mode :END: #+BEGIN_SRC emacs-lisp (use-package mediawiki :commands mediawiki-mode) #+END_SRC ** asciidoc-mode :PROPERTIES: :CUSTOM_ID: asciidoc-mode :END: Used by Eldev documentation. #+BEGIN_SRC emacs-lisp (use-package adoc-mode :mode "\\.adoc$") #+END_SRC ** gemini-mode :PROPERTIES: :CUSTOM_ID: gemini-mode :END: <2021-07-12T16:06:08+0530> #+BEGIN_SRC emacs-lisp (use-package gemini-mode :ensure t :hook (gemini-mode . (lambda () (make-local-variable 'outline-regexp) (setq outline-regexp "[#]+"))) :bind (:map gemini-mode-map ("TAB" . contrapunctus-outline-indent-or-toggle-children))) #+END_SRC ** TODO LaTeX :PROPERTIES: :CUSTOM_ID: latex :END: 1. [ ] merge personal commands with those in Lilypond mode #+BEGIN_SRC emacs-lisp (use-package auctex :ensure t :hook ((tex-mode . TeX-source-correlate-mode)) :bind (:map LaTeX-mode-map ("M-c" . cp-backward-def) ("M-r" . cp-forward-def) ("M-." . forward-sentence)) :config (setq ;; TeX-auto-save t ;; TeX-parse-self t TeX-engine 'xetex ;; 2017-10-14T18:55:05+0530 ) (add-to-list 'TeX-view-program-selection '(output-pdf "PDF Tools" TeX-pdf-tools-sync-view)) ;; 2017-10-19T09:19:34+0530 - these should be merged with ;; cp-forward-def/cp-backward-def in cp-lily... (defun cp-backward-def () (interactive) (push-mark) (re-search-backward "\\\\scene" nil t) (beginning-of-line) (recenter)) (defun cp-forward-def () (interactive) (push-mark) (if (not (re-search-forward "\\\\scene" nil t 2)) (re-search-forward "\\end{document}" nil t)) (beginning-of-line) (recenter))) #+END_SRC * Programming :PROPERTIES: :CUSTOM_ID: programming :END: ** FIXME common [%] :PROPERTIES: :CUSTOM_ID: common :END: *** xref :PROPERTIES: :CUSTOM_ID: xref :END: #+BEGIN_SRC emacs-lisp (use-package xref :bind (:map xref--xref-buffer-mode-map ("c" . #'xref-prev-line) ("r" . #'xref-next-line))) #+END_SRC *** swap keys :disabled: :PROPERTIES: :CUSTOM_ID: swap-keys :END: 1. [X] =keyswap-mode= swaps numeric keys with symbols by default. We want to swap just () with [] (to begin with), but the code below doesn't work, for some reason. 2. [ ] Does not always work in the minibuffer, namely =eval-expression= Swap [] with () in programming and text modes. pjb suggested a different approach [[http://paste.lisp.org/display/10157][here]], which is basically - #+BEGIN_SRC emacs-lisp :tangle no :load no (defun contrapunctus-swap-brackets-parens () (interactive) (keyboard-translate ?\( ?\[) (keyboard-translate ?\) ?\]) (keyboard-translate ?\[ ?\() (keyboard-translate ?\] ?\))) (add-hook 'prog-mode-hook 'contrapunctus-swap-brackets-parens) (add-hook 'text-mode-hook 'contrapunctus-swap-brackets-parens) (defun normal-brackets-parens () (interactive) (keyboard-translate ?\( ?\() (keyboard-translate ?\) ?\)) (keyboard-translate ?\[ ?\[) (keyboard-translate ?\] ?\])) #+END_SRC ...which I tried, but experienced some subtle bugs, and luckily I got keyswap mode to work the way I wanted - no number-symbol switching, just parens and brackets. #+BEGIN_SRC emacs-lisp (use-package keyswap :disabled t :hook ((minibuffer-setup-hook . contrapunctus-swap-brackets-parens) ;; probably unnecessary (eval-expression-minibuffer-setup-hook . contrapunctus-swap-brackets-parens) (prog-mode . contrapunctus-swap-brackets-parens) (text-mode . contrapunctus-swap-brackets-parens) (emacs-lisp-mode . keyswap-colon-semicolon) (ielm-mode . contrapunctus-swap-brackets-parens) (ielm-mode . keyswap-colon-semicolon) (lisp-mode . keyswap-colon-semicolon) (slime-repl-mode . contrapunctus-swap-brackets-parens) (slime-repl-mode . keyswap-colon-semicolon) (scheme-mode . keyswap-colon-semicolon) (geiser-repl-mode . contrapunctus-swap-brackets-parens) (geiser-repl-mode . keyswap-colon-semicolon)) :config (defun contrapunctus-keyswap-common () (setq-local keyswap-pairs nil) ;; dont swap numbers and symbols (keyswap-mode)) (defun contrapunctus-swap-brackets-parens () ;; (message "keyswap-pairs is %s" keyswap-pairs) (contrapunctus-keyswap-common) (keyswap-add-pairs ?\[ ?\() (keyswap-add-pairs ?\] ?\)) (keyswap-update-keys))) #+END_SRC *** other things :PROPERTIES: :CUSTOM_ID: other-things :END: #+BEGIN_SRC emacs-lisp (use-package projectile :ensure t :hook (prog-mode . projectile-mode) :bind (:map boon-command-map ("\\" . projectile-command-map))) #+END_SRC #+BEGIN_SRC emacs-lisp (use-package rainbow-delimiters :ensure t :hook (prog-mode . rainbow-delimiters-mode)) #+END_SRC *** feature-mode :PROPERTIES: :CREATED: 2022-01-14T01:31:08+0530 :CUSTOM_ID: feature-mode :END: #+BEGIN_SRC emacs-lisp (use-package feature-mode :mode "\\.feature$") #+END_SRC *** paredit :disabled: :PROPERTIES: :CREATED: 2022-01-13T23:04:41+0530 :CUSTOM_ID: paredit :END: #+BEGIN_SRC emacs-lisp :load no :tangle no (require 'paredit) (add-hook 'emacs-lisp-mode-hook 'paredit-mode) (add-hook 'lisp-mode-hook 'paredit-mode) (add-hook 'scheme-mode-hook 'paredit-mode) (add-hook 'inferior-scheme-mode-hook 'paredit-mode) (add-hook 'inferior-lisp-mode-hook 'paredit-mode) (add-hook 'ielm-mode-hook 'paredit-mode) (global-set-key (kbd "C-x C-p") 'paredit-mode) (cp-set-keys :keymap paredit-mode-map :bindings `((,(kbd "C-p") paredit-backward-down) (,(kbd "C-n") paredit-forward-up) (,(kbd "C-b") paredit-backward) (,(kbd "C-f") paredit-forward) (,(kbd "C-u") paredit-backward-up) (,(kbd "C-d") paredit-forward-down) (,(kbd "M-b") backward-char) (,(kbd "M-f") forward-char) (,(kbd "M-p") previous-line) (,(kbd "M-n") next-line) (,(kbd "M-u") paredit-kill-0) (,(kbd "M-d") paredit-forward-delete) (,(kbd "C-M-b") backward-word) (,(kbd "C-M-f") forward-word) (,(kbd "C-M-u") upcase-word) (,(kbd "C-M-d") paredit-forward-kill-word) (,(kbd "C-M-p") nil) (,(kbd "C-M-n") nil) (,(kbd "M-w") paredit-copy-as-kill) (,(kbd "C-h") paredit-backward-delete) (,(kbd "C-w") paredit-backward-kill-word))) (cp-set-keys :keymap comint-mode-map :bindings `((,(kbd "C-d") paredit-forward-down) (,(kbd "C-M-p") comint-previous-input) (,(kbd "C-M-n") comint-next-input))) #+END_SRC *** smartparens :PROPERTIES: :CUSTOM_ID: smartparens :END: #+BEGIN_SRC emacs-lisp (use-package smartparens :ensure t :init (smartparens-global-mode) :config (require 'smartparens-config) (add-to-list 'sp-no-reindent-after-kill-modes 'markdown-mode) ;; (sp-pair "(" ")" :trigger-wrap (kbd "M-(") :actions '(insert wrap ;; autoskip navigate escape)) (sp-pair "(" ")" :wrap "M-(") (sp-pair "[" "]" :wrap "M-[") (sp-pair "\"" "\"" :wrap "M-\"") ;; (global-unset-key (kbd "M-\'")) (sp-pair "\'" "\'" :wrap "M-\'") ;; ;; buggy (sp-pair "<" ">" :wrap "M-<") (sp-pair "{" "}" :wrap "M-{") ;; (sp-pair "\\\\*" "\\\\*" :actions '(wrap)) (sp-pair "\\\\*" :wrap) ;; (sp-pair "\\\\/" :wrap) ;; Disable inserting pair if preceded by : (e.g. in IRC smileys) (defun cp-point-after-colon-p () (equal (string (char-before)) ":")) ;; (sp-pair "(" nil :unless '(cp-point-after-colon-p)) ;; sp-backward-kill-word and subword-backward-kill conflict ;; (define-key emacs-lisp-mode-map (kbd "C-p") 'sp-previous-sexp) ;; (define-key emacs-lisp-mode-map (kbd "C-n") 'sp-next-sexp) :bind (("M-" . sp-splice-sexp-killing-backward) :map prog-mode-map ("M-'" . sp-indent-defun) :map smartparens-mode-map ("C-)" . sp-forward-slurp-sexp) ("C-(" . sp-backward-slurp-sexp) ("C-}" . sp-forward-barf-sexp) ("C-{" . sp-backward-barf-sexp) ("C-j" . sp-newline) ("C-|" . sp-join-sexp) ("C-k" . sp-kill-hybrid-sexp) ("C-h" . sp-backward-delete-char) ("" . sp-backward-delete-char) ("C-w" . sp-backward-kill-word) ("" . sp-backward-kill-word) ("M-DEL" . sp-backward-kill-word) ("M-e" . sp-kill-word) ("C-M-p" . sp-backward-down-sexp) ("C-M-n" . sp-backward-up-sexp) ("C-M-b" . sp-backward-sexp) ("C-M-f" . sp-forward-sexp) ("C-M-u" . sp-up-sexp) ("C-M-d" . sp-down-sexp) ("C-M-a" . sp-beginning-of-sexp) ("C-M-e" . sp-end-of-sexp) ("C-M-k" . sp-kill-sexp) ("C-M-w" . sp-copy-sexp) :map emacs-lisp-mode-map (";" . sp-comment)) :hook (eval-expression-minibuffer-setup . smartparens-mode) (paredit-mode . turn-off-smartparens-mode) (erc-mode . smartparens-mode)) #+END_SRC *** lispy :PROPERTIES: :CUSTOM_ID: lispy :END: Create advice for =lispy-pair= - if =lispy--in-string-or-comment-p= is true, self-insert (which =smartparens= will add the closing pair for) #+BEGIN_SRC emacs-lisp (use-package lispy :ensure t :hook (emacs-lisp-mode . lispy-mode) (inferior-emacs-lisp-mode . lispy-mode) (lisp-mode . lispy-mode) (scheme-mode . lispy-mode) (slime-repl-mode . lispy-mode) ;; Boon-style keys on Dvorak :bind (:map lispy-mode-map ;; essential movement ("h" . special-lispy-left) ;; QWERTY J ("s" . special-lispy-right) ;; QWERTY ; ("c" . special-lispy-up) ;; QWERTY I ("r" . special-lispy-down) ;; QWERTY O ;; ;; defined in :config ;; ("t" . special-lispy-backward) ;; ("n" . special-lispy-forward) ("i" . special-lispy-flow) ;; "inwards" ("l" . special-lispy-teleport) ("j" . special-lispy-occur) ;; essential manipulation ("k" . special-lispy-clone) ("K" . special-lispy-convolute) ("C" . special-lispy-move-up) ("R" . special-lispy-move-down) ("o" . special-lispy-splice) ;; QWERTY S ("p" . special-lispy-raise-some) ;; ;; defined in :config ;; ("T" . special-lispy-splice-sexp-killing-forward) ;; ("N" . special-lispy-splice-sexp-killing-backward) ;; ("l" . special-lispy-new-copy) ;; ;; Lispy shadows this, but it's essential for Org literate programs ("M-o" . nil) ("M-o M-o" . font-lock-fontify-block) ;; It does not insert a pair in strings or comments. I want ;; that. I'll let smartparens take care of it. ("(" . self-insert-command)) ;; :config (setq lispy-mode-map-c-digits nil) :config ;; (dolist (key '("C-1" "C-2" "C-3" "C-4")) ;; (define-key lispy-mode-map-c-digits (kbd key) nil)) (lispy-set-key-theme '(lispy special)) ;; These keys do not have special-* variants defined by default, so ;; here we define them ourselves. (cl-loop for (key . cmd) in '(("t" . lispy-backward) ("n" . lispy-forward) ("T" . lispy-splice-sexp-killing-forward) ("N" . lispy-splice-sexp-killing-backward) ("?" . lispy-describe-inline)) do (lispy-define-key lispy-mode-map (kbd key) cmd)) (setq lispy-colon-p nil)) #+END_SRC *** treemacs :disabled: :PROPERTIES: :CUSTOM_ID: treemacs :END: #+BEGIN_SRC emacs-lisp (use-package treemacs :disabled t :bind (:map treemacs-mode-map ([mouse-1] . #'treemacs-TAB-action) ("c" . #'treemacs-previous-line) ("r" . #'treemacs-next-line)) :config (treemacs-tag-follow-mode) (treemacs-toggle-fixed-width) (setq treemacs-tag-follow-delay 0)) #+END_SRC *** imenu :PROPERTIES: :CUSTOM_ID: imenu :END: #+BEGIN_SRC emacs-lisp (use-package imenu :hook (imenu-after-jump . (lambda () (recenter 0))) ;; also applies to `imenus' :config (setq imenu-auto-rescan t org-imenu-depth 10)) #+END_SRC **** imenus :PROPERTIES: :CUSTOM_ID: imenus :END: #+BEGIN_SRC emacs-lisp (use-package imenus :ensure t :commands imenus) #+END_SRC *** side-hustle :disabled: :PROPERTIES: :CUSTOM_ID: side-hustle :END: #+BEGIN_SRC emacs-lisp (use-package side-hustle :disabled t :bind (:map side-hustle-mode-map (("r" . next-line) ("c" . previous-line)))) #+END_SRC *** dump-jump :PROPERTIES: :CUSTOM_ID: dump-jump :END: #+BEGIN_SRC emacs-lisp (use-package dumb-jump :init (add-hook 'xref-backend-functions #'dumb-jump-xref-activate)) #+END_SRC *** dired-sidebar :PROPERTIES: :CUSTOM_ID: dired-sidebar :END: #+BEGIN_SRC emacs-lisp (use-package dired-sidebar :config (setq dired-sidebar-no-delete-other-windows t dired-sidebar-should-follow-file t dired-sidebar-follow-file-idle-delay 0.2 dired-sidebar-follow-file-timer 0.2)) #+END_SRC ** lisp :PROPERTIES: :CREATED: 2022-01-13T21:03:15+0530 :CUSTOM_ID: lisp :END: #+BEGIN_SRC emacs-lisp (defun contrapunctus-lisp-copy (arg) "Run `whole-line-or-region-copy-region-as-kill' if region is active, else `sp-copy-sexp'." (interactive "P") (if (region-active-p) (whole-line-or-region-copy-region-as-kill arg) (sp-copy-sexp arg))) (defun colorize-compilation-buffer () (ansi-color-apply-on-region compilation-filter-start (point))) (add-hook 'compilation-filter-hook 'colorize-compilation-buffer) ;; Stopped calling `exec-path-from-shell-initialize' on init, but ;; won't this keep running it excessively? ;; `exec-path-from-shell-copy-envs' (which it calls internally) ;; doesn't seem idempotent, either. (use-package exec-path-from-shell :ensure t :hook (compilation-mode . exec-path-from-shell-initialize) (shell-mode-hook . exec-path-from-shell-initialize) (minibuffer-setup-hook . exec-path-from-shell-initialize)) #+END_SRC ** Emacs Lisp :PROPERTIES: :CUSTOM_ID: emacs-lisp :END: #+BEGIN_SRC emacs-lisp (use-package elisp-mode :diminish :bind (:map emacs-lisp-mode-map ;; ("" . 'company-indent-or-complete-common) ;; ("" . 'outline-toggle-children) ("M-n" . 'outline-next-heading) ("M-p" . 'outline-previous-heading) ("M-m" . macrostep-expand)) :config (put 'cl-loop 'lisp-indent-function 'defun)) #+END_SRC #+BEGIN_SRC emacs-lisp (use-package ielm :bind (:map ielm-map ("M-'" . sp-indent-defun))) #+END_SRC #+BEGIN_SRC emacs-lisp (defun cp/eval-sexp (arg) "In emacs-lisp-mode, just run eval-defun. In other modes - jump to first Lisp expression in current line and eval it." (interactive "P") (save-excursion (cond ((or (equal major-mode 'emacs-lisp-mode) (equal major-mode 'lisp-interaction-mode)) (eval-defun arg)) ((cp/re-search-line "(") (progn (forward-char -1) (forward-sexp) (eval-last-sexp arg))) (t nil)))) #+END_SRC *** ELSA - Emacs Lisp Static Analyzer :disabled: :PROPERTIES: :CUSTOM_ID: elsa :END: #+BEGIN_SRC emacs-lisp (use-package elsa :disabled t :commands flycheck-elsa-setup) #+END_SRC *** literate-elisp :PROPERTIES: :CUSTOM_ID: literate-elisp :END: #+BEGIN_SRC emacs-lisp (use-package literate-elisp :ensure t :commands (literate-elisp-load)) #+END_SRC *** eldoc :PROPERTIES: :CUSTOM_ID: eldoc :END: #+BEGIN_SRC emacs-lisp (use-package eldoc :diminish :if (featurep 'elisp-mode) :init (add-hook 'emacs-lisp-mode-hook 'eldoc-mode) :config (setq eldoc-idle-delay 0 eldoc-message-commands (vconcat eldoc-message-commands [sp-backward-delete-char]))) #+END_SRC *** emr - emacs refactor :PROPERTIES: :CUSTOM_ID: emr :END: #+BEGIN_SRC emacs-lisp (use-package emr :bind (:map prog-mode-map ("M-S-" . emr-show-refactor-menu))) #+END_SRC *** nameless-mode :PROPERTIES: :CUSTOM_ID: nameless-mode :END: #+BEGIN_SRC emacs-lisp (use-package nameless :ensure t :commands nameless-mode :hook (ert-results-mode . nameless-mode) (emacs-lisp-mode . nameless-mode) (org-mode . nameless-mode) :bind (:map emacs-lisp-mode-map ("C-c C-n" . nameless-mode) ("C-c C--" . nameless-insert-name))) #+END_SRC *** explain-pause-mode :disabled: :PROPERTIES: :CUSTOM_ID: explain-pause-mode :END: #+BEGIN_SRC emacs-lisp (use-package explain-pause-mode :disabled t :load-path "~/.emacs.d/elisp-git/explain-pause-mode/" :diminish :commands (explain-pause-mode) :init (explain-pause-mode)) #+END_SRC *** WIP async-tangle :PROPERTIES: :CUSTOM_ID: async-tangle :END: Adapted from https://stackoverflow.com/questions/16815598/run-commands-in-emacs-asynchronously-but-display-output-incrementally/16816575#16816575 and the Elisp manual #+BEGIN_SRC emacs-lisp (defun my-start-process* (buffer &rest command-specs) "Execute COMMAND-SPECS sequentially. All COMMAND-SPECS should be a list in the form \(NAME COMMAND COMMAND-ARGS*\)" (with-current-buffer buffer (set (make-local-variable 'commands-list) command-specs) (boon-mode) (my-start-next-process))) (defun my-start-next-process () "Run the first command in the list." (if (null commands-list) (insert "\nDone.") (-let* [(command-spec (car commands-list)) ((name command . command-args) command-spec)] (setq commands-list (cdr commands-list)) (insert (format ">>> %s\n" command)) (let ((process (funcall #'start-process name (current-buffer) command command-args))) (set-process-sentinel process 'my-sentinel))))) (defun my-sentinel (process event) "After a process exited, call `my-start-next-process' again" (let ((buffer (process-buffer process))) (when (buffer-live-p buffer) (with-current-buffer buffer (let ((moving (= (point) (process-mark process)))) (save-excursion ;; Insert the text, advancing the process marker. (goto-char (process-mark process)) ;; (insert (format "Command `%s' %s" process event)) (set-marker (process-mark process) (point)) (my-start-next-process)) (when moving (goto-char (process-mark process)))))))) #+END_SRC #+BEGIN_SRC emacs-lisp (defun contrapunctus-async-tangle (&optional prefix) "Use `org-babel-tangle' on the file visited by the current buffer." (interactive "P") (let* ((proc-buffer (get-buffer-create "*async-tangle-process*")) (file-name (buffer-file-name)) (file-name-no-ext (file-name-sans-extension (buffer-file-name))) (old-win (selected-window)) ; ? (process (start-process "async-tangle" proc-buffer "emacs" "-q" "-Q" "--batch" "--eval=(require 'ob-tangle)" (format "--eval=(org-babel-tangle-file \"%s\")" file-name file-name-no-ext)))) ;; don't create window if buffer already visible (unless (get-buffer-window-list proc-buffer) ;; to avoid messing up my usual two-windows-same-buffer setup (select-window (split-window-below)) (switch-to-buffer proc-buffer) ;; so I can access my Hydra to switch back (boon-mode)) ;; (select-window old-win) )) #+END_SRC ** Common Lisp :PROPERTIES: :CUSTOM_ID: programming-common-lisp :END: #+BEGIN_SRC emacs-lisp (use-package lisp-mode :interpreter ("cl" . lisp-mode) :config (modify-syntax-entry ?\[ "(]" lisp-mode-syntax-table) (modify-syntax-entry ?\] ")[" lisp-mode-syntax-table)) ;; cl-launch scripts (defvar my-test-command nil "Command which runs tests for the current project.") (defun my-run-cl-tests () (interactive) (cond ((bound-and-true-p my-test-command) ;; (slime-interactive-eval my-test-command) (slime-repl-send-string my-test-command) (slime-switch-to-output-buffer)) (t (message "my-test-commmand is unbound or nil")))) #+END_SRC *** redshank :PROPERTIES: :CUSTOM_ID: redshank :END: #+BEGIN_SRC emacs-lisp (use-package redshank :ensure t :hook (slime-mode . redshank-mode)) #+END_SRC *** slime :PROPERTIES: :CUSTOM_ID: slime :END: #+BEGIN_SRC emacs-lisp (use-package slime :ensure t :commands (slime-eval-buffer slime-eval-defun) :mode ("\\.asd$" . asdf-mode) :bind (:map slime-mode-map ("M-n" . next-line) ("M-p" . previous-line) ("SPC" . self-insert-command) (" " . slime-documentation) ("TAB" . company-indent-or-complete-common) ("C-i" . company-indent-or-complete-common)) (:map slime-repl-mode-map ("M-p" . slime-repl-previous-matching-input) ;; QWERTY "r" ("M-c" . slime-repl-previous-input) ("M-r" . slime-repl-next-input) ("TAB" . company-indent-or-complete-common) ("C-i" . company-indent-or-complete-common)) :config (slime-setup '(slime-fancy slime-company slime-tramp)) (setq inferior-lisp-program ;; "/usr/bin/ecl" "sbcl" slime-net-coding-system 'utf-8-unix) (defun cp-slime-completion-in-region (_fn completions start end) (funcall completion-in-region-function start end completions)) (advice-add 'slime-display-or-scroll-completions :around #'cp-slime-completion-in-region) (add-to-list 'slime-filename-translations (slime-create-filename-translator :machine-instance "tilde" :remote-host "tilde.team" :username "contrapunctus")) (add-to-list 'company-backends 'company-slime)) #+END_SRC **** slime-fancy-inspector :PROPERTIES: :CUSTOM_ID: slime-fancy-inspector :END: #+BEGIN_SRC emacs-lisp (use-package slime-fancy-inspector :bind (:map slime-inspector-mode-map ("c" . slime-inspector-previous-inspectable-object) ("r" . slime-inspector-next-inspectable-object) ("n" . slime-inspector-operate-on-point) ("t" . slime-inspector-pop) ("" . slime-inspector-pop))) #+END_SRC *** slime-company :PROPERTIES: :CUSTOM_ID: slime-company :END: #+BEGIN_SRC emacs-lisp (use-package slime-company :ensure t :after (slime company) :config (setq slime-company-completion 'fuzzy slime-company-after-completion 'slime-company-just-one-space)) #+END_SRC *** common-lisp-snippets :PROPERTIES: :CREATED: 2022-01-17T12:07:32+0530 :CUSTOM_ID: common-lisp-snippets :END: #+BEGIN_SRC emacs-lisp (use-package common-lisp-snippets :ensure t :init (common-lisp-snippets-initialize)) #+END_SRC ** Scheme :PROPERTIES: :CUSTOM_ID: scheme :END: #+BEGIN_SRC emacs-lisp (setq scheme-program-name "csi -:c") (setq comint-prompt-read-only t) (use-package geiser :mode ("\\.scm\\'" . geiser-mode) :commands (run-chicken run-guile geiser-mode) :custom (geiser-active-implementations '(chicken)) :config (setq geiser-scheme-implementation 'chicken)) ;; (with-eval-after-load 'geiser-mode ;; (setq geiser-mode-smart-tab-p t) ;; (define-key geiser-mode-map (kbd "C-.") nil) ;; ;; (cp-set-keys ;; ;; :unset t ;; ;; :keymap geiser-mode-map ;; ;; :bindings ;; ;; `((,(kbd "C-.")))) ;; ) (use-package scheme-mode :mode ("\\.scm\\'" . scheme-mode) :interpreter "csi") #+END_SRC *** CHICKEN Scheme :PROPERTIES: :CUSTOM_ID: chicken-scheme :END: *** Guile :PROPERTIES: :CUSTOM_ID: guile :END: ** Lilypond :PROPERTIES: :CUSTOM_ID: lilypond :END: #+BEGIN_SRC emacs-lisp (use-package lilypond-mode :load-path "elisp-git/lilypond/elisp" :bind (("M-]" . set-selective-display) :map LilyPond-mode-map ("M-c" . cp-backward-def) ("M-r" . cp-forward-def) ("M-C" . cp-upper-level) ("M-R" . cp-lower-level) ("C-c C-w" . cp-ly-wrap-para)) :commands LilyPond-mode :mode (("\\.ly$" . LilyPond-mode) ("\\.ily$" . LilyPond-mode)) :config (--map (add-hook 'LilyPond-mode-hook it) '(subword-mode (lambda () (turn-on-font-lock)))) (defalias 'string-to-int #'string-to-number) (defvar cp/ly-definition-rx '(and bol (1+ (any "a-z" "A-Z" "\\\\")) (1+ (any "a-z" "A-Z" "\\\\" " ")) (any "{" "=" "#"))) (defun cp-backward-def () (interactive) (unless (region-active-p) (push-mark)) (re-search-backward (rx-to-string cp/ly-definition-rx) nil t) (beginning-of-line) (recenter)) (defun cp-forward-def () (interactive) (let* ((regex (rx-to-string cp/ly-definition-rx)) (count (if (looking-at-p regex) 2 1))) (unless (region-active-p) (push-mark)) ;; (forward-char) (if (not (re-search-forward regex nil t count)) (re-search-forward "^}" nil t)) ;; (re-search-forward "^[\\a-zA-Z]" nil t) (beginning-of-line) (recenter))) ;; (defun cp-backward-def () ;; (interactive) ;; (re-search-backward "\(^\\\\?[a-zA-Z]\|^ *\\[a-zA-Z]\)") ;; (beginning-of-line)) ;; (defun cp-forward-def () ;; (interactive) ;; (forward-char) ;; (re-search-forward "\(^\\\\?[a-zA-Z]\|^ *\\[a-zA-Z]\)") ;; (beginning-of-line)) (defun cp-upper-level () (interactive) (re-search-backward "{")) (defun cp-lower-level () (interactive) (if (equal (string (char-after)) "{") (forward-char)) (if (not (re-search-forward "{")) (message "At deepest level.")) (backward-char)) ;; (defun cp-lilypond-enclose-<< () ;; (interactive) ;; (if (equal (string (char-after)) "\\") ;; (progn (insert "<< ") ;; (search-forward "{") ;; (backward-char) ;; (forward-sexp)))) ;; if at a \new ... block - enclose expression ;; otherwise, enclose current position and after the first bar check ;; found ;; if region is active, enclose beginning and end ;; (defun cp-lilypond-enclose-<< () ;; (interactive) ;; (if (equal (thing-at-point 'sexp) ;; "\\new") ;; (progn ;; (insert "<< ") ;; (newline-and-indent) ;; (search-forward "{") ;; (backward-char) ;; (forward-list) ;; ;; (forward-sexp)) ;; ) ;; ;; (let ((point1 (point))) ;; ;; (next-line) ;; ;; (goto-char point1)) ;; )) ;; (define-key LilyPond-mode-map (kbd "<<") ;; 'cp-lilypond-enclose-<<) ;; If I change files, it's still main.ly that gets compiled; this is ;; good most of the time, but many times I want to compile a part-* ;; file instead. If we compile both main.ly and the respective part-* ;; file every time, it's wasteful. Having to select means giving up ;; the 'effortless-compilation' behaviour. ;; 2017-03-14T00:52:07+0530 - commented out, see cp/after-save ;; (defadvice LilyPond-save-buffer ;; (after lysb activate) ;; ;; (compile "make") ;; (cd (locate-dominating-file (buffer-file-name) ;; "main.ly")) ;; (compile (car compile-history))) ;; ;; (defadvice compile ;; (before compile activate) ;; (if (equal major-mode 'LilyPond-mode) ;; (cd (locate-dominating-file (buffer-file-name) ;; "main.ly")))) ;; TODO - refactor into one COND, with one case per operation. ;; TODO - operate on region as well. (defun cp-ly-wrap-para (arg) "Wrap current paragraph with - \\relative c { ... } with no args, \\repeat { ... } with universal argument, and only braces - { ... } - with null argument. Numeric arg wraps that many paragraphs. TODO - wrap region if region active" (interactive "P") (let ((point-a (point))) (beginning-of-line) (unless (looking-at "[[:blank:]]*$") ;; go to start of paragraph or block, or previous blank line (re-search-backward (rx (or (and bol (0+ blank) eol) (and "{" eol)))) (end-of-line)) (newline-and-indent) (insert (pcase arg (`(,x) "\\repeat {") (0 "{") ;; nil (_ "\\relative c {"))) (let ((indent-start (point))) (forward-paragraph (pcase arg (`(,x) 1) (_ (if (and arg (<= arg 0)) 1 arg)))) (indent-region indent-start (point)) (insert "}") (indent-for-tab-command) (newline) ;; FIXME (goto-char (pcase arg (0 point-a) (_ (- indent-start 2)))))))) ;; TODO - cp-ly-new-var, bind to M-RET. ;; Exits current variable body, if in any, and inserts "| = \relative ;; c {\n\n \n}", where | is the cursor #+END_SRC ** Prolog :PROPERTIES: :CUSTOM_ID: prolog-1 :END: #+BEGIN_SRC emacs-lisp (use-package ediprolog :commands ediprolog-dwim) #+END_SRC ** C :PROPERTIES: :CUSTOM_ID: c :END: #+BEGIN_SRC emacs-lisp (use-package cc-mode :bind (:map c-mode-map ("TAB" . company-indent-or-complete-common) ("C-i" . company-indent-or-complete-common))) (use-package irony-eldoc :hook (c-mode . irony-eldoc)) (use-package irony :config (add-hook 'irony-mode-hook #'irony-eldoc)) (use-package company-irony) (use-package rtags :hook (c-mode . rtags-call-rc) :config (setq rtags-rc-binary-name "rtags-rc" rtags-rdm-binary-name "rtags-rdm") :bind (:map c-mode-map (" " . rtags-find-symbol-at-point))) #+END_SRC ** nodejs-repl :disabled: :PROPERTIES: :CUSTOM_ID: nodejs-repl :END: #+BEGIN_SRC emacs-lisp (use-package nodejs-repl :disabled :config (setq nodejs-repl-command "nodejs")) #+END_SRC ** web development :PROPERTIES: :CUSTOM_ID: web-development :END: https://emacs.cafe/emacs/javascript/setup/2017/04/23/emacs-setup-javascript.html Potentially useful - https://www.draketo.de/software/emacs-javascript.html *** js2-mode :PROPERTIES: :CREATED: 2022-01-14T16:22:00+0530 :CUSTOM_ID: js2-mode :END: #+BEGIN_SRC emacs-lisp (use-package js2-mode :mode ("\\.js\\'" . js2-mode) ;; Better imenu :hook (js2-mode-hook . js2-imenu-extras-mode)) #+END_SRC *** js2-refactor :PROPERTIES: :CREATED: 2022-01-14T16:22:05+0530 :CUSTOM_ID: js2-refactor :END: #+BEGIN_SRC emacs-lisp (use-package js2-refactor :hook (js2-mode-hook . js2-refactor-mode)) #+END_SRC *** tern :PROPERTIES: :CREATED: 2022-01-14T16:22:10+0530 :CUSTOM_ID: tern :END: #+BEGIN_SRC emacs-lisp (use-package tern) #+END_SRC *** company-tern :PROPERTIES: :CREATED: 2022-01-14T16:22:13+0530 :CUSTOM_ID: company-tern :END: #+BEGIN_SRC emacs-lisp (use-package company-tern :load-path "~/.emacs.d/elisp-git/company-tern/" :init (add-to-list 'company-backends 'company-tern)) #+END_SRC *** skewer-mode :PROPERTIES: :CREATED: 2022-01-14T16:22:17+0530 :CUSTOM_ID: skewer-mode :END: #+BEGIN_SRC emacs-lisp (use-package skewer-mode :hook (js2-mode-hook . skewer-mode)) #+END_SRC text size change nicked from wasamasa's init - https://github.com/wasamasa/dotemacs/blob/934d0b37692d62fe9af56b52accac5bcd4445ae3/init.org #+BEGIN_SRC emacs-lisp #+END_SRC #+BEGIN_SRC emacs-lisp ;; (set-face-attribute 'default nil :font "-outline-Bitstream Vera Sans Mono-normal-normal-normal-mono-12-*-*-*-c-*-iso8859-1") #+END_SRC ** SQL :PROPERTIES: :CUSTOM_ID: sql :END: #+BEGIN_SRC emacs-lisp (use-package sql ;; History is not saved if you kill the buffer; use ;; `comint-quit-subjob' or `comint-kill-subjob' to do so :hook (sql-interactive-mode . (lambda () (setq-local sql-input-ring-file-name (locate-user-emacs-file "sql-input-ring"))))) #+END_SRC ** Python :PROPERTIES: :CUSTOM_ID: python :END: #+BEGIN_SRC emacs-lisp (use-package python :config (setq python-shell-interpreter "python3")) #+END_SRC * The end :PROPERTIES: :CUSTOM_ID: the-end :END: Reset the GC settings, so Emacs doesn't use up tons of RAM. #+BEGIN_SRC emacs-lisp (setq gc-cons-threshold 400000) ;; (toggle-debug-on-quit) ;; (profiler-stop) ;; (emacs-init-time) ;; (profiler-report) #+END_SRC #+BEGIN_SRC emacs-lisp (provide 'init) ;;; init.el ends here #+END_SRC # Local Variables: # nameless-current-name: "contrapunctus" # eval: (visual-fill-column-mode -1) # eval: (nameless-mode) # compile-command: "make -Bk tangle" # my-org-src-default-lang: "emacs-lisp" # eval: (when (package-installed-p 'literate-elisp) (require 'literate-elisp) (literate-elisp-load (buffer-file-name))) # End: