Emacs config, org-mode style.

Table of Contents

This is the current version of my emacs configuration. It is rendered to a init.el by way of a slightly kludgey bash script.

Commented-out org-mode headings are neither shown here nor present in the tangled resulting elisp, but they are present in the repo that houses this document.

Fundamentals

The fundamental drivers of this configuration layout system are:

  • straight.el
  • use-package
  • org-mode.

These components must be present; everything else is user-specific.

package management with straight.el and use-package

Straight.el is a next-generation, purely functional package manager for the Emacs hacker. https://github.com/radian-software/straight.el/issues/1053

(when (version<= "29.1" emacs-version)
  (defvar native-comp-deferred-compilation-deny-list nil))

have a per-emacs version build directory in case reverting is a good idea. h/t vifon

(setq straight-build-dir (format "build-%s" emacs-version))

bootstrap straight

(let ((bootstrap-file (concat user-emacs-directory "straight/repos/straight.el/bootstrap.el"))
      (bootstrap-version 3))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))

configure use-package integration

(setq straight-use-package-by-default t)
(straight-use-package 'use-package)
(use-package git)

Disable other config files

Since all the configuration should live in this version-controlled file, discard any customizations made inside Emacs.

(setq custom-file (make-temp-file ""))

org-mode

setup

Getting org to play with straight.el is a little fussy.

install package

(use-package
  org
  :straight (:type built-in)
  :mode ("\\.org\\'" . org-mode)
  :bind (:map org-mode-map
        ("M-n" . outline-next-visible-heading)
        ("M-p" . outline-previous-visible-heading)
        ("M-RET" . org-meta-return)
        ("C-c l" . org-store-link)
        ("C-c a" . org-agenda)
        ("M-<" . grym/go-to-first-heading))
  :config
  ;; fontify code in code blocks
  (setq org-src-fontify-natively t)
  ;; Enable org capture
  (require 'org-capture)
  ;; Enable template expansion (<s, <q, etc.)
  (require 'org-tempo)
  ;; ignore flycheck files when looking in agenda
  (setq org-agenda-file-regexp
        "\\`[^\\(.\\|flycheck_\\)].*\\.org\\'")
  ;; log  TODO state changes into the default LOGBOOK drawer.
  (setq org-log-into-drawer t)
  ;; wrap agenda tags properly for alignment
  (setq org-agenda-tags-column
        -79)
  ;; start with follow-mode on in agenda
  (setq org-agenda-start-with-follow-mode
        t)
  ;; add counsel-mode to org speecbars
  (setq org-use-speed-commands t)

  (setq org-speed-commands
          (append
           '(("User-defined commands")
             ;;("o" . counsel-outline)
             ("o" . consult-org-heading)
             ("O" . org-open-at-point)
             ("w" . avy-org-refile-as-child)
             ("W" . org-refile)
             ("q" . bury-buffer))
           org-speed-commands))


  ;; make org-agenda-cycle shut up
  (require 'bind-key)
  (unbind-key "C-'" org-mode-map)
  (unbind-key "C-," org-mode-map)
  )

os-specific configurations

Some things only need to be tweaked for some OSes, but not others.

mac os

Disable ⌘-q

Accidentally closing your editor window when you don't mean to just sucks. In Linux, we can handle this at the desktop environment/window manager level; on macOS, it's easier to just disable the key inside of emacs itself, instead of relying on something like karabiner.

(when (eq system-type 'darwin)
  (global-unset-key (kbd "s-q")))

fix ls

  (when (eq system-type 'darwin)
  (setq insert-directory-program "gls" dired-use-ls-dired t)
)

personal variables

This is the place where we set up some variables pointing to where user-specific things live.

I use emacs and org mode to maintain my lab notebook, so most of these variables relate to some special handling or convenience functions for that purpose.

my/org-path-name

Part of this configuration's purpose is to use emacs as a digital lab notebook, which is a git repo. This is its canonical location.

(defvar my/org-path-name (expand-file-name "~/lab-notebook/"))

my/org-file-name

Set up a convenience function for path manipulation inside the lab notebook.

(defun my/make-org-file-name (file-name)
  (concat my/org-path-name file-name))

my/notes-file-name

The lab notebook in question has a central org-mode document where most of the work happens.

(defvar my/notes-file-name (my/make-org-file-name "notes.org"))

my/template-directory

(defvar my/template-directory (concat my/org-path-name "templates"))

my/make-template-file-name

(defun my/make-template-file-name (file-name)
  (concat my/template-directory file-name))

custom info directory

(add-to-list 'Info-directory-list (concat user-emacs-directory "info/"))

my/secrets

(require 'json)
(let ((json-object-type 'plist))
  (setq  my/secrets (json-read-file (concat user-emacs-directory "secrets.json"))))

themes

Emacs has many themes that it can use. One set of them are configured here.

The problem with themes is that I like to use emacsclient as exec emacsclient -c -a="" "$@" & > /dev/null, but if you do this, the theme comes out all weird the first time because emacs doesn't know if it has a GUI or not, so it reverts to a halfassed guess of what terminal colors it might have available to it.

The solution is to delay theme load for a few seconds until the GUI comes up and then it works fine. this reddit post lead to this solution.

ef-themes

  (use-package ef-themes
   :defer
   :straight (ef-themes :type git :host github :repo "protesilaos/ef-themes")
   :bind ("<f5>" . ef-themes-toggle)
   :config
   (defun my-ef-themes-hl-todo-faces ()
  "Configure `hl-todo-keyword-faces' with Ef themes colors.
The exact color values are taken from the active Ef theme."
  (ef-themes-with-colors
    (setq hl-todo-keyword-faces
          `(("HOLD" . ,yellow)
            ("TODO" . ,red)
            ("NEXT" . ,blue)
            ("THEM" . ,magenta)
            ("PROG" . ,cyan-warmer)
            ("OKAY" . ,green-warmer)
            ("DONT" . ,yellow-warmer)
            ("FAIL" . ,red-warmer)
            ("BUG" . ,red-warmer)
            ("DONE" . ,green)
            ("NOTE" . ,blue-warmer)
            ("KLUDGE" . ,cyan)
            ("HACK" . ,cyan)
            ("TEMP" . ,red)
            ("FIXME" . ,red-warmer)
            ("XXX+" . ,red-warmer)
            ("REVIEW" . ,red)
            ("DEPRECATED" . ,yellow)))))

(add-hook 'ef-themes-post-load-hook #'my-ef-themes-hl-todo-faces)
   (setq ef-themes-to-toggle (list 'ef-deuteranopia-light 'ef-deuteranopia-dark)))

solar

(use-package solar
:straight (:type built-in)
:config
(setq calendar-latitude (plist-get my/secrets :home-latitude)
      calendar-longitude (plist-get my/secrets :home-longitude)))

circadian

Switch themes based on time of day at a given physical location.

(use-package circadian
  :after solar
  :config
  (setq circadian-themes '((:sunrise .  ef-deuteranopia-light)
                           (:sunset  .  ef-deuteranopia-dark)))
  (circadian-setup))

emacs

Configuration related core editor functions are here.

Font Configuration

 ;; (add-to-list 'default-frame-alist '(font . "Menlo 10"))
     (when (eq system-type 'darwin)
       (add-to-list 'default-frame-alist '(font . "JetBrainsMono Nerd Font Mono 12"))
       )
 (when (eq system-type 'gnu/linux)
 (add-to-list 'default-frame-alist '(font . "JetBrainsMono Nerd Font Mono 10"))
)

 ;; (add-to-list 'default-frame-alist '(font . "NotoSansMono 10"))
 ;; (add-to-list 'default-frame-alist '(font . "NotoMonoNerdFont 10"))
 ;; (setq nerd-icons-font-family "Noto Mono Nerd Font")

autosaves

Auto-save will periodically save your file to backup file while you edit. This is great if something goes catastrophically wrong to Emacs!

store autosaves in a single place

(defvar emacs-autosave-directory
  (concat user-emacs-directory "autosaves/")
  "This variable dictates where to put auto saves. It is set to a
  directory called autosaves located wherever your .emacs.d/ is
  located.")

(unless (file-exists-p emacs-autosave-directory)
    (make-directory emacs-autosave-directory))

(setq auto-save-file-name-transforms `((".*"
,emacs-autosave-directory t)))

set save interval

Here, save every 20 secs or 20 keystrokes.

(setq auto-save-timeout 20
      auto-save-interval 20)

autosave every buffer that visits a file

(setq auto-save-default t)

disable lockfiles

(setq create-lockfiles nil)

backups

Backups are created everytime a buffer is saved. This is really useful for recovering work that takes place between version-control commits or on unversioned files.

store backups with the autosaves

(setq backup-directory-alist
      `((".*" . ,emacs-autosave-directory)))

keep 10 backups

(setq kept-new-versions 10
      kept-old-verisons 0)

delete old backups

(setq delete-old-versions t)

copy files to avoid various problems

(setq backup-by-copying t)

backup files even if version controlled

(setq vc-make-backup-files t)

cursor

style

Cursors have many styles. I like bar, but I like box when i leave the window.

(setq-default cursor-type 'box)
(setq-default cursor-in-non-selected-windows 'hollow)

blinking

Should the cursor blink?

(blink-cursor-mode 1)

disable

Emacs has many, many features – and some of them should be turned off!

menubar

The menubar just takes up space.

(menu-bar-mode -1)

toolbar

The toolbar just takes up space.

(tool-bar-mode -1)

scrollbar

The scrollbar just gets in the way. Minimap or nothing!

;;disable scroll-bar mode if it's even compiled.  

(scroll-bar-mode -1)

startup message

Squelch startup messages.

(setq inhibit-startup-message t
      initial-scratch-message nil)

bell

(setq ring-bell-function 'ignore)

editing

Default values for basic editing options live here.

use spaces

(setq-default indent-tabs-mode nil)

visual fill-column

Provided by visual-fill-column:

visual-fill-column-mode is a small Emacs minor mode that mimics the effect of fill-column in visual-line-mode. Instead of wrapping lines at the window edge, which is the standard behaviour of visual-line-mode, it wraps lines at fill-column. If fill-column is too large for the window, the text is wrapped at the window edge.

  (use-package visual-fill-column
:config (setq visual-fill-column-width 79))

fill at 79 characters

(setq-default fill-column 79)

autofill text-mode

(add-hook 'text-mode-hook 'turn-on-auto-fill)

ssh for tramp

(setq tramp-default-method "ssh")

click-to-select-region

(define-key global-map (kbd "<S-down-mouse-1>") 'mouse-save-then-kill)

save system clipboard

(setq save-interprogram-paste-before-kill t)

so-long mode

;; Avoid performance issues in files with very long lines.
(global-so-long-mode 1)

require final newline

(setq require-final-newline t)

ffap

(global-set-key (kbd "C-x C-S-f") 'ffap)

key-bindings

Set some default keybindings here.

meta n & p

(global-set-key (kbd "M-p") 'backward-paragraph)
(global-set-key (kbd "M-n") 'forward-paragraph)

zap-to-char

;;(global-set-key (kbd "M-z") 'zap-up-to-char)

minor-modes

There are a variety of minor modes that are worth having.

whitespace-mode

It's good to know where your whitespace is! whitespace requires you to see it.

          (use-package whitespace
            :diminish global-whitespace-mode
            :init
            (setq whitespace-style '(face spaces empty tabs newline trailing space-mark tab-mark
                  space-before-tab space-after-tab newline-mark))
            :config
  (require 'color)
  (let* ((ws-lighten 30) ;; Amount in percentage to lighten up black.
         (ws-color (color-lighten-name "#000000" ws-lighten)))
    (custom-set-faces
     `(whitespace-newline                ((t (:foreground ,ws-color))))
     `(whitespace-missing-newline-at-eof ((t (:foreground ,ws-color))))
     `(whitespace-space                  ((t (:foreground ,ws-color))))
     `(whitespace-space-after-tab        ((t (:foreground ,ws-color))))
     `(whitespace-space-before-tab       ((t (:foreground ,ws-color))))
     `(whitespace-tab                    ((t (:foreground ,ws-color))))
     `(whitespace-trailing               ((t (:foreground ,ws-color))))))

  ;; Make these characters represent whitespace.
(setq-default whitespace-display-mappings
      '(
        ;; space -> · else .
        (space-mark 32 [183] [46])
        ;; new line -> ¬ else $
        (newline-mark ?\n [172 ?\n] [36 ?\n])
        ;; carriage return (Windows) -> ¶ else #
        (newline-mark ?\r [182] [35])
        ;; tabs -> » else >
        (tab-mark ?\t [187 ?\t] [62 ?\t])))
;; Don't enable whitespace for.
(setq-default whitespace-global-modes
              '(not shell-mode
                    help-mode
                    magit-mode
                    magit-diff-mode
                    ibuffer-mode
                    dired-mode
                    occur-mode))

;; (global-whitespace-mode 1)
            )
            ;; (global-whitespace-mode 1))
          ;;add a marker to empty lines, like vim does
          ;;(setq-default indicate-empty-lines nil)

electric-pair-mode

Matching closed brackets are inserted for any typed open bracket.

  (electric-pair-mode 1)
;; disable {} auto pairing in electric-pair-mode for web-mode
(add-hook
 'org-mode-hook
 (lambda ()
   (setq-local electric-pair-inhibit-predicate
               `(lambda (c)
                  (if (char-equal c ?<) t (,electric-pair-inhibit-predicate c))))))

rainbow-delimeters-mode

Add some colors to them delimiters!

  ;; (require 'color)
  ;; (defun gen-col-list (length s v &optional hval)
  ;;   (cl-flet ( (random-float () (/ (random 10000000000) 10000000000.0))
  ;;           (mod-float (f) (- f (ffloor f))) )
  ;;     (unless hval
  ;;       (setq hval (random-float)))
  ;;     (let ((golden-ratio-conjugate (/ (- (sqrt 5) 1) 2))
  ;;           (h hval)
  ;;           (current length)
  ;;           (ret-list '()))
  ;;       (while (> current 0)
  ;;         (setq ret-list
  ;;               (append ret-list
  ;;                       (list (apply 'color-rgb-to-hex (color-hsl-to-rgb h s v)))))
  ;;         (setq h (mod-float (+ h golden-ratio-conjugate)))
  ;;         (setq current (- current 1)))
  ;;       ret-list)))

  ;; (defun set-random-rainbow-colors (s l &optional h)
  ;;   ;; Output into message buffer in case you get a scheme you REALLY like.
  ;;   ;; (message "set-random-rainbow-colors %s" (list s l h))
  ;;   (interactive)
  ;;   (rainbow-delimiters-mode t)

  ;;   ;; Show mismatched braces in bright red.
  ;;   (set-face-background 'rainbow-delimiters-unmatched-face "red")

  ;;   ;; Rainbow delimiters based on golden ratio
  ;;   (let ( (colors (gen-col-list 9 s l h))
  ;;          (i 1) )
  ;;     (let ( (length (length colors)) )
  ;;       ;;(message (concat "i " (number-to-string i) " length " (number-to-string length)))
  ;;       (while (<= i length)
  ;;         (let ( (rainbow-var-name (concat "rainbow-delimiters-depth-" (number-to-string i) "-face"))
  ;;                (col (nth i colors)) )
  ;;           ;; (message (concat rainbow-var-name " => " col))
  ;;           (set-face-foreground (intern rainbow-var-name) col))
  ;;         (setq i (+ i 1))))))


(use-package rainbow-delimiters
  :config
  (set-face-background 'rainbow-delimiters-unmatched-face "red")
  (set-face-background 'rainbow-delimiters-mismatched-face "orange")
  :hook ((prog-mode) . rainbow-delimiters-mode)
  )
  ;; (use-package rainbow-delimiters :commands rainbow-delimiters-mode :hook ...
  ;;   :init
  ;;   (setq rainbow-delimiters-max-face-count 16)
  ;;   (set-random-rainbow-colors 0.6 0.7 0.5)
  ;;   (add-hook 'prog-mode-hook 'rainbow-delimiters-mode))

show-paren-mode

Hilight your other delimiter.

(show-paren-mode 1)
(setq show-paren-delay 0)
(require 'paren)
(set-face-background 'show-paren-match (face-background 'default))
(set-face-background 'show-paren-mismatch (face-background 'default))
(set-face-foreground 'show-paren-match "#488")
(set-face-foreground 'show-paren-mismatch "#f44")
(set-face-attribute 'show-paren-match nil :weight 'extra-bold)

which-key-mode

Show keybindings, because we have a LOT of them.

(use-package which-key              ;;; Display available keybindings in popup
  :diminish which-key-mode
  :config
  ;; sort single chars alphabetically P p Q q
  (setq which-key-sort-order 'which-key-key-order-alpha)
  (setq which-key-idle-delay 0.5)
  (which-key-setup-side-window-right-bottom)
  (which-key-mode))

company-mode

company mode is an intellisenseish complete anywhere mode.

(use-package company
  :bind (("C-." . company-complete))
  :config (add-hook 'after-init-hook 'global-company-mode)
  (global-company-mode 1))
(use-package company-box
  :after company
 ;; :straight (  :type git :host nil :repo "")
 :hook (company-mode . company-box-mode))

ispell-minor-mode

Ispell is nice; aspell is nicer; one day hunspell might also be nice.

(setq ispell-program-name (expand-file-name
                          (shell-command-to-string "which aspell")))

modeline

This configures the modeline display.

column number

(column-number-mode 1)

shorten prompts

(fset 'yes-or-no-p 'y-or-n-p)

anzu

(use-package anzu
               :config
               (global-anzu-mode 1))

##+INCLUDE: "../nougat/emacs/undo-tree.org" :minlevel 2

overwrite kill-buffer with kill-with-diff

When killing a buffer that has unsaved changes, there's no way to see the diff. This provides one. Based on this SO answer.

(defun diff-kill-this-buffer ()
  (interactive)
  (catch 'quit
    (save-window-excursion
      (let (done)
        (when (and buffer-file-name (buffer-modified-p))
          (while (not done)
            (let ((response (read-char-choice
                             (format "Save file %s? (y, n, d, q) " (buffer-file-name))
                             '(?y ?n ?d ?q))))
              (setq done (cond
                          ((eq response ?q) (throw 'quit nil))
                          ((eq response ?y) (save-buffer) t)
                          ((eq response ?n) (set-buffer-modified-p nil) t)
                          ((eq response ?d) (diff-buffer-with-file) nil))))))
        (kill-buffer (current-buffer))))))
(global-set-key [remap kill-buffer] 'diff-kill-this-buffer)

flycheck

Flycheck is a modern on-the-fly syntax checking extension for GNU Emacs, intended as replacement for the older Flymake extension which is part of GNU Emacs.

install

  (use-package flycheck
  ;;  :ensure t
    :straight (flycheck :type git :host github :repo "flycheck/flycheck")
    :bind (("M-8" . flycheck-next-error)
           ("M-*" . flycheck-previous-error))
    ;; :init (global-flycheck-mode)
  )
  (use-package flycheck-eglot
      :after (flycheck eglot)
:config
(global-flycheck-eglot-mode 1))
(use-package flycheck-projectile
 :straight ( flycheck-projectile :type git :host nil :repo "https://github.com/nbfalcon/flycheck-projectile")
 :config
 )

shell-check

(add-hook 'sh-mode-hook 'flycheck-mode)

proselint

(flycheck-add-mode 'proselint 'org-mode)

all the icons

(use-package all-the-icons
:config (require 'all-the-icons))

dired

dired is emacs' built in file manager.

dired-sort-dirs

Dired sorting should lump directories first because I'm used to it.

    (setq dired-listing-switches "-alvh --group-directories-first")
(eval-after-load "dired" '(progn (define-key dired-mode-map (kbd "C-c w") 'dired-toggle-read-only)))

transparently create directories when renaming

(defadvice dired-mark-read-file-name (after rv:dired-create-dir-when-needed (prompt dir op-symbol arg files &optional default) activate)
  (when (member op-symbol '(copy move))
    (let ((directory-name (if (< 1 (length files))
                              ad-return-value
                              (file-name-directory ad-return-value))))
      (when (and (not (file-directory-p directory-name))
                 (y-or-n-p (format "directory %s doesn't exist, create it?" directory-name)))
        (make-directory directory-name t)))))

compute size with du

from oremacs, circa 2015:

  (defun dired-get-size ()
    (interactive)
    (let ((files (dired-get-marked-files)))
      (with-temp-buffer
        (apply 'call-process "/usr/bin/du" nil t nil "-sch" files)
        (message
         "Size of all marked files: %s"
         (progn
           (re-search-backward "\\(^[ 0-9.,]+[A-Za-z]+\\).*total$")
           (match-string 1))))))

;;(define-key dired-mode-map (kbd "z") 'dired-get-size)

wdired move to beginning of filename

  (defun my-wdired-move-bof ()
    "Move point to the beginning of the current WDired file name."
    (interactive)
    (dired-move-to-filename))
(add-hook 'wdired-mode-hook
          (lambda () (local-set-key (kbd "C-a") 'my-wdired-move-bof)))

###+INCLUDE: "../nougat/emacs/popwin.org" :minlevel 2

whitespace-butler

ws-butler is an unobtrusive way to trim spaces from the end of lines, which is great.

(use-package ws-butler
  :straight (ws-butler :type git
             :host github
             :repo "lewang/ws-butler")
  :hook ((org-mode markdown-mode yaml-ts-mode) . ws-butler-mode)
  )
;; (require 'ws-butler)
;; enable for programming modes
;; (add-hook 'org-mode-hook #'ws-butler-mode)
;; (add-hook 'markdown-mode-hook #'ws-butler-mode)
;; or enable everywhere
;; (ws-butler-global-mode)

avy

Avy is awesome; set some keybindings.

(use-package avy
  ;; :straight (avy :type git :host nil :repo "https://github.com/abo-abo/avy.git")
  :config
  (defun grym/avy-goto-after-char-timer ()
    ;; put point _after_ selected char, not before
    (interactive)
    (avy-goto-char-timer)
    (forward-symbol 1))
  (global-set-key (kbd "C-;") 'avy-goto-char-timer)
  (global-set-key (kbd "C-M-;") 'grym/avy-goto-after-char-timer)
  (global-unset-key (kbd "C-'"))
  (global-set-key (kbd "C-'") 'avy-goto-line)
  (global-set-key (kbd "M-g w") 'avy-goto-word-1)
  )

built-in tweaks

completion-preview

save-place

(use-package saveplace
  :straight (:type built-in)
  :config
  (save-place-mode 1)
  (setq save-place-forget-unreadable-files nil
  ))

hippie-expand

(global-set-key (kbd "M-/") 'hippie-expand)
(setq hippie-expand-try-functions-list
      '(try-expand-dabbrev
        try-expand-dabbrev-all-buffers
        try-expand-dabbrev-from-kill
        try-complete-file-name-partially
        try-complete-file-name
        try-expand-all-abbrevs
        try-expand-list
        try-expand-line
        try-complete-lisp-symbol-partially
        try-complete-lisp-symbol))

Highlight row, column marker

Hilight the row the mark is on.

(global-hl-line-mode)
;; (set-face-background 'hl-line "gray22")
; (add-hook 'prog-mode-hook #'display-fill-column-indicator-mode)
; (add-hook 'org-mode-hook #'display-fill-column-indicator-mode)
; (set-face-foreground 'fill-column-indicator "gray22")
; n(setq display-fill-column-indicator-character "|")

display line numbers

Line numbers are good.

:    ;; (global-display-line-numbers-mode)
(add-hook 'prog-mode-hook #'display-line-numbers-mode)
  ;;  (setq display-line-numbers 'relative)
    (setq-default display-line-numbers-width 4)

auto-revert mode

Auto-revert acts a lot more like modern editors (sublime, etc), which helps a great deal when editing files with potentially many editors at once.

(global-auto-revert-mode t)

split window sensibly

From SO, this is a way to prefer vertical splits over horizontal ones depending on window size.

(defun my-split-window-sensibly (&optional window)
  (let ((window (or window (selected-window))))
    (or (and (window-splittable-p window t)
             ;; Split window horizontally.
             (with-selected-window window
               (split-window-right)))
        (and (window-splittable-p window)
             ;; Split window vertically.
             (with-selected-window window
               (split-window-below)))
        (and (eq window (frame-root-window (window-frame window)))
             (not (window-minibuffer-p window))
             ;; If WINDOW is the only window on its frame and is not the
             ;; minibuffer window, try to split it horizontally disregarding
             ;; the value of `split-width-threshold'.
             (let ((split-width-threshold 0))
               (when (window-splittable-p window t)
                 (with-selected-window window
                   (split-window-right))))))))

(setq split-window-preferred-function 'my-split-window-sensibly)

ibuffer

;;(use-package ibuffer-projectile)

    (global-set-key (kbd "C-x C-b") 'ibuffer)

    (setq ibuffer-saved-filter-groups
          (quote
           (("default"
             ("dired" (mode . dired-mode))
             ("org"
              (or (name . "^.*org$")
                  (name . "^\*Org")))
             ("shell"
              (or (mode . eshell-mode)
                  (mode . shell-mode)
                  (mode . vterm-mode)))
             ("documents"
              (or (mode . pdf-view-mode)))
             ("git"
              (or (name . "^\*magit")
                  (name . "^\magit")))
             ("rg" (mode . rg-mode))
             ("programming"
              (or (mode . python-mode)
                  (mode . python-ts-mode)
                  (mode . c++-mode)
                  (mode . makefile-gmake-mode)
                  (mode . latex-mode)
                  (mode . toml-mode)
                  (mode . toml-ts-mode)
                  (mode . gnu-makefile-mode)))
             ("lsp"
              (or (name . "^\*EGLOT*")
                  (name . "^\\*lsp*\\*$")
                  (name . "^\\*pyls*\\*$")))
             ;; ("scratch"(or(name . (rx-to-string ))))
             ("data / configs "
              (or (mode . json-mode)
                  (mode . yaml-mode)))
             ;; ("Pinboard" (or
             ;;              (name . "^\*Pinboard")))
             ("help/docs"
              (or (mode . helpful-mode)
                  (name . "^\\*Help\\*$")
                  (mode . woman-mode)
                  (mode . Man-mode)
                  (mode . Info-mode)))
             ("emacs"
              (or (name . "^\\*scratch\\*$")
                  (name . "^\\*Messages\\*$")
                  (name . "^\\*straight-process\\*$")
                  (name . "^\\*Calendar\\*$")))))))
    (add-hook 'ibuffer-mode-hook
    (lambda ()
    (ibuffer-auto-mode 1)
    (ibuffer-switch-to-saved-filter-groups "default")))

    (require 'ibuf-ext)
    ;; (add-to-list 'ibuffer-never-show-predicates "^\\*")
    ;; don't show these
    ;(add-to-list 'ibuffer-never-show-predicates "zowie")
    ;; Don't show filter groups if there are no buffers in that group
    (setq ibuffer-show-empty-filter-groups nil)

    ;; Don't ask for confirmation to delete marked buffers
    (setq ibuffer-expert t)
(defun ajv/human-readable-file-sizes-to-bytes (string)
  "Convert a human-readable file size into bytes."
  (interactive)
  (cond
   ((string-suffix-p "G" string t)
    (* 1000000000 (string-to-number (substring string 0 (- (length string) 1)))))
   ((string-suffix-p "M" string t)
    (* 1000000 (string-to-number (substring string 0 (- (length string) 1)))))
   ((string-suffix-p "K" string t)
    (* 1000 (string-to-number (substring string 0 (- (length string) 1)))))
   (t
    (string-to-number (substring string 0 (- (length string) 1))))
   )
  )

(defun ajv/bytes-to-human-readable-file-sizes (bytes)
  "Convert number of bytes to human-readable file size."
  (interactive)
  (cond
   ((> bytes 1000000000) (format "%10.1fG" (/ bytes 1000000000.0)))
   ((> bytes 100000000) (format "%10.0fM" (/ bytes 1000000.0)))
   ((> bytes 1000000) (format "%10.1fM" (/ bytes 1000000.0)))
   ((> bytes 100000) (format "%10.0fk" (/ bytes 1000.0)))
   ((> bytes 1000) (format "%10.1fk" (/ bytes 1000.0)))
   (t (format "%10db" bytes)))
  )

;; Use human readable Size column instead of original one
(define-ibuffer-column size-h
  (:name "Size"
         :inline t
         :summarizer
         (lambda (column-strings)
           (let ((total 0))
             (dolist (string column-strings)
               (setq total
                     ;; like, ewww ...
                     (+ (float (ajv/human-readable-file-sizes-to-bytes string))
                        total)))
             (ajv/bytes-to-human-readable-file-sizes total)))    ;; :summarizer nil
         )
  (ajv/bytes-to-human-readable-file-sizes (buffer-size)))

;; Modify the default ibuffer-formats
(setq ibuffer-formats
      '((mark modified read-only locked " "
              (name 20 20 :left :elide)
              " "
              (size-h 11 -1 :right)
              " "
              (mode 16 16 :left :elide)
              " "
              filename-and-process)
        (mark " "
              (name 16 -1)
              " " filename)))
(use-package
  ibuffer-vc
  :config (add-hook
           'ibuffer-mode-hook
           (lambda ()
             (local-set-key
              "/v"
              'ibuffer-vc-set-filter-groups-by-vc-root))))

zippy

Restore M-x yow.

;;; yow.el --- quote random zippyisms  -*- lexical-binding: t; -*-

;; Copyright (C) 1993-1995, 2000-2024 Free Software Foundation, Inc.

;; Author: Richard Mlynarik
;; Maintainer: emacs-devel@gnu.org
;; Keywords: games
;; Obsolete-since: 24.4

;; This file is part of GNU Emacs.

;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:

;; Important pinheadery for GNU Emacs.
;; This file is obsolete.  For similar functionality, see
;; fortune.el and cookie1.el.

;;; Code:

(require 'cookie1)

(defgroup yow nil
  "Quote random zippyisms."
  :prefix "yow-"
  :group 'games)

(defcustom yow-file (expand-file-name "yow.lines" data-directory)
   "File containing pertinent pinhead phrases."
  :type 'file)

(defconst yow-load-message "Am I CONSING yet?...")
(defconst yow-after-load-message "I have SEEN the CONSING!!")

;;;###autoload
(defun yow (&optional insert display)
  "Return or display a random Zippy quotation.  With prefix arg, insert it."
  (interactive "P\np")
  (let ((yow (cookie yow-file yow-load-message yow-after-load-message)))
    (cond (insert
           (insert yow))
          ((not display)
           yow)
          (t
           (message "%s" yow)))))

(defsubst read-zippyism (prompt &optional require-match)
  "Read a Zippyism from the minibuffer with completion, prompting with PROMPT.
If optional second arg is non-nil, require input to match a completion."
  (cookie-read prompt yow-file yow-load-message yow-after-load-message
               require-match))

;;;###autoload
(defun insert-zippyism (&optional zippyism)
  "Prompt with completion for a known Zippy quotation, and insert it at point."
  (interactive (list (read-zippyism "Pinhead wisdom: " t)))
  (insert zippyism))

;;;###autoload
(defun apropos-zippy (regexp)
  "Return a list of all Zippy quotes matching REGEXP.
If called interactively, display a list of matches."
  (interactive "sApropos Zippy (regexp): ")
  (cookie-apropos regexp yow-file (called-interactively-p 'interactive)))

;; Yowza!! Feed zippy quotes to the doctor. Watch results.
;; fun, fun, fun. Entertainment for hours...
;;
;; written by Kayvan Aghaiepour

(declare-function doctor-ret-or-read "doctor" (arg))

;;;###autoload
(defun psychoanalyze-pinhead ()
  "Zippy goes to the analyst."
  (interactive)
  (cookie-doctor yow-file))

(provide 'yow)

;;; yow.el ends here
(require 'yow nil t)
(setq yow-file (concat user-emacs-directory (file-name-as-directory "emacs-nougat") (file-name-as-directory "resources") "yow.lines"))

delete-selection-mode on yank with active region

(delete-selection-mode t)

make directories on the fly

(defadvice find-file (before make-directory-maybe (filename &optional wildcards) activate)
  "Create parent directory if not exists while visiting file."
  (unless (file-exists-p filename)
    (let ((dir (file-name-directory filename)))
      (unless (file-exists-p dir)
        (make-directory dir t))))) ;; todo change this to prompt?

clickable links

(goto-address-mode t)

time zones

  ;; https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
(setq display-time-world-list '(
                                ("PST8PDT" "Seattle") ;; yes DST
                                ("Canada/Pacific" "Vancouver")
                                ("CST6CDT" "Houston") ;; yes DST
                                ("EST5EDT" "**Boston**") ;; yes DST
                                ("Europe/Warsaw" "Warsaw")
                                ;; ("Europe/Moscow" "Moscow")
                                ;; ("Asia/Saigon" "Saigon")
                                ("Pacific/Auckland" "Christchurch")
                                ))
  (setq display-time-world-time-format
        "%Z\t%a %d %b %R")

view-mode

 (use-package view
   :straight (:type built-in)
   :config
   (setq view-read-only t)
   :hook view-mode .
(lambda ()
  (cond ((derived-mode-p 'org-mode)
         (define-key view-mode-map (kbd "p") 'org-previous-visible-heading)
         (define-key view-mode-map (kbd "n") 'org-next-visible-heading)
         (define-key view-mode-map (kbd "o") 'consult-org-heading))
        ((derived-mode-p 'markdown-mode)
         (define-key view-mode-map (kbd "p") 'markdown-outline-previous)
         (define-key view-mode-map (kbd "n") 'markdown-outline-next))
        ((derived-mode-p 'html-mode)
         (define-key view-mode-map (kbd "p") 'sgml-skip-tag-backward)
         (define-key view-mode-map (kbd "n") 'sgml-skip-tag-forward))
        ((derived-mode-p 'python-mode)
         (define-key view-mode-map (kbd "p") 'python-nav-backward-block)
         (define-key view-mode-map (kbd "n") 'python-nav-forward-block))
        ((derived-mode-p 'emacs-lisp-mode)
         (define-key view-mode-map (kbd "p") 'backward-sexp)
         (define-key view-mode-map (kbd "n") 'forward-sexp))
        ((derived-mode-p 'makefile-mode)
         (define-key view-mode-map (kbd "p") 'makefile-previous-dependency)
         (define-key view-mode-map (kbd "n") 'makefile-next-dependency))
        ((derived-mode-p 'c-mode)
         (define-key view-mode-map (kbd "p") 'c-beginning-of-defun)
         (define-key view-mode-map (kbd "n") 'c-end-of-defun))
        (t
         (define-key view-mode-map (kbd "p") 'scroll-down-command)
         (define-key view-mode-map (kbd "n") 'scroll-up-command)))))

flyspell

(use-package flyspell
  :straight (:type built-in)
  :config
  (setq ispell-program-name (executable-find "aspell"))
  (setq ispell-extra-args '("--lang=en_US"))
  )

window management keybinds

(global-set-key (kbd "M-0")   'delete-window)

dictionary

https://mbork.pl/2017-01-14_I'm_now_using_the_right_dictionary see also https://www.masteringemacs.org/article/wordsmithing-in-emacs emacs can speak dictd, so installing dictd and gcide gives us webster's 1913 at the OS level. Jargon file can also be installed this way. (foldoc) .

;; mandatory, as the dictionary misbehaves!
(setq switch-to-buffer-obey-display-actions t)
(add-to-list 'display-buffer-alist
   '("^\\*Dictionary\\*" display-buffer-in-side-window
     (side . right)
     (window-width . 70)))

tramp

(with-eval-after-load "tramp"
(add-to-list 'tramp-remote-path 'tramp-own-remote-path))

mouse movement

  (global-set-key [mouse-8] 'previous-buffer)
(global-set-key [mouse-9] 'next-buffer)

tab navigation

;; (setq tab-always-indent 'nil)
(setq tab-always-indent 'complete)
(setq completion-cycle-threshold t)

silence native-comp warnings

;; shut up all the warnings, god
(setq native-comp-async-report-warnings-errors 'silent)

recentf mode

(recentf-mode 1)
(setopt recentf-max-saved-items nil)
(setopt recentf-auto-cleanup 'never)
(global-set-key (kbd "C-S-t") #'consult-recent-file)

mouse behavior

(setq mouse-yank-at-point t)

uniquify

(require 'uniquify)
(use-package minimap
 )

dimmer

(use-package dimmer
 :straight (dimmer :type git :host nil :repo "https://github.com/gonewest818/dimmer.el")
 :config
 (dimmer-configure-which-key)
 (dimmer-configure-magit)
(setq dimmer-adjustment-mode :background)
(setq dimmer-fraction 0.05)
;; (add-to-list 'dimmer-exclusion-regexp-list "^\\*imenu-list\\*$")
(setq dimmer-use-colorspace :rgb)
 (dimmer-mode t)
 )

with-editor

(use-package with-editor
  :straight (with-editor :type git :host nil :repo "https://github.com/magit/with-editor")
  :config

  (cl-defun with-editor-export-editor-eat (process &optional (envvar "EDITOR"))
    "Like `with-editor-export-editor', but for `eat-exec-hook'."
    (cond
     ((derived-mode-p 'eat-mode)
      (if with-editor-emacsclient-executable
          (let ((with-editor--envvar envvar)
                (process-environment process-environment))
            (with-editor--setup)
            (while (accept-process-output process 0.1))
            (when-let ((v (getenv envvar)))
              (eat-term-send-string eat-terminal (format " export %s=%S" envvar v))
              (eat-self-input 1 'return))
            (when-let ((v (getenv "EMACS_SERVER_FILE")))
              (eat-term-send-string eat-terminnal (format " export EMACS_SERVER_FILE=%S" v))
              (eat-self-input 1 'return))
            (eat-term-send-string eat-terminal "clear")
            (eat-self-input 1 'return))
        (error "Cannot use sleeping editor in this buffer")))
     (t (error "Cannot export environment variables in this buffer")))
    (message "Successfully exported %s" envvar))

  (add-hook 'eat-exec-hook #'with-editor-export-editor-eat)

  (keymap-global-set "<remap> <async-shell-command>"
                     #'with-editor-async-shell-command)
  (keymap-global-set "<remap> <shell-command>"
                     #'with-editor-shell-command)
  )

whisper.el

Use whisper.cpp to offer transcriptions in emacs.

(use-package whisper
  :straight (whisper :type git :host nil :repo "https://github.com/natrys/whisper.el")
  :bind ("<XF86Launch9>" . whisper-run)
  :config
  (setq whisper-install-whispercpp 'manual)
  (setq whisper-install-directory "~/.local/bin/"))

info-colors

(use-package info-colors
  :config
  (add-hook 'Info-selection-hook 'info-colors-fontify-node)
 )

windmove and friends

(use-package windmove)
(use-package transpose-frame)
(use-package ace-window
  ;; :bind* (("C-<tab>" . ace-window))  ;; bind* overrides other bindings, i think
  :config (setq aw-scope 'frame))
;; (require 'windmove)
;; (require 'ace-window)

switchy window

(use-package switchy-window
  :ensure t
  :custom (switchy-window-delay 1.5) ;; That's the default value.
  :bind (:map switchy-window-minor-mode-map
                        ;; Bind to separate key...
                        ;; ...or as `other-key' substitute (C-x o).
                        ;; ("<remap> <other-window>" . switchy-window))
                        ("C-<tab>" . switchy-window))

  :init
  (switchy-window-minor-mode))

beacon

(use-package beacon
 :config
 (beacon-mode t
 ))

breadcrumbs

(use-package breadcrumb
 :straight (breadcrumb :type git :host nil :repo "https://github.com/joaotavora/breadcrumb")
 :config
 (breadcrumb-mode
 )
 )

casual

(use-package casual
 :straight (casual :type git :host nil :repo "https://github.com/kickingvegas/casual")
 ;; :after (ibuffer calc dired)
 :bind ((:map dired-mode-map ("C-o" . casual-dired-tmenu))
        (:map calc-mode-map ("C-o" . casual-calc-tmenu))
        (:map Info-mode-map ("C-o" . casual-info-tmenu))
        (:map isearch-mode-map ("<f2>" . casual-isearch-tmenu))
        (:map ibuffer-mode-map
              ("C-o" . casual-ibuffer-tmenu)
              ("F" . casual-ibuffer-filter-tmenu)
              ("s" . casual-ibuffer-sortby-tmenu))
        )
 :config
 ;; (global-set-key (kbd"C-c o") 'casual-editkit-main-tmenu)
 )

comment-dwim-2

(use-package comment-dwim-2
  :straight (comment-dwim-2 :type git :host github :repo "remyferre/comment-dwim-2")
  :config
 (global-set-key (kbd "M-;") 'comment-dwim-2)
 (define-key org-mode-map (kbd "M-;") 'org-comment-dwim-2))

crux

(use-package crux
  :straight (crux :type git
                  :host github
                  :repo "bbatsov/crux")
  :config
    (global-set-key (kbd "C-<backspace>") #'crux-kill-line-backwards)
    (global-set-key (kbd "C-a") #'crux-move-beginning-of-line)
    (global-set-key (kbd "C-M-j") #'crux-eval-and-replace))

devil-mode

(use-package devil
 :straight (devil :type git :host nil :repo "https://github.com/susam/devil")
 :config
  (global-devil-mode)
  (global-set-key (kbd "C-,") 'global-devil-mode))

diff-hl

diff-hl is a "git gutter": it hilights changed lines on the left hand side of a buffer.

(use-package diff-hl
 :straight (diff-hl :type git
                    :host github
                    :repo "dgutov/diff-hl")
 :config (global-diff-hl-mode))

dired configurations

dired-open-with

(use-package
  dired-open-with
  :straight (dired-open-with
             :type git
             :host nil
             :repo "https://github.com/FrostyX/dired-open-with")
  :bind (:map dired-mode-map
              ("O" . dired-open-with)))

dired-hacks

(defun grym-dired-filter-group-mode-off ()
  (interactive)
  (dired-filter-group-mode -1))
(defun grym-dired-filter-group-mode-on ()
  (interactive)
  (dired-filter-group-mode 1))

(use-package dired-hacks-utils)
(use-package dired-open
  :bind (:map dired-mode-map
              ("O" . dired-open-with)))

(use-package dired-subtree
  :after dired
  :bind (:map dired-mode-map
              ("i" . dired-subtree-insert)
              (";" . dired-subtree-remove))
  )

(use-package dired-filter
  :after    dired
  :bind (:map dired-mode-map
              ("h" . dired-filter-by-dot-files)
              ("/" . dired-filter-map)
              ("f" . grym-dired-filter-group-mode-off)
              ;; ("s" . grym/dwim-shell-command-dragon-marked-files)
              )
  :hook ((dired-mode . dired-filter-mode)
         (dired-mode . dired-filter-group-mode)
         (dired-mode . dired-filter-by-dot-files)
         )
  :init (setq dired-filter-revert 'never
              dired-filter-group-saved-groups
              '(("default"
                 ("Git"
                  (directory . ".git")
                  (file . ".gitignore")
                  (file . ".gitattributes"))
                 ("Directory"
                  (directory))
                 ("PDF"
                  (extension . "pdf"))
                 ("LaTeX"
                  (extension "tex" "bib" "cls" "sty"))
                 ("Source"
                  (extension "c" "cpp" "hs" "rb" "m" "r" "cs" "el" "lisp" "html" "js" "css"))
                 ("Python"
                  (extension "py" "pyx" "pyc" "whl" "ipynb"))
                 ("Documents"
                  (extension "md" "rst" "txt" "docx" "DOCX" "pptx" "xlsx"))
                 ("Data"
                  (extension "yaml" "toml" "cfg" "json" "xml" "ini" "csv"))
                 ("Packages"
                  (extension "deb" "rpm" "dmg" "iso"))
                 ("Org"
                  (extension . "org"))
                 ("Archives"
                  (extension "zip" "rar" "gz" "bz2" "tar" "tgz"))
                 ("Videos"
                  (extension "mp4" "webm" "mkv" "mov" "avi" "m3u8"))
                 ("Audio"
                  (extension "m4a" "aac" "wav" "mp3" "ogg"))
                 ("Images"
                  (extension "jpg" "JPG" "webp" "png" "PNG" "jpeg" "JPEG" "bmp" "BMP" "TIFF" "tiff" "gif" "GIF" "svg" "HEIC" "heic"))))))


(when (executable-find "avfsd")
  (use-package dired-avfs))
(use-package dired-collapse)

(use-package dired-rainbow
  :after dired
  :config
  (dired-rainbow-define-chmod directory "#6cb2eb" "d.*")
  (dired-rainbow-define html        "#eb5286" ("css" "less" "sass" "scss" "htm" "html" "jhtm" "mht" "eml" "mustache" "xhtml"))
  (dired-rainbow-define xml         "#0c7b2a" ("xml" "xsd" "xsl" "xslt" "wsdl" "bib" "json" "msg" "pgn" "rss" "yaml" "yml" "rdata"))
  (dired-rainbow-define document    "#9561e2" ("docm" "doc" "docx" "odb" "odt" "pdb" "pdf" "ps" "rtf" "djvu" "epub" "odp" "ppt" "pptx"))
  (dired-rainbow-define markdown    "#9561e2" ("org" "etx" "info" "markdown" "md" "mkd" "nfo" "pod" "rst" "tex" "textfile" "txt"))
  (dired-rainbow-define database    "#6574cd" ("xlsx" "xls" "csv" "accdb" "db" "mdb" "sqlite" "nc"))
  (dired-rainbow-define media       "#de751f" ("mp3" "mp4" "m3u8" "MP3" "MP4" "avi" "mpeg" "mpg" "flv" "ogg" "mov" "mid" "midi" "wav" "aiff" "flac" "webm" "mkv" "m4a"))
  (dired-rainbow-define image       "#f66d9b" ("tiff" "tif" "cdr" "gif" "ico" "jpeg" "jpg" "png" "psd" "eps" "svg" "webp" "JPG" "HEIC" "heic"))
  (dired-rainbow-define log         "#c17d11" ("log"))
  (dired-rainbow-define shell       "#f6993f" ("awk" "bash" "bat" "sed" "sh" "zsh" "vim"))
  (dired-rainbow-define interpreted "#38c172" ("py" "ipynb" "rb" "pl" "t" "msql" "mysql" "pgsql" "sql" "r" "clj" "cljs" "scala" "js"))
  (dired-rainbow-define compiled    "#4dc0b5" ("asm" "cl" "lisp" "el" "c" "h" "c++" "h++" "hpp" "hxx" "m" "cc" "cs" "cp" "cpp" "go" "f" "for" "ftn" "f90" "f95" "f03" "f08" "s" "rs" "hi" "hs" "pyc" ".java"))
  (dired-rainbow-define executable  "#8cc4ff" ("exe" "msi"))
  (dired-rainbow-define compressed  "#51d88a" ("7z" "zip" "bz2" "tgz" "txz" "gz" "xz" "z" "Z" "jar" "war" "ear" "rar" "sar" "xpi" "apk" "xz" "tar" "whl" "pt" "pth"))
  (dired-rainbow-define packaged    "#faad63" ("deb" "rpm" "apk" "jad" "jar" "cab" "pak" "pk3" "vdf" "vpk" "bsp"))
  (dired-rainbow-define encrypted   "#620c7b" ("gpg" "pgp" "asc" "bfe" "enc" "signature" "sig" "p12" "pem"))
  (dired-rainbow-define fonts       "#6cb2eb" ("afm" "fon" "fnt" "pfb" "pfm" "ttf" "otf"))
  (dired-rainbow-define partition   "#e3342f" ("dmg" "iso" "bin" "nrg" "qcow" "toast" "vcd" "vmdk" "bak"))
  (dired-rainbow-define vc          "#0074d9" ("git" "gitignore" "gitattributes" "gitmodules"))
  (dired-rainbow-define-chmod executable-unix "#38c172" "-.*x.*"))

(use-package dired-narrow
  :after    dired
  :bind (:map dired-mode-map
              ("\\" . dired-narrow) ;; g reverts
              ;; ("<down>"  . dired-narrow-next-file)
              ;; ("<up>"    . dired-narrow-previous-file)
              ;; ("<right>" . dired-narrow-enter-directory)
              ))
(use-package dired-collapse
  :hook 'dired-mode-hook)
;;preview files in dired
;; (use-package peep-dired
;; :after dired
;;   :bind (:map dired-mode-map
;;               ("P" . peep-dired))
;;   :config (setq peep-dired-ignored-extensions '("mkv" "iso" "mp4"))
;;   (setq peep-dired-cleanup-on-disable t)
;;   (setq peep-dired-cleanup-eagerly t)
;;   (setq peep-dired-enable-on-directories t)
;;   )

dired-quick-sort

(use-package
  dired-quick-sort
  :straight (dired-quick-sort
             :type git
             :host gitlab
             :repo "xuhdev/dired-quick-sort")
  :config (dired-quick-sort-setup))

dired-preview

  • ready-player
    (use-package ready-player
      :straight ( ready-player :type git :host nil :repo "https://github.com/xenodium/ready-player")
      :after dired
      :bind ((:map dired-mode-map ("SPC" . ready-player-toggle-play-stop)))
      :config
      (setq ready-player-autoplay nil)
      (setq ready-player-repeat nil)
      (setq ready-player-thumbnail-max-pixel-height 768)
      )
    
  • dired-preview
    (use-package dired-preview
      :straight (dired-preview :type git :host nil :repo "https://github.com/protesilaos/dired-preview")
      :bind (:map dired-mode-map ("P" . dired-preview-mode))
      ;; :after ready-player
      ;; :hook ((dired-mode . dired-preview-mode)(ready-player-mode . dired-preview-mode))
      :config
      (ready-player-mode t)
      (setq dired-preview-max-size (expt 2 25))
      (setq dired-preview-delay 0)
      (setq dired-preview-ignored-extensions-regexp
              (concat "\\."
                      "\\(gz\\|"
                      "zst\\|"
                      "tar\\|"
                      "xz\\|"
                      "rar\\|"
                      "zip\\|"
                      "iso\\|"
                      "epub"
                      "\\)"))
      )
    

doom-modeline

(use-package doom-modeline
 :straight (doom-modeline :type git :host github :repo "seagle0128/doom-modeline")
 :init (doom-modeline-mode 1)
 :defer 1
 :config
 (setq doom-modeline-icon t)
 (setq doom-modeline-unicode-fallback t)
 (setq doom-modeline-vcs-max-length 24)
 ;; (setq doom-modeline-enable-word-count nil)
 ;; (setq doom-modeline-gnus nil)
 ;; (setq doom-modeline-irc nil)
 ;; (setq doom-modeline-env-version nil)
 )

dwim-shell-command

(use-package
  dwim-shell-command
  :straight (dwim-shell-command
             :type git
             :host github
             :repo "xenodium/dwim-shell-command")
  :config

  (defun grym/dwim-shell-command-thumbnail-with-ffmpeg ()
    "Generate a thumbnail with ffmpeg."
    (interactive)
    (dwim-shell-command-on-marked-files
     "Thumbnail with ffmpeg"
     " ffmpeg -i '<<f>>' -vf  \"thumbnail,scale=640:360\" -frames:v 1 '<<td>>/<<fne>>.png'"
     :utils "ffmpeg"
     :silent-success t))

  (defun grym/dwim-shell-command-feh-marked-files ()
    "View all marked files with feh."
    (interactive)
    (dwim-shell-command-on-marked-files
     "View with feh"
     " feh --auto-zoom --scale-down '<<*>>'"
     :utils "feh"
     :silent-success t))

  (defun grym/dwim-shell-command-oxo-marked-files ()
    "Upload all marked files to 0x0.st"
    (interactive)
    (when (y-or-n-p "upload with oxo?")
    (dwim-shell-command-on-marked-files
     "upload with oxo"
     "oxo files '<<*>>'"
     :utils "oxo")))

  (defun grym/dwim-shell-command-dragon-marked-files ()
    "Share all marked files with dragon."
    (interactive)
    (dwim-shell-command-on-marked-files
     "Share with dragon"
     " dragon --on-top '<<*>>'"
     :utils "dragon"
     :silent-success t))
  )

eat

 (use-package eat
  :straight (eat :type git :host nil :repo "https://codeberg.org/akib/emacs-eat" :files (:defaults "integration"))
  :after with-editor
  :config
(setq eat-query-before-killing-running-terminal 'auto)
(setq eat-kill-buffer-on-exit t)
(setq eat-term-scrollback-size nil))

emojify

  (use-package emojify
  :config
  (when (member "Noto Color Emoji" (font-family-list))
    (set-fontset-font
     t 'symbol (font-spec :family "Noto Color Emoji") nil 'prepend))
  (setq emojify-display-style 'unicode)
  (setq emojify-emoji-styles '(unicode))
  (bind-key* (kbd "C-c e") #'emojify-insert-emoji))


;; (use-package
;;     emojify
;;     ;; :hook (after-init . global-emojify-mode)
;;     :bind ("C-c e" . 'emojify-insert-emoji)
;;     :config (setq emojify-display-style
;;                   'unicode))
;; (setq emojify-emoji-styles)
;; '(unicode))

esses

<2022-04-27 Wed> 21:07  Catie  Not to brag or anything but I wrote a long-ſ-mode for emacs. ſo you can uſe the right letters without having to think about it 21:07  acdw  Catie: I love it 21:07  acdw  link? 21:08  Catie  http://0x0.st/oT5B.el 21:08  acdw  :D :D :D 21:09  Catie  I'm like two bad days away from uſing it when I write emails 21:13  acdw  Catie: do you take pull requeſtſ? 21:14  Catie  acdw: What have you got? 21:15  acdw  nothing right now 21:15  acdw  but i might have ſomething later 21:16  Catie  Pleaſe do! 21:16  acdw  herhehehheheeh 21:16  acdw  this ſparks joy

(define-minor-mode long-s-mode
  "Minor mode for inserting 'ſ' characters")

(defconst +long-s+ ?ſ)
(defconst +short-s+ ?s)

(defun long-s-p (char)
  (char-equal char +long-s+))

(defun short-s-p (char)
  (or (char-equal char +short-s+)))

(defun s-char-p (char)
  (or (long-s-p char)
      (short-s-p char)))

(defun alpha-char-p (char)
  (memq (get-char-code-property char 'general-category)
        '(Ll Lu Lo Lt Lm Mn Mc Me Nl)))

(defun long-s-insert-short-s ()
  (interactive)
  (if (long-s-p (preceding-char))
      (insert-char +short-s+)
    (insert-char +long-s+)))

(defun long-s-insert-space ()
  (interactive)
  (if (long-s-p (preceding-char))
      (progn (delete-backward-char 1)
             (insert-char +short-s+))
    (save-excursion
      (while (not (alpha-char-p (preceding-char)))
        (backward-char))
      (when (long-s-p (preceding-char))
        (delete-backward-char 1)
        (insert-char +short-s+))))
  (insert-char ?\ ))

(defvar long-s-mode-map
  (let ((map (make-sparse-keymap)))
    (set-keymap-parent map (current-global-map))
    (define-key map (kbd "s") #'long-s-insert-short-s)
    (define-key map (kbd "SPC") #'long-s-insert-space)
    map))

(setq long-s-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "s") #'long-s-insert-short-s)
    (define-key map (kbd "SPC") #'long-s-insert-space)
    map))

(unless (seq-some #'(lambda (x) (eq (car x) 'long-s-mode))
                 minor-mode-map-alist)
  (push (cons 'long-s-mode long-s-mode-map)
        minor-mode-map-alist))

(provide 'long-s-mode)

exec-path from shell

This is a GNU Emacs library to ensure environment variables inside Emacs look the same as in the user's shell. This is useful when, like me, you don't really make an effort to keep $PATH manipulation out of your .zshrc.

  (use-package exec-path-from-shell
  :init
  (setq exec-path-from-shell-check-startup-files nil)
  :config (exec-path-from-shell-initialize)
)

fussy

  (when (eq system-type 'darwin)
       (use-package flx-rs
          :after fussy
          :straight
          (flx-rs
           :type git
           :host nil
           :repo "https://github.com/jcs-elpa/flx-rs"
           :files (:defaults "bin"))
          :config
          (setq fussy-score-fn 'fussy-flx-rs-score)
          (flx-rs-load-dyn))

  )
(when (eq system-type 'gnu/linux)
    (use-package sublime-fuzzy
      :after fussy
      :straight
      (sublime-fuzzy
       :type git
       :host nil
       :repo  "https://github.com/jcs-elpa/sublime-fuzzy"
       :files (:defaults "bin"))
      :config
      (setq fussy-score-fn 'fussy-sublime-fuzzy-score)
      (sublime-fuzzy-load-dyn))
  )



          (use-package fussy
           :straight (fussy :type git :host nil :repo "https://github.com/jojojames/fussy")
           :after company
           :config
           (push 'fussy completion-styles)

           ;; (setq fussy-score-fn 'fussy-flx-rs-score)
          (setq
           ;; For example, project-find-file uses 'project-files which uses
           ;; substring completion by default. Set to nil to make sure it's using
           ;; flx.
           completion-category-defaults nil
           completion-category-overrides nil)

          (defun j-company-capf (f &rest args)
        "Manage `completion-styles'."
        (if (length= company-prefix 0)
            ;; Don't use `company' for 0 length prefixes.
            (let ((completion-styles (remq 'fussy completion-styles)))
              (apply f args))
          (let ((fussy-max-candidate-limit 5000)
                (fussy-default-regex-fn 'fussy-pattern-first-letter)
                (fussy-prefer-prefix nil))
            (apply f args))))

      (defun j-company-transformers (f &rest args)
        "Manage `company-transformers'."
        (if (length= company-prefix 0)
            ;; Don't use `company' for 0 length prefixes.
            (apply f args)
          (let ((company-transformers '(fussy-company-sort-by-completion-score)))
            (apply f args))))

      (advice-add 'company-auto-begin :before 'fussy-wipe-cache)
      (advice-add 'company--transform-candidates :around 'j-company-transformers)
      (advice-add 'company-capf :around 'j-company-capf)


      (with-eval-after-load 'eglot
        (add-to-list 'completion-category-overrides
                     '(eglot (styles fussy basic))))

           )

helpful

;; https://github.com/Wilfred/helpful
(use-package helpful
  :after transient
  :init
  (transient-define-prefix helpful-transient ()
    "Helpful commands"
    [["Helpful"
      ("f" "callable" helpful-callable)
      ("v" "variable" helpful-variable)
      ("k" "key" helpful-key)
      ("c" "command" helpful-command)
      ("d" "thing at point" helpful-at-point)]])
  :bind ("M-C-h" . helpful-transient))

indent-bars

 (use-package indent-bars
  :straight (indent-bars :type git :host nil :repo "https://github.com/jdtsmith/indent-bars")
  :hook ((python-mode yaml-mode) . indent-bars-mode)
:custom
(indent-bars-treesit-support t)
(indent-bars-no-descend-string t)
(indent-bars-treesit-ignore-blank-lines-types '("module"))
 (indent-bars-treesit-wrap '((python argument_list parameters ; for python, as an example
                                     list list_comprehension
                                     dictionary dictionary_comprehension
                                     parenthesized_expression subscript)))
 :config

  )


 ;; )

jinx spellchecking

(use-package jinx
  :straight (:host github :repo "minad/jinx" :files (:defaults "jinx-mod.c" "emacs-module.h"))
  :bind (([remap ispell-word] . #'jinx-correct))
  :hook (emacs-startup-hook . global-jinx-mode)
  )

multiple-cursors

Multiple Cursors is an emacs package to provide Sublime Text-like multiple selections.

(use-package region-bindings-mode
  :config (region-bindings-mode-enable))
(use-package expreg
 :straight (expreg :type git :host nil :repo "https://github.com/casouri/expreg")
 :bind* (("M-2" . expreg-expand)
         ("M-3" . expreg-contract))
 :config
 )
(use-package multiple-cursors
  :after region-bindings-mode
  :bind (:map region-bindings-mode-map
              ("a" . mc/mark-all-like-this)
              ("p" . mc/mark-previous-like-this)
              ("n" . mc/mark-next-like-this)
              ("P" . mc/unmark-previous-like-this)
              ("N" . mc/unmark-next-like-this)
              ("[" . mc/cycle-backward)
              ("]" . mc/cycle-forward)
              ("m" . mc/mark-more-like-this-extended)
              ("h" . mc-hide-unmatched-lines-mode)
              ("\\" . mc/vertical-align-with-space)
              ("#" . mc/insert-numbers) ; use num prefix to set the starting number
              ("^" . mc/edit-beginnings-of-lines)
              ("$" . mc/edit-ends-of-lines))
  )
(global-set-key (kbd "C-M-SPC") 'set-rectangular-region-anchor)

olivetti

(use-package
  olivetti
  :hook (Info-mode . olivetti-mode)
  :config (setq olivetti-body-width 120)
  )

jira

(use-package jira
 :straight ( :type git :host nil :repo "https://github.com/unmonoqueteclea/jira.el")
 :config
  (setq jira-base-url (plist-get my/secrets :work-jiralib-url)) ;; Jira instance URL
  (setq jira-username (plist-get my/secrets :work-jira-username)) ;; Jira username (usually, an email)
  ;; API token for Jira
  ;; See https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/
  (setq jira-token (plist-get my/secrets :work-jira-key))
  (setq jira-token-is-personal-access-token nil)
  (setq jira-api-version 3) ;; Version 2 is also allowed
 )

org-jira

(use-package org-jira
 :straight (org-jira :type git :host nil :repo "https://github.com/ahungry/org-jira")
 :config
 (setq jiralib-url (plist-get my/secrets :work-jiralib-url))
 (setq org-jira-working-dir (concat my/org-path-name (plist-get my/secrets :work-jira-working-dir)))
 (setq org-jira-custom-jqls `((:jql ,(plist-get my/secrets :work-org-jira-all-query)
                              :limit 200
                              :filename ,(plist-get my/secrets :work-org-jira-file)))))

pdf-tools

(use-package
  pdf-tools
  :init (pdf-tools-install t)
  (setq-default
   pdf-view-display-size
   'fit-page)
  :config
  ;;https://github.com/vedang/pdf-tools?tab=readme-ov-file#auto-revert
  (add-hook 'TeX-after-compilation-finished-functions #'TeX-revert-document-buffer))

popper

(use-package popper
 :straight ( popper :type git :host nil :repo "https://github.com/karthink/popper")
   :bind (("C-`"   . popper-toggle)
         ("M-`"   . popper-cycle)
         ("C-M-`" . popper-toggle-type))
  :init
  (setq popper-reference-buffers
        '("\\*Messages\\*"
          "Output\\*$"
          "\\*Async Shell Command\\*"
          help-mode
          compilation-mode))
  (popper-mode +1)
  (popper-echo-mode +1)
 )

rainbow-mode

(use-package rainbow-mode
  :hook ((org-mode emacs-lisp-mode yaml-ts-mode python-ts-mode sgml-mode) . rainbow-mode))

rg.el

(use-package rg
 :straight (rg :type git :host github :repo "dajva/rg.el")
 :config
 (rg-enable-menu)
 )

super save

(use-package
  super-save
  :straight (super-save
             :type git
             :host github
             :repo "bbatsov/super-save")
  :config (super-save-mode 1)
  (setq super-save-remote-files nil)
  (setq super-save-all-buffers t)
  (setq super-save-auto-save-when-idle t)
  (setq super-save-idle-duration 1))

unfill

(use-package unfill
 :straight (unfill :type git :host github :repo "purcell/unfill")
 :bind ("C-M-q" . unfill-region)
 :config
 )

unicode-fonts

  (use-package unicode-fonts
    :ensure t
    :config
    (unicode-fonts-setup)
    (set-fontset-font t 'symbol "Noto Color Emoji"))
  ;; Enable colour emoji. 😄 🎉
;; (set-fontset-font t 'symbol "Noto Color Emoji" nil)
;; (set-fontset-font t 'symbol "Segoe UI Emoji" nil 'append)
;; (set-fontset-font t 'symbol "Symbola" nil 'append)

hack up font injection, cf blog.

;; Add Apple Color Emoji to the default symbol fontset used by Emacs
(defun zdx/set-emoji-font ()
  (set-fontset-font "fontset-default" 'symbol "Noto Color Emoji" nil 'prepend))

;; Call the config function once and then remove the handler
(defun zdx/set-emoji-font-in-frame (frame)
  (with-selected-frame frame
    (zdx/set-emoji-font))
  ;; Unregister this hook once its run
  (remove-hook 'after-make-frame-functions
               'zdx/set-emoji-font-in-frame))

;; Attach the function to the hook only if in Emacs server
;; otherwise just call the config function directly
(if (daemonp)
    (add-hook 'after-make-frame-functions
              'zdx/set-emoji-font-in-frame)
  (zdx/set-emoji-font))

visual-regexp

(use-package visual-regexp
  :straight (visual-regexp :type git :host github :repo "benma/visual-regexp.el")
  :config
  )
(use-package
  visual-regexp-steroids
  :after visual-regexp
  :straight (visual-regexp-steroids
             :type git
             :host github
             :repo "benma/visual-regexp-steroids.el")
  :config (define-key global-map (kbd "C-c r")
            'vr/replace)
  (define-key global-map (kbd "C-c q")
    'vr/query-replace)
  ;; if you use multiple-cursors, this is for you:
  (define-key global-map (kbd "C-c m")
    'vr/mc-mark))

vundo

(use-package vundo
 :straight (vundo :type git :host nil :repo "https://github.com/casouri/vundo")
 :config
  (setq vundo-glyph-alist vundo-unicode-symbols)
 )

webpaste

(use-package
  webpaste
  :bind (("C-c C-p C-b" . webpaste-paste-buffer)
         ("C-c C-p C-r" . webpaste-paste-region))
  :config (progn
            (setq webpaste-provider-priority
                  '("bpa.st"
                    "dpaste.org"
                    "dpaste.com")))
  (setq webpaste-paste-confirmation
        t)
  ;; fix emacs-lisp handling for bpa.st, add csharp
  ;; to see list of lexers access https://bpa.st/api/v1/lexer
  (plist-put
   (cdr (assoc "bpa.st"
               webpaste-providers-alist))
   :lang-overrides '((emacs-lisp-mode . "emacs-lisp")
                     ;; (csharp-mode . "csharp")
                     ;; (csharp-ts-mode . "csharp")
                     (python-mode . "python")
                     (python-ts-mode . "python")))
  (require 'cl-lib)
  (cl-defun
      workpaste-paste-buffer-or-region
      (&optional point mark)
    "use internally facing webpaste provider only."
    (interactive "r")
    (let ((webpaste-providers-alist `(("bpaste.work"
                                       :uri ,(plist-get
                                              my/secrets
                                              :work-pastebin-url)
                                       :post-data (("expiry" . "longtime"))
                                       :error-lambda webpaste--providers-error-lambda-no-failover
                                       :post-field-lambda webpaste--providers-pinnwand-request
                                       :lang-overrides ((emacs-lisp-mode . "emacs-lisp")
                                                        (python-ts-mode . "python")
                                                        (json-ts-mode . "json"))
                                       :success-lambda webpaste--providers-pinnwand-success)))
          (webpaste-provider-priority '("bpaste.work")))
      ;; if region is selected
      (if (region-active-p)
          ;; Paste selected region
          (webpaste-paste-region
           point
           mark)
        ;; Else, Paste buffer
        (webpaste-paste-buffer)))))

winner

winner mode

(winner-mode t)

zoom-window

 (use-package zoom-window
  :straight (zoom-window :type git :host github :repo "emacsorphanage/zoom-window")
  :config
(setq zoom-window-mode-line-color "medium sea green"
  ))

zzap-to-char

(use-package zzz-to-char
 :straight (zzz-to-char :type git :host nil :repo "https://github.com/mrkkrp/zzz-to-char")
 :config
 (global-set-key (kbd "M-z") #'zzz-to-char-up-to-char)
 (global-set-key (kbd "M-Z") 'zap-up-to-char)
 )

user functions

consult-line-symbol-at-point

via https://arialdomartini.github.io/consult-line-at-point

(defun consult-line-symbol-at-point ()
  "Search for a line matching the symbol found near point."
  (interactive)
  (consult-line
   (or (thing-at-point 'symbol))))

(global-set-key (kbd "M-s .") #'consult-line-symbol-at-point)
(global-set-key (kbd "M-s M-s .") #'isearch-forward-symbol-at-point)

open dired in homedir

(defun grym/dired-go-home ()
  "Go home"
  (interactive)
  (dired "~" )
  )

kill python repl if running and send buffer

;; kill the python repl if running, start a new repl, and send the current buffer to it as main:
(defun grym/python-kill-and-send-buffer-as-main-interactive ()
  (interactive)
  (let*
    (
      (python-buffer "*Python*")
    )
    (if (get-buffer python-buffer)
      (let ((kill-buffer-query-functions nil))
        (kill-buffer python-buffer)))
    (run-python (python-shell-calculate-command) t t)
    (sit-for 0.2)
;    (run-python)  ; note this does not switch the current buffer
    (python-shell-send-buffer t)))

insert file at point

(defun grym/insert-file-at-point ()
  "Insert a newline, then contents of file at point if possible."
  (interactive)
  (save-excursion
    (insert-file-contents (ffap-file-at-point)))
  (newline))

insert url at point

;; https://gist.github.com/barrosfelipe/a177c63331a877864b499b080eb939fd
(defun insert-url-1 (url insert-func)
  (let* ((tem (funcall insert-func url)))
    (push-mark (+ (point) (car (cdr tem))))))


(defun grym/insert-url (url)
  "Insert contents of URL into buffer after point.
    Set mark after the inserted text.

    This function is meant for the user to run interactively.
    Don't call it from programs!
    Use `url-insert-file-contents' instead.
    \(Its calling sequence is different; see its documentation)."
  (declare (interactive-only url-insert-file-contents))
  (interactive "*sInsert URL: ")
  (insert-url-1 url #'url-insert-file-contents))

handle pinnwand urls differently

(defun grym/get-raw-pinnwand-url (pinnwand-url)
  "Take a pinnwand URL (e.g., bpa.st) and get its raw form."
  (let ((original-url (url-generic-parse-url
                       pinnwand-url)))
    (setf
     (url-filename original-url)
     (concat
      "/raw"
      (url-filename original-url)))
    (url-recreate-url original-url)))

(defun grym/insert-pinnwand-url (url)
  (interactive "*sInsert URL: ")
  (grym/insert-url (grym/get-raw-pinnwand-url url)))

clean empty lines

(defun xah-clean-empty-lines ()
  "Replace repeated blank lines to just 1.
Works on whole buffer or text selection, respects `narrow-to-region'.

URL `http://xahlee.info/emacs/emacs/elisp_compact_empty_lines.html'
Version 2017-09-22 2020-09-08"
  (interactive)
  (let ($begin $end)
    (if (use-region-p)
        (setq $begin (region-beginning) $end (region-end))
      (setq $begin (point-min) $end (point-max)))
    (save-excursion
      (save-restriction
        (narrow-to-region $begin $end)
        (progn
          (goto-char (point-min))
          (while (re-search-forward "\n\n\n+" nil "move")
            (replace-match "\n\n")))))))

precommit

(defun grym/spam-precommit-in-project ()
  "Run pre-commit repeatedly in current project, if pre-commit is installed."
  (interactive)
  (unless (not (executable-find "pre-commit"))
  (let ((precommit-max-tries 6))
    (while (or (/= 0 (call-process "pre-commit" nil "*pre-commit output*" nil "run" "--all-files")) (> 0 precommit-max-tries))
      (setq precommit-max-tries (1- precommit-max-tries))
      ))
    )
)

new scratch buffer in text

  (defun grym/new-scratch-text-buffer ()
  "make a new empty text mode buffer"
(interactive)
  (switch-to-buffer (format-time-string "%d %b %Y %H:%M:%S")))

projectile

projectile is a project interaction library for emacs.

setup

(use-package projectile
  :init
  (setq projectile-enable-caching t)
  (setq projectile-keymap-prefix (kbd "C-c p"))
  :config
  (projectile-mode t))

Override default project directory path.

(setq projectile-project-search-path (list (expand-file-name "~/Projects/")
                                           (expand-file-name "~/lab-notebook")
                                           (expand-file-name (concat user-emacs-directory "emacs-nougat"))))

org-mode customizations

org is life.

look

permit default themes

(setq org-resource-download-policy 'safe)
(setq org-safe-remote-resources (list "https://fniessen.github.io/org-html-themes/org/theme-readtheorg.setup"))

indent by header level

(add-hook 'org-mode-hook #'org-indent-mode)

superscripts

require subscripts to need curlies: camel_case won't display weirdly this way

(setq org-use-sub-superscripts "{}")

org-modern

(set-face-attribute 'org-table nil :inherit 'fixed-pitch)
  (use-package org-modern
   :after org
   :straight (org-modern :type git :host nil :repo "https://github.com/minad/org-modern")
   :config
   (custom-set-variables '(org-modern-table nil))
   (add-hook 'org-mode-hook #'org-modern-mode)
   (add-hook 'org-agenda-finalize-hook #'org-modern-agenda)
   )

feel

auto-fill paragraphs

(add-hook 'org-mode-hook 'turn-on-auto-fill)

respect content on heading insert

If you try to insert a heading in the middle of an entry, don't split it in half, but instead insert the new heading after the end of the current entry.

(setq org-insert-heading-respect-content nil)

ensure one-line between headers

;; (defun org-mode--ensure-one-blank-line ()
;;   (save-excursion (goto-char (point-min))
;;                   (while (re-search-forward "#\\+[a-z_]+\\s-\\*" nil t)
;;                     (replace-match "#+end_src

;; *")
;;                     (call-interactively 'org-previous-visible-heading)
;;                     (call-interactively 'org-cycle)
;;                     (call-interactively 'org-cycle))
;;                   (org-save-outline-visibility t
;;                     (org-mode))))

;; (add-hook 'org-mode-hook
;;           (lambda ()
;;             (add-hook 'before-save-hook 'org-mode--ensure-one-blank-line nil 'make-it-local)))

speed keys

speed keys

(setq org-use-speed-commands t)

ret follows link

(setq org-return-follows-link t)

add languages to babel

(use-package ob-markdown
 :straight (ob-markdown :type git :host github :repo "tnoda/ob-markdown")
 :after markdown-mode
 )
(org-babel-do-load-languages
 'org-babel-load-languages
 '((shell . t)
         (emacs-lisp . t)
         (sqlite . t)
         (python . t)
         (awk . t)
         ;; (markdown . t)
         ;; (restclient . t )
         ))

set default header args

(setq org-babel-default-header-args
      '((:session . "none")
        (:results . "replace")
        (:exports . "code")
        (:cache . "no")
        (:noweb . "no")
        (:hlines . "no")
        (:tangle . "no")))

disable code evaluation prompts

(setq org-confirm-babel-evaluate nil)
(setq org-confirm-shell-link-function nil)
(setq org-confirm-elisp-link-function nil)

##+INCLUDE: "../nougat/packages/org-mode/extras/org-tidy.org" ##+INCLUDE: "../nougat/packages/org-mode/extras/nested-narrowing.org"

LaTeX export settings

use smart quotes when appropriate

Newer org-modes support sane quoting

(setq org-export-with-smart-quotes t)

set latex as lualatex

xelatex (or luatex) are usually provided with texlive. They allow, among other things, native use of truetype fonts.

(setq org-latex-compiler "lualatex")

use minted for code blocks

Minted should be usd to dump codeblocks to PDF. This requires pygments and a working python installation on the path.

Insert line-breaks where needed to avoid PDF overflow.

(setq org-latex-listings 'minted
      org-latex-packages-alist '(("" "minted"))
      org-latex-pdf-process
      '("%latex -shell-escape -interaction nonstopmode -output-directory %o %f"
        "%latex -shell-escape -interaction nonstopmode -output-directory %o %f"))

(setq org-latex-minted-options '(("breaklines" "true")
                                 ("breakanywhere" "true")
                                 ("fontsize" "\\normalsize"))
)

org-capture

set default notes file

(setq org-default-notes-file (expand-file-name
                              (concat my/org-path-name "captures.org")))

bind a key for capture

(global-set-key (kbd "C-c c") 'org-capture)

configure some templates

  (defun org-capture-template-goto-link ()
  "Set point for capturing at what capture target file+headline with headline     set to %l would do."
  (org-capture-put :target (list 'file+headline (nth 1 (org-capture-get :target))     (org-capture-get :annotation)))
  (org-capture-set-target-location))

(setq org-capture-templates
        '(("d"
           "Diary"
           entry
           (file+datetree
            (lambda ()
              (concat
               my/org-path-name
               "diary.org")))
           "* %?")
          ("q" "Quote" entry (file+function (lambda () (concat my/org-path-name "personal/quotes.org")) org-capture-template-goto-link) "** %?\n")
          ("p" "Poem" entry (file+function (lambda () (concat my/org-path-name "personal/poems.org")) org-capture-template-goto-link) "** %?\n")))

htmlize

htmlize is required for exporting org files to html.

(use-package htmlize)

toc-org

toc-org helps maintain an up-to-date table of contents in org files without exporting (useful primarily for readme files on GitHub).

;; (use-package toc-org
;;   :config
;;   (add-hook 'org-mode-hook 'toc-org-enable))

##+INCLUDE: "../nougat/packages/org-mode/extras/ox-jira.org"

ox-gfm

export to github-flavored markdown from the exporter menu with ox-gfm.

(use-package ox-gfm)
(eval-after-load "org"
  '(require 'ox-gfm nil t))

ox-beamer

add beamer export

(require 'ox-beamer)
(setq org-beamer-mode t)
(require 'ox-latex)

##+INCLUDE: "../nougat/packages/org-mode/extras/org-avy.org" ##+INCLUDE: "../nougat/packages/org-mode/extras/org-projectile.org" ##+INCLUDE: "../nougat/packages/org-mode/extras/org-roam.org"

org-download

see more here

(use-package org-download
 :straight (org-download :type git
               :host github
               :repo "abo-abo/org-download")
 :config
 (add-hook 'dired-mode-hook 'org-download-enable)
 )

##+INCLUDE: "../nougat/packages/org-mode/extras/ox-hugo.org"

org babel speed commands

ref mailing list

(defun org-babel-speed-command-hook (keys)
  (when (org-babel-where-is-src-block-head)
    (cdr (assoc keys org-babel-key-bindings))))

ox-clip

(use-package ox-clip
 :straight (ox-clip :type git :host nil :repo "https://github.com/jkitchin/ox-clip")
 :config
 )

org-protocol

(require 'org-protocol)

toggle done on subtask completion

(defun org-summary-todo (n-done n-not-done)
  "Switch entry to DONE when all subentries are done, to TODO otherwise."
  (let (org-log-done org-log-states)   ; turn off logging
    (org-todo (if (= n-not-done 0) "DONE" "TODO"))))

(add-hook 'org-after-todo-statistics-hook #'org-summary-todo)

mixed-pitch

(use-package mixed-pitch
  :hook
  ;; If you want it in all text modes:
  (org-mode . mixed-pitch-mode))

tidy up line breaks between subheadings

(setq org-cycle-separator-lines 0)

get value of link on line

(defun grym/org-kill-block-or-link-at-point ()
  "Kill the body of the block or the link of the link at point.

For blocks, the content is copied if the cursor is in any
position within the block.

+ #+BEGIN_SRC
+ #+BEGIN_EXPORT
+ #+BEGIN_EXAMPLE
+ #+BEGIN_COMMENT
+ #+BEGIN_VERSE
+ #+BEGIN_QUOTE

For links, everything but the description is copied.

from rdrg109"
  (interactive)
  (let* (content
         (element (org-element-context))
         (type (org-element-type element)))
    (cond
     ;; The link is the first condition so that it is copied
     ;; even though it is within a #+BEGIN_VERSE or
     ;; #+BEGIN_QUOTE.
     ;;
     ;; This doesn't consider links within #+BEGIN_SRC,
     ;; #+BEGIN_EXPORT, #+BEGIN_EXAMPLE or #+BEGIN_COMMENT.
     ((eq type 'link)
      (setq content (org-element-property :raw-link element)))
     ((or (eq type 'src-block)
          (eq type 'export-block)
          (eq type 'example-block)
          (eq type 'comment-block))
      (setq content (org-element-property :value element)))
     ;; True if the cursor is in the line containing
     ;; #+BEGIN_VERSE, #+END_VERSE, #+BEGIN_QUOTE or #+END_QUOTE.
     ((or (eq type 'verse-block)
          (eq type 'quote-block))
      (setq content (buffer-substring-no-properties
                     (org-element-property :contents-begin element)
                     (org-element-property :contents-end element))))
     ;; Visit the parents to find out whether we are within a
     ;; #+BEGIN_QUOTE or #+BEGIN_VERSE block.
     ((let (type outermost (parent element))
        (while (setq parent (org-element-property :parent parent))
          (setq outermost parent))
        (setq type (org-element-type outermost))
        (when (or (eq type 'verse-block)
                  (eq type 'quote-block))
          (setq element outermost)))
      (setq content (buffer-substring-no-properties
                     (org-element-property :contents-begin element)
                     (org-element-property :contents-end element))))
     (t
      (error "The element at point couldn't be copied.")))
    (kill-new content)))
  (defun grym/link-on-this-line ()
    "Return the contents of org-style link on this line"
    (interactive)
    (org-next-link)
    (kill-new
     (plist-get
      (cadr
       (org-element-link-parser))
      :raw-link)))

;;(org-defkey org-mouse-map [mouse-2] 'grym/link-on-this-line)

copy and unfill

(defun acdw/copy-region-plain (start end)
  "Copy a region to clipboard.
   Remove all Org formatting, and strip leading and trailing whitespace."
  (interactive "r")
  (let ((s (string-trim
            (buffer-substring-no-properties
             start
             end))))
    (with-temp-buffer
      (insert s)
      (let ((sentence-end-double-space nil))
        (unfill-region
         (point-min)
         (point-max)))
      (copy-region-as-kill
       (point-min)
       (point-max)))))

strikethrough-done

Render org TODO states in the DONE category with struck-through text with a handy color scheme.

(setq org-fontify-done-headline t)
(custom-set-faces
 '(org-done ((t (:foreground "DeepSkyBlue"
                 :weight normal
                 :strike-through t))))
 '(org-headline-done
            ((((class color) (min-colors 16) (background dark))
               (:foreground "LightSalmon" :strike-through t)))))

org-refile

from here

(setq org-agenda-files (list my/org-path-name (concat my/org-path-name (plist-get my/secrets :work-jira-working-dir)(plist-get my/secrets :work-org-jira-file))))
(setq org-refile-targets '((org-agenda-files :maxlevel . 5)))
(setq org-refile-allow-creating-parent-nodes 'confirm)

refile with links (broken)

From this gist, set things up so that org-refile leaves a backlink behind. This is useful to me for my note style right now (as of <2020-07-08 Wed> because i usually want to have a per-date work log tree, but move todos off where they belong under a project.)

;; (setq org-id-link-to-org-use-id t)

;; (defun org-refile--insert-link ( &rest _ )
;;     (org-back-to-heading)
;;     (let* ((refile-region-marker (point-marker))
;;            (source-link (org-store-link nil)))
;;     (org-insert-heading)
;;     (insert source-link)
;;     (goto-char refile-region-marker)))

;; (advice-add 'org-refile
;;             :before
;;             #'org-refile--insert-link)

org-agenda customizations

(setq org-agenda-sorting-strategy '((agenda habit-down time-up priority-down category-keep)
 (todo priority-down category-keep)
 (tags todo-state-down priority-down category-keep)
 (search category-keep)))

org-hook jump to first heading

(defun grym/go-to-first-heading ()
  "Go to the first org heading in a file. Intended to be run as a
hook."
  (interactive)
  (unless (derived-mode-p 'org-mode)
    (user-error "This function is intended to be used in org-mode buffers."))
  ;; h/t irc user rdrg109 for improvements here
  (let ((first-match
         (save-excursion
           (beginning-of-buffer)
           (re-search-forward "^\*+" nil :noerror))))
    ;; Move the cursor if and only if a match is found.
    (if first-match
        ;; if there is a first header, go there
      (progn
      (goto-char first-match)
      (back-to-indentation))
      ;; otherwise, go to the beginning unconditionally
      (beginning-of-buffer
      ))))
(add-hook 'org-mode-hook 'grym/go-to-first-heading)

org-collapse-todo-in-subtree

(defun grym/org-collapse-todo ()
  (interactive)
  (org-map-entries 'outline-hide-subtree "TODO=\"DONE\"" 'tree)
  (org-map-entries 'outline-hide-subtree "TODO=\"ABANDONED\"" 'tree) )

org-archive-subtree-hierarchical

from gist by way of SO

  ;; org-archive-subtree-hierarchical.el
  ;;
  ;; version 0.2
  ;; modified from https://lists.gnu.org/archive/html/emacs-orgmode/2014-08/msg00109.html
  ;; modified from https://stackoverflow.com/a/35475878/259187

  ;; In orgmode
  ;; * A
  ;; ** AA
  ;; *** AAA
  ;; ** AB
  ;; *** ABA
  ;; Archiving AA will remove the subtree from the original file and create
  ;; it like that in archive target:

  ;; * AA
  ;; ** AAA

  ;; And this give you
  ;; * A
  ;; ** AA
  ;; *** AAA
  ;;
  ;; Install file to your include path and include in your init file with:
  ;;
  ;;  (require 'org-archive-subtree-hierarchical)
  ;;  (setq org-archive-default-command 'org-archive-subtree-hierarchical)
  ;;
  (provide 'org-archive-subtree-hierarchical)
  (require 'org-archive)

  (defun org-archive-subtree-hierarchical--line-content-as-string ()
    "Returns the content of the current line as a string"
    (save-excursion
      (beginning-of-line)
      (buffer-substring-no-properties
       (line-beginning-position) (line-end-position))))

  (defun org-archive-subtree-hierarchical--org-child-list ()
    "This function returns all children of a heading as a list. "
    (interactive)
    (save-excursion
      ;; this only works with org-version > 8.0, since in previous
      ;; org-mode versions the function (org-outline-level) returns
      ;; gargabe when the point is not on a heading.
      (if (= (org-outline-level) 0)
          (outline-next-visible-heading 1)
        (org-goto-first-child))
      (let ((child-list (list (org-archive-subtree-hierarchical--line-content-as-string))))
        (while (org-goto-sibling)
          (setq child-list (cons (org-archive-subtree-hierarchical--line-content-as-string) child-list)))
        child-list)))

  (defun org-archive-subtree-hierarchical--org-struct-subtree ()
    "This function returns the tree structure in which a subtree
  belongs as a list."
    (interactive)
    (let ((archive-tree nil))
      (save-excursion
        (while (org-up-heading-safe)
          (let ((heading
                 (buffer-substring-no-properties
                  (line-beginning-position) (line-end-position))))
            (if (eq archive-tree nil)
                (setq archive-tree (list heading))
              (setq archive-tree (cons heading archive-tree))))))
      archive-tree))

  (defun org-archive-subtree-hierarchical ()
    "This function archives a subtree hierarchical"
    (interactive)
    (let ((org-tree (org-archive-subtree-hierarchical--org-struct-subtree))
          (this-buffer (current-buffer))
          (file (abbreviate-file-name
                 (or (buffer-file-name (buffer-base-buffer))
                     (error "No file associated to buffer")))))
      (save-excursion
        (setq location org-archive-location
              afile (car (org-archive--compute-location
                                     (or (org-entry-get nil "ARCHIVE" 'inherit) location)))
              ;; heading (org-extract-archive-heading location)
              infile-p (equal file (abbreviate-file-name (or afile ""))))
        (unless afile
          (error "Invalid `org-archive-location'"))
        (if (> (length afile) 0)
            (setq newfile-p (not (file-exists-p afile))
                  visiting (find-buffer-visiting afile)
                  buffer (or visiting (find-file-noselect afile)))
          (setq buffer (current-buffer)))
        (unless buffer
          (error "Cannot access file \"%s\"" afile))
        (org-cut-subtree)
        (set-buffer buffer)
        (org-mode)
        (goto-char (point-min))
        (while (not (equal org-tree nil))
          (let ((child-list (org-archive-subtree-hierarchical--org-child-list)))
            (if (member (car org-tree) child-list)
                (progn
                  (search-forward (car org-tree) nil t)
                  (setq org-tree (cdr org-tree)))
              (progn
                (goto-char (point-max))
                (newline)
                (org-insert-struct org-tree)
                (setq org-tree nil)))))
        (newline)
        (org-yank)
        (when (not (eq this-buffer buffer))
          (save-buffer))
        (message "Subtree archived %s"
                 (concat "in file: " (abbreviate-file-name afile))))))

  (defun org-insert-struct (struct)
    "TODO"
    (interactive)
    (when struct
      (insert (car struct))
      (newline)
      (org-insert-struct (cdr struct))))

  (defun org-archive-subtree ()
    (org-archive-subtree-hierarchical)
    )

(require 'org-archive-subtree-hierarchical)
(setq org-archive-default-command 'org-archive-subtree-hierarchical)

insert-file at point

(defun grym/insert-org-file-dwim ()
  "Insert file contents of the current org-mode file path at point."
  (interactive)
  (let ((fname-to-insert (thing-at-point 'filename)))
    (when (and (file-exists-p fname-to-insert)
               (equal (file-name-extension
                       fname-to-insert)
                      "org"))
      (save-excursion
        (org-end-of-line)
        (org-newline-and-indent)
        (insert-file fname-to-insert)
        (message
         "Inserting %s"
         (file-name-base
          fname-to-insert)))
      (org-beginning-of-line)
      (kill-line 1)
      (org-demote-subtree))))

vertico/consult/ …

(use-package vertico
  :init
  (vertico-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)

  ;; Optionally enable cycling for `vertico-next' and `vertico-previous'.
  ;; (setq vertico-cycle t)
  )

(use-package vertico-prescient
  :init
  (vertico-prescient-mode))

;; Persist history over Emacs restarts. Vertico sorts by history position.
(use-package savehist
  :init
  (savehist-mode))

;; A few more useful configurations...
(use-package emacs
  :init
  ;; Add prompt indicator to `completing-read-multiple'.
  ;; We display [CRM<separator>], e.g., [CRM,] if the separator is a comma.
  (defun crm-indicator (args)
    (cons (format "[CRM%s] %s"
                  (replace-regexp-in-string
                   "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
                   crm-separator)
                  (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))

;; Optionally use the `orderless' completion style.
(use-package orderless
      :init
      ;; Configure a custom style dispatcher (see the Consult wiki)
      ;; (setq orderless-style-dispatchers '(+orderless-dispatch)
      ;;       orderless-component-separator #'orderless-escapable-split-on-space)
      (setq completion-styles '(orderless basic)
            completion-category-defaults nil
            completion-category-overrides '((file (styles partial-completion)))))
    ;; Enable rich annotations using the Marginalia package
  (use-package marginalia
    ;; Either bind `marginalia-cycle' globally or only in the minibuffer
    :bind (("M-A" . marginalia-cycle)
           :map minibuffer-local-map
           ("M-A" . marginalia-cycle))

    ;; The :init configuration is always executed (Not lazy!)
    :init

    ;; Must be in the :init section of use-package such that the mode gets
    ;; enabled right away. Note that this forces loading the package.
    (marginalia-mode))

      ;; Example configuration for Consult
    (use-package consult
      ;; Replace bindings. Lazily loaded due by `use-package'.
      :bind (;; C-c bindings (mode-specific-map)
             ("C-s" . consult-line)
             ("C-c h" . consult-history)
             ("C-c m" . consult-mode-command)
             ("C-c k" . consult-kmacro)
             ;; C-x bindings (ctl-x-map)
             ("C-x M-:" . consult-complex-command)     ;; orig. repeat-complex-command
             ("C-x b" . consult-buffer)                ;; orig. switch-to-buffer
             ("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window
             ("C-x 5 b" . consult-buffer-other-frame)  ;; orig. switch-to-buffer-other-frame
             ("C-x r b" . consult-bookmark)            ;; orig. bookmark-jump
             ("C-x p b" . consult-project-buffer)      ;; orig. project-switch-to-buffer
             ;; Custom M-# bindings for fast register access
             ("M-#" . consult-register-load)
             ("M-'" . consult-register-store)          ;; orig. abbrev-prefix-mark (unrelated)
             ("C-M-#" . consult-register)
             ;; Other custom bindings
             ("M-y" . consult-yank-pop)                ;; orig. yank-pop
             ;; M-g bindings (goto-map)
             ("M-g e" . consult-compile-error)
             ("M-g f" . consult-flymake)               ;; Alternative: consult-flycheck
             ("M-g g" . consult-goto-line)             ;; orig. goto-line
             ("M-g M-g" . consult-goto-line)           ;; orig. goto-line
             ("M-g o" . consult-outline)               ;; Alternative: consult-org-heading
             ("M-g m" . consult-mark)
             ("M-g k" . consult-global-mark)
             ("M-g i" . consult-imenu)
             ("M-g I" . consult-imenu-multi)
             ;; M-s bindings (search-map)
             ("M-s d" . consult-fd)
             ("M-s D" . consult-locate)
             ("M-s g" . consult-grep)
             ("M-s G" . consult-git-grep)
             ("M-s r" . consult-ripgrep)
             ("M-s l" . consult-line)
             ("M-s L" . consult-line-multi)
             ("M-s m" . consult-multi-occur)
             ("M-s k" . consult-keep-lines)
             ("M-s u" . consult-focus-lines)
             ;; Isearch integration
             ("M-s e" . consult-isearch-history)
             :map isearch-mode-map
             ("M-e" . consult-isearch-history)         ;; orig. isearch-edit-string
             ("M-s e" . consult-isearch-history)       ;; orig. isearch-edit-string
             ("M-s l" . consult-line)                  ;; needed by consult-line to detect isearch
             ("M-s L" . consult-line-multi)            ;; needed by consult-line to detect isearch
             ;; Minibuffer history
             :map minibuffer-local-map
             ("M-s" . consult-history)                 ;; orig. next-matching-history-element
             ("M-r" . consult-history))                ;; orig. previous-matching-history-element

      ;; Enable automatic preview at point in the *Completions* buffer. This is
      ;; relevant when you use the default completion UI.
      :hook (completion-list-mode . consult-preview-at-point-mode)

      ;; The :init configuration is always executed (Not lazy)
      :init

      ;; Optionally configure the register formatting. This improves the register
      ;; preview for `consult-register', `consult-register-load',
      ;; `consult-register-store' and the Emacs built-ins.
      (setq register-preview-delay 0.5
            register-preview-function #'consult-register-format)

      ;; Optionally tweak the register preview window.
      ;; This adds thin lines, sorting and hides the mode line of the window.
      (advice-add #'register-preview :override #'consult-register-window)

      ;; Use Consult to select xref locations with preview
      (setq xref-show-xrefs-function #'consult-xref
            xref-show-definitions-function #'consult-xref)

      ;; Configure other variables and modes in the :config section,
      ;; after lazily loading the package.
      :config

      ;; Optionally configure preview. The default value
      ;; is 'any, such that any key triggers the preview.
      ;; (setq consult-preview-key 'any)
      ;; (setq consult-preview-key (kbd "M-."))
      ;; (setq consult-preview-key (list (kbd "<S-down>") (kbd "<S-up>")))
      ;; For some commands and buffer sources it is useful to configure the
      ;; :preview-key on a per-command basis using the `consult-customize' macro.
      (consult-customize
       consult-theme :preview-key '(:debounce 0.2 any)
       consult-ripgrep consult-git-grep consult-grep
       consult-bookmark consult-recent-file consult-xref
       consult--source-bookmark consult--source-file-register
       consult--source-recent-file consult--source-project-recent-file
       ;; :preview-key (kbd "M-.")
       :preview-key '(:debounce 0.4 any))

      ;; Optionally configure the narrowing key.
      ;; Both < and C-+ work reasonably well.
      (setq consult-narrow-key "<") ;; (kbd "C-+")

      ;; Optionally make narrowing help available in the minibuffer.
      ;; You may want to use `embark-prefix-help-command' or which-key instead.
      ;; (define-key consult-narrow-map (vconcat consult-narrow-key "?") #'consult-narrow-help)

      ;; By default `consult-project-function' uses `project-root' from project.el.
      ;; Optionally configure a different project root function.
      ;; There are multiple reasonable alternatives to chose from.
      ;;;; 1. project.el (the default)
      ;; (setq consult-project-function #'consult--default-project--function)
      ;;;; 2. projectile.el (projectile-project-root)
      ;; (autoload 'projectile-project-root "projectile")
      ;; (setq consult-project-function (lambda (_) (projectile-project-root)))
      ;;;; 3. vc.el (vc-root-dir)
      ;; (setq consult-project-function (lambda (_) (vc-root-dir)))
      ;;;; 4. locate-dominating-file
      ;; (setq consult-project-function (lambda (_) (locate-dominating-file "." ".git")))
    )
    ;; (use-package consult-git-log-grep
    ;;   :custom
    ;;   (consult-git-log-grep-open-function #'magit-show-commit))
(use-package consult-git-log-grep
 :straight (consult-git-log-grep :type git :host nil :repo "https://github.com/ghosty141/consult-git-log-grep")
 :config
 )
  (use-package consult-eglot
   :straight (consult-eglot :type git :host nil :repo "https://github.com/mohkale/consult-eglot")
   :config
   )

  (use-package consult-projectile
:straight (consult-projectile :type git :host gitlab :repo "OlMon/consult-projectile" :branch "master"))
(use-package consult-dir
 :straight (consult-dir :type git :host nil :repo "https://github.com/karthink/consult-dir")
 :bind (("M-g d"   . consult-dir)
        :map vertico-map
        ("M-s f" . consult-dir-jump-file)
        ("M-g d" . consult-dir))
 :config
 (setq consult-dir-project-list-function #'consult-dir-projectile-dirs)

 )
(use-package consult-flymake
 :straight ( consult-flymake :type git :host nil :repo "https://github.com/minad/consult-flycheck")
 :after consult
 :config
 )
(use-package
  affe
  ;; :config ;; Manual preview key for `affe-grep'
  ;; (consult-customize
  ;;  affe-grep
  ;;  :preview-key (kbd "M-."))
  ;; (defun affe-orderless-regexp-compiler (input _type _ignorecase)
  ;;   (setq input
  ;;         (orderless-pattern-compiler
  ;;          input))
  ;;   (cons input
  ;;         (lambda (str)
  ;;           (orderless--highlight
  ;;            input
  ;;            str))))
  ;; (setq affe-regexp-compiler
  ;;       #'affe-orderless-regexp-compiler)
  )
;; (use-package embark
;;   :ensure t

;;   :bind
;;   (("C-." . embark-act)         ;; pick some comfortable binding
;;    ;; ("C-;" . embark-dwim)        ;; good alternative: M-.
;;    ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings'

;;   :init

;;   ;; Optionally replace the key help with a completing-read interface
;;   (setq prefix-help-command #'embark-prefix-help-command)

;;   :config

;;   ;; Hide the mode line of the Embark live/completions buffers
;;   (add-to-list 'display-buffer-alist
;;                '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
;;                  nil
;;                  (window-parameters (mode-line-format . none)))))

;; ;; Consult users will also want the embark-consult package.
;; (use-package embark-consult
;;   :ensure t ; only need to install it, embark loads it after consult if found
;;   :hook
;;   (embark-collect-mode . consult-preview-at-point-mode))

yasnippet

YASnippet extends org-mode's <s-like templating in an arbitrary way. This is useful for quickly applying export settings, annotating code blocks, and the like.

install

This assumes that snippets are stored or symlinked to ${HOME}/.emacs.d/snippets/*

(use-package yasnippet)
(setq yas-snippet-dirs
      (list (concat (file-name-as-directory user-emacs-directory)
                    (file-name-as-directory "emacs-nougat")
                    "snippets")))

(yas-global-mode 1) ;; or M-x yas-reload-all if you've started YASnippet already.
(setq yas-triggers-in-field t) ;; allow nested snippets
(setq yas-indent-line 'fixed)
(add-hook 'org-mode-hook '(lambda () (set (make-local-variable 'yas-indent-line) 'fixed)))

git

gitignore

Syntax highlighting

Gitignore syntax highlighting is provided by the gitignore-mode package in magit.

(use-package git-modes
 ;; :straight (git-modes :type git :host github :repo "magit/git-modes")
 :config
 )
;; (use-package gitignore-mode)

gitignore.io

Access to pre-baked gitignore lists for various languages.

(use-package gitignore
:straight (gitignore
           :type git
           :host github
           :repo "syohex/emacs-gitignore")
)

vc visit file

(setq vc-follow-symlinks t)

magit

    (add-hook 'before-save-hook 'magit-wip-commit-initial-backup)
      (use-package magit
       :straight (magit :type git
                     :host github
                     :repo "magit/magit")
       :bind(("C-x g" . magit-status)
           ;;https://emacs.stackexchange.com/a/54542
             (:map magit-file-section-map
                   ("RET" . magit-diff-visit-file-other-window)
                   :map magit-hunk-section-map
           ("RET" . magit-diff-visit-file-other-window)))
       :init (setq magit-status-margin '(nil "%d %b %y %H:%M" magit-log-margin-width t 24))
       :config
       (require 'magit-extras)
       (setq magit-log-section-commit-count 25)
       (magit-wip-mode 1)
)

forge

Forge interfaces with github and gitlab

(use-package forge
  :ensure t
  :after magit
  :config (setq forge-add-pullreq-refspec 'ask forge-pull-notifications t
                   forge-topic-list-limit '(60 . 0))
  (add-to-list 'forge-alist `(,(plist-get my/secrets :work-gitlab-alias) ,(plist-get my/secrets :work-gitlab-api-url) ,(plist-get my/secrets :work-gitlab-alias) forge-gitlab-repository))
  )

code-review

review bits of PRs

(use-package code-review
 :straight ( code-review :type git :host nil :repo "https://github.com/phelrine/code-review/" :branch "fix/closql-update")
 :hook (code-review-mode . emojify-mode)
 :config
 (setq code-review-fill-column 80)
 (setq code-review-auth-login-marker 'code-review)
 (setq code-review-gitlab-host (plist-get my/secrets :work-gitlab-codereview-url))
 (setq code-review-gitlab-graphql-host (plist-get my/secrets :work-gitlab-codereview-url))
 (define-key forge-topic-mode-map (kbd "C-c r") 'code-review-forge-pr-at-point)
 (define-key code-review-mode-map (kbd "C-c C-n") 'code-review-comment-jump-next)
 (define-key code-review-mode-map (kbd "C-c C-p") 'code-review-comment-jump-previous)
 )
;;  (use-package abridge-diff
;;   :straight ( abridge-diff :type git :host nil :repo "https://github.com/jdtsmith/abridge-diff")
;; :after magit
;;   :init
;;   (abridge-diff-mode 1)
;;   )

git-timemachine

(use-package git-timemachine
 :straight (git-timemachine :type git :host nil :repo "https://github.com/emacsmirror/git-timemachine")
 :config
 )

git-link

 (use-package git-link
  :straight (git-link :type git :host nil :repo "https://github.com/sshaw/git-link")
  :config
(add-to-list 'git-link-remote-alist
    `(,(plist-get my/secrets :work-gitlab-alias) git-link-gitlab))
  (add-to-list 'git-link-commit-remote-alist
    `(,(plist-get my/secrets :work-gitlab-alias) git-link-commit-gitlab)))

magit difftastic

(use-package difftastic
  :after magit
  :demand t
  :bind (:map magit-blame-read-only-mode-map
         ("D" . difftastic-magit-show)
         ("S" . difftastic-magit-show))
  :config
  (eval-after-load 'magit-diff
    '(transient-append-suffix 'magit-diff '(-1 -1)
       [("D" "Difftastic diff (dwim)" difftastic-magit-diff)
        ("S" "Difftastic show" difftastic-magit-show)])))

pandoc mode

configure

Pandoc is John MacFarlane's project to convert files between markup types.

pandoc-mode provides an emacs minor mode to access pandoc functions on buffers whose major mode is a supported type. To bring up a contextual menu of supported actions, type C-c / while the mode is enabled.

(use-package pandoc-mode)
(add-hook 'markdown-mode-hook 'pandoc-mode)
(add-hook 'org-mode-hook 'pandoc-mode)

language support

treesitter

(when (version<= emacs-version "29.1")
  (use-package tree-sitter
    :straight (tree-sitter :type git
                           :host github
                           :repo "ubolonton/emacs-tree-sitter"
                           :files ("lisp/*.el"))
    :config (add-to-list 'tree-sitter-major-mode-language-alist '(python-mode . python))
    :hook ((python-mode ) . tree-sitter-hl-mode))

  (use-package tree-sitter-langs
    :straight (tree-sitter-langs :type git
                                 :host github
                                 :repo "ubolonton/emacs-tree-sitter"
                                 :files ("langs/*.el" "langs/queries"))
    :after tree-sitter)
)
(add-to-list 'auto-mode-alist '("\\.yml\\'" . yaml-ts-mode))
(add-to-list 'auto-mode-alist '("\\.yaml\\'" . yaml-ts-mode))
(add-to-list 'auto-mode-alist '("/Dockerfile[^/]*\\'" . dockerfile-ts-mode))

treesit-auto and combobulate

(when (version<= "29.1" emacs-version)
    (use-package treesit-auto
      :straight (treesit-auto :type git :host nil :repo "https://github.com/renzmann/treesit-auto")
      :config
      (setq treesit-auto-install 'prompt)
      (global-treesit-auto-mode)
      )

    (use-package
      combobulate
      :straight (combobulate
                 :type git
                 :host nil
                 :nonrecursive t
                 :repo "https://github.com/mickeynp/combobulate")
      :preface
        (dolist (mapping '((python-mode . python-ts-mode)
                   ;; (css-mode . css-ts-mode)
                   ;; (typescript-mode . tsx-ts-mode)
                   ;; (js-mode . js-ts-mode)
                   ;; (css-mode . css-ts-mode)
                    ;; (markdown-mode . markdown-ts-mode)
                    (json-mode . json-ts-mode)
                   (yaml-mode . yaml-ts-mode)))
          (add-to-list 'major-mode-remap-alist mapping))
      :hook ((python-ts-mode . combobulate-mode)
             ;; (js-ts-mode . combobulate-mode)
             ;; (css-ts-mode . combobulate-mode)
             ;; (markdown-ts-mode . combobulate-mode)
             ;; (typescript-ts-mode . combobulate-mode)
             ;; (tsx-ts-mode . combobulate-mode)
             (yaml-ts-mode . combobulate-mode)
             (json-ts-mode . combobulate-mode)))
    )

eglot

(use-package eglot
 :straight (eglot :type git :host github :repo "joaotavora/eglot")
 :after yasnippet
 :init (add-to-list 'eglot-server-programs `(python-ts-mode . ("pylsp")))
       ;; (add-to-list 'eglot-server-programs `(yaml-ts-mode . ("yaml-language-server --stdio")))
       ;; (add-to-list 'eglot-server-programs `(toml-ts-mode . ("taplo lsp stdio")))
 :hook ((python-mode . eglot-ensure)
        (python-ts-mode . eglot-ensure)
        ;; (yaml-ts-mode . eglot-ensure)
        ;; (toml-ts-mode . eglot-ensure)
        )
 :config
 (setq eglot-ignored-server-capabilities '(:documentOnTypeFormattingProvider))
 )
(use-package eldoc-box)
(use-package eglot-booster
 :straight ( eglot-booster :type git :host nil :repo "https://github.com/jdtsmith/eglot-booster")
 :after eglot
 :init (setq eglot-booster-io-only t)
 :config (eglot-booster-mode)
 )

LaTeX

AuCTeX

AuCTex is a major mode for managing the compilation of latex documents.

(if (not (eq system-type 'darwin))
  (straight-use-package 'auctex)
  ;; Use xetex by default, so we can use ttf fonts.
  (setq-default TeX-engine 'xetex)
  ;; PDF output by default
  (setq-default TeX-PDF-mode t)
)

sqlite

(use-package sqlite-mode-extras
 :straight (sqlite-mode-extras :type git :host nil :repo "https://github.com/xenodium/sqlite-mode-extras")
 :hook ((sqlite-mode . sqlite-extras-minor-mode))
 :config
 )

lisp

lispy-mode

(use-package
  lispy
  :bind (("C-j" . eval-print-last-sexp))
  :hook ((emacs-lisp-mode . lispy-mode)
  (racket-mode . lispy-mode)
  (lisp-interaction-mode . lispy-mode)
  (inferior-emacs-lisp-mode . lispy-mode))
  :config (use-package
            adjust-parens
            ;; Bind my `adjust-parens' commands as lispy special commands.  NOTE: `adjust-parens' lacks
            ;; autoloads for these commands, so we have to force the package to load before binding its
            ;; commands.
            :config (lispy-define-key
                        lispy-mode-map-special
                        "H"
                        'lisp-dedent-adjust-parens
                      (lispy-define-key
                          lispy-mode-map-special
                          "L"
                          'lisp-indent-adjust-parens))
            ;; [2019-09-06 Fri 10:05]  Trying out W/S bindings too.
            ;; (cl-loop for (key . def) in (alist "H" 'lisp-dedent-adjust-parens
            ;;                                    "W" 'lisp-dedent-adjust-parens
            ;;                                    "L" 'lisp-indent-adjust-parens
            ;;                                    "S" 'lisp-indent-adjust-parens)
            ;;          do (lispy-define-key lispy-mode-map-special key def))
            )
  (progn
    ;; I use these keys!
    (dolist (key '("C-1" "C-2" "C-3"))
      (define-key lispy-mode-map-c-digits (kbd key)
        nil))
    (lispy-set-key-theme
     '(special lispy c-digits)))
  (defun conditionally-enable-lispy ()
    (when (eq this-command
              'eval-expression)
      (lispy-mode 1)))
  (add-hook
   'minibuffer-setup-hook
   'conditionally-enable-lispy))

python

dape

  (use-package dape
    :straight (dape :type git :host nil :repo "https://github.com/svaante/dape")
    :config
    ;; (add-to-list 'dape-configs
    ;;              `(debugpy
    ;;                modes (python-ts-mode python-mode)
    ;;                command "python"
    ;;                command-args ("-m" "debugpy.adapter")
    ;;                :type "executable"
    ;;                :request "launch"
    ;;                :cwd dape-cwd-fn
    ;;                ;; :program dape-find-file-buffer-default
    ;;                ))
(transient-define-prefix dape-transient ()
       "Transient for dape."
        [["Stepping"
         ("n" "Next" dape-next :transient t)
         ("i" "Step in" dape-step-in :transient t)
         ("o" "Step out" dape-step-out :transient t)
         ("c" "Continue" dape-continue :transient t)
         ("r" "Restart" dape-restart :transient t)]
         ["Breakpoints"
         ("bb" "Toggle" dape-breakpoint-toggle :transient t)
         ("bd" "Delete" dape-breakpoint-remove-at-point :transient t)
         ("bD" "Delete all" dape-breakpoint-remove-all :transient t)
         ("bl" "Log" dape-breakpoint-log :transient t)]
         ["Info"
         ("si" "Info" dape-info :transient t)
         ("sm" "Memory" dape-read-memory :transient t)
         ("ss" "Select Stack" dape-select-stack :transient t)
         ("R" "Repl" dape-repl :transient t)]
         ["Quit"
         ("qq" "Quit" dape-quit :transient nil)
         ("qk" "Kill" dape-kill :transient nil)]])
    )

run-python

(advice-add 'run-python :around #'envrc-propagate-environment)

(when (executable-find "ipython")
(setq python-shell-interpreter "ipython"
      ;; python-shell-interpreter-args "--simple-prompt --classic"
      ))

python-pytest

(use-package python-pytest)

python-coverage

(use-package python-coverage)

flymake-ruff

(use-package flymake-ruff
 :straight (flymake-ruff :type git :host nil :repo "https://github.com/erickgnavar/flymake-ruff")
 :config
 (add-hook 'python-ts-mode-hook #'flymake-ruff-load)
 )

markdown

markdown-mode

All the internet uses it.

(straight-use-package 'markdown-mode)
(use-package markdown-mode
  :ensure nil
  :commands (markdown-mode gfm-mode)
  :mode (("README\\.md\\'" . gfm-mode)
         ("readme\\.md\\'" . gfm-mode)
         ("\\.md\\'" . markdown-mode)
         ("\\.markdown\\'" . markdown-mode))
  :init (setq markdown-command "pandoc"))
(use-package edit-indirect)

markdown-toc

(use-package markdown-toc)

json

jsonian

(use-package jsonian
 :straight (jsonian :type git :host github :repo "iwahbe/jsonian")
 :after so-long orderless
  :custom
  (jsonian-no-so-long-mode)
  (setq jsonian-find-filter-fn #'orderless-filter)
 )

docker

##+INCLUDE: "../nougat/support/docker/dockerfile-mode.org"

direnv

direnv allows directory-specific environment variable overrides. It's very useful for obtuse compilation steps, python tooling, and much else.

(use-package envrc
 :straight (envrc :type git :host github :repo "purcell/envrc")
 :config (envrc-global-mode)
 )

transients

##+INCLUDE: "../nougat/packages/hydra/setup.org"

##+INCLUDE: "../nougat/packages/hydra/extras/hydra-org.org"

user functions

close next pane

(defun grym/close-next-pane ()
  "if there are multiple windows, close the other pane"
  (interactive)
  (other-window 1)
  (if (not (one-window-p))
      (delete-window)))

close and kill next pane

(defun grym/close-and-kill-next-pane ()
  "If there are multiple windows, then close the other pane and kill the buffer in it also."
  (interactive)
  (other-window 1)
  (kill-this-buffer)
  (if (not (one-window-p))
      (delete-window)))

dired open other window

(defun grym/dired-open-other-window nil
  "Open dired in another window to the right of the current one, but do not bring focus there."
  (interactive)
  (save-excursion
    (with-selected-window
        (split-window-right)
      (balance-windows)
      (dired default-directory))))

switch projectile vc

(defun grym/switch-projectile-vc ()
  (interactive)
  (let ((current-prefix-arg '(4)))
    (call-interactively
     'projectile-vc)))

reader mode toggle

(defun grym/toggle-reader-mode ()
  (interactive)
  (if (and (bound-and-true-p view-mode)
           (bound-and-true-p olivetti-mode))
      (progn
        (view-mode -1)
        (olivetti-mode -1)
        (message
         "disabling olivetti mode"))
    (view-mode)
    (olivetti-mode)
    (message
     "enabling olivetti mode")))

midden heap transient

Launch bindings for different OS/keyboards

(when (eq system-type 'gnu/linux)
  (setq grym/transient-key "<XF86Launch8>" ))
(when (eq system-type 'darwin)
  (setq grym/transient-key "<f9>" ))
  (require 'transient)
  (require 'indent-bars)
    (transient-define-prefix grym/utility-transient ()
      "þe dumping ground"
      [
       ["Open"
        ("od" "dired indirect" grym/dired-open-other-window)
        ("of" "find file at point" ffap)
        ;; ("V" "projectile vc here" projectile-vc)
        ;; ("d" "dired" dired)
        ]
       ["Search"
        ("A"   "rg in project" rg-project)
        ("R"   "consult ripgrep" consult-ripgrep)
        ;; ("o"   "Occur in project" projectile-multi-occur)
        ]
       ["Buffers"
          ("0" "kill buffer and window" kill-buffer-and-window)
          ("1" "close other window" grym/close-next-pane)
          ("2" "kill other buffer and window" grym/close-and-kill-next-pane)
          ("W" "whitespace" whitespace-mode)
          ("a" "copy region plain" acdw/copy-region-plain)
          ("be" "eval and replace" crux-eval-and-replace)
          ("bI" "indent bars" indent-bars-mode)
          ("bj" "zoom" zoom-window-zoom)
          ("bl" "clean empty lines" xah-clean-empty-lines)
          ("bn" "toggle line numbers" display-line-numbers-mode)
          ("bs" "new scratch text buffer" grym/new-scratch-text-buffer)
          ("bu" "insert url contents at point" grym/insert-url)
          ("bw" "paste marked region or buffer" webpaste-paste-buffer-or-region)
          ("c" "insert character" insert-char)
          ("d" "define word at point" dictionary-lookup-definition)
          ("g" "clickable links" goto-address-mode)
          ("t" "transcribe toggle" whisper-run)
          ;; ("i" "imenu list" imenu-list-smart-toggle)
          ("q" "bury" bury-buffer)
          ("r" "view+olivetti" grym/toggle-reader-mode)
          ("x" "+/- ro" read-only-mode)
          ;; ("R" "rectangle" cc/rectangle-tmenu )
          ;; ("m" "mark ring" consult-mark)
          ;; ("o" "olivetti" olivetti-mode)
          ;; ("t" "column marker" display-fill-column-indicator-mode)
      ]
       ["Projectile"
          ("C"   "Rebuild cache" projectile-invalidate-cache)
          ("X"   "Cleanup" projectile-cleanup-known-projects)
          ("f" "find file" consult-projectile-find-file)
          ("k"   "Kill project" projectile-kill-buffers)
          ("v" "projectile vc"  grym/switch-projectile-vc)
        ]
       ["Window"
            ("w" "window movement" grym/window-transient)
        ]
       ["Org" :if-mode org-mode
         ("Oc"  "collapse todo headings" grym/org-collapse-todo)
         ("Or" "restart org" org-mode-restart)
        ]
       ["Dired" :if-mode dired-mode
         ("d" "share with dragon" grym/dwim-shell-command-dragon-marked-files)
         ("f" "view with feh" grym/dwim-shell-command-feh-marked-files)
         ;; ("Dg" "git log" dired-git-log-mode)
         ("u" "upload to oxo" grym/dwim-shell-command-oxo-marked-files)
       ]
      ["Programming" :if-derived prog-mode
       ("e" "eglot transient" grym/eglot-transient)
       ("l" "git file log" magit-log-buffer-file)
       ("p" "precommit" grym/spam-precommit-in-project)
       ;; ("s" "toggle sidebars" +sidebar-toggle)
       ]
      ["Meta"
        ("<XF86Launch8>" "quit" transient-quit-one)]]
      )

  (transient-define-prefix grym/window-transient ()
    ["Window Movement"
     ["Move windows"
      ("I" "up" windmove-swap-states-up :transient t)
      ("J" "left" windmove-swap-states-left :transient t)
      ("K" "right" windmove-swap-states-right :transient t)
      ("L" "down" windmove-swap-states-down :transient t)
      ]
     ["Modify windows"
        ("0" "delete this window" delete-window)
        ("1" "delete other windows" delete-other-windows)
        ("2" "split below" split-window-below)
        ("3" "split right" split-window-right)
        ("C" "clone" clone-indirect-buffer)
        ("c" "clone indirect" clone-indirect-buffer-other-window)
        ("j" "zoom" zoom-window-zoom)
        ("w" "swap window" ace-swap-window)
      ]
     ["winner-mode"
      ("u" "undo"  winner-undo :transient t)
      ("U" "redo"  winner-redo :transient t)]
     ["Meta"
        ("<XF86Launch8>" "quit" transient-quit-one)]]
)

  (global-set-key (kbd grym/transient-key) 'grym/utility-transient)

eglot-transient

(require 'transient)
(transient-define-prefix grym/eglot-transient ()
  ["Eglot"
   ["Formatting"
    ("i" "indent-bars" indent-bars-mode)
    ("r" "rename at point" eglot-rename)
    ;; ("h" "show menu" eglot-transient-menu) ;; heh
    ]
   ["Eldoc"
    "e" "doc hover at point" eldoc-box-hover-at-point-mode
    ]
   ["Unit test" :if-mode python-ts-mode
    ("c" "show coverage" python-coverage-overlay-mode)
    ("p" "pytest" python-pytest-dispatch)
    ]
   ["Meta"
        ("<XF86Launch8>" "quit" transient-quit-one)]
   ]
  )

Author: grym

Created: 2025-06-04 Wed 15:44

Validate