ldlework's init.el

Table of Contents

This config is best viewed in Emacs!

bootstrap

lexical binding

Activate lexical-binding for the entire config.

;; -*- lexical-binding: t -*-

package management via straight.el

straight.el is an alternative to package.el with many advantages including the ability to integrate with use-package and installing packages from git or github.

(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)
  (setq straight-vc-git-default-clone-depth 1))

use-package integration

(setq straight-use-package-by-default t)
(straight-use-package 'use-package)
(use-package git) ;; ensure we can install from git sources

general dependencies

The following package dependencies are used throughout the rest of the configuration. They provide modern APIs for working with various elisp data structures.

(require 'cl-lib)
(require 'seq)
(use-package f :demand t)          ;; files
(use-package dash :demand t)       ;; lists
(use-package ht :demand t)         ;; hash-tables
(use-package s :demand t)          ;; strings
(use-package a :demand t)          ;; association lists
(use-package anaphora :demand t)   ;; anaphora

boilerplate

Calculate file paths relative to various locations.

my/get-org-file

(defun my/get-org-file (file-name)
  (concat my/org-directory file-name))

my/get-project-directory

(defun my/get-project-directory (name)
  (concat my/projects-directory name))

my/get-source-directory

(defun my/get-source-directory (name)
  (concat my/sources-directory name))

local settings

These settings are specific to current machine.

linux paths

(when (string-equal system-type "gnu/linux")
  (defvar my/home-directory (expand-file-name "~/"))
  (defvar my/data-directory (concat my/home-directory ".emacs.d/"))
  (defvar my/projects-directory (concat my/home-directory "src/"))
  (defvar my/sources-directory (concat my/home-directory "ext/"))
  (defvar my/org-directory (concat my/home-directory "org/"))
  (defvar my/yas-directory (concat my/data-directory "yasnippet/")))

windows paths

(when (string-equal system-type "windows-nt")
  (defvar my/home-directory (expand-file-name "b:/"))
  (defvar my/data-directory (concat my/home-directory "Emacs/"))
  (defvar my/projects-directory (concat my/home-directory "Projects/"))
  (defvar my/sources-directory (concat my/home-directory "Sources/"))
  (defvar my/org-directory (concat my/home-directory "Syncthing/Org/"))
  (defvar my/yas-directory (concat my/data-directory "Yas/"))
  (setq linkmarks-file (concat my/org-directory "bookmarks.org")))

data paths

(defvar my/notes-file-name (my/get-org-file "notes.org") "Main notes file-name")
(defvar my/bookmarks-file-name (my/get-org-file "bookmarks.org") "Main bookmarks file-name")
(defvar my/autosaves-directory (concat my/data-directory "autosaves/") "Main bookmarks file-name")
(defvar my/backups-directory (concat my/data-directory "backups/") "Main bookmarks file-name")

external browser

(setq browse-url-browser-function 'browse-url-chrome)
(setq browse-url-chrome-program "chrome")

default zoom

(setq my/default-zoom-level 4)

helpers

These macros are conveinent shorthands.

:function

Make non-interactive functions out of forms or a symbol.

e.g. (:function (message "Hello world.") e.g. (:function foobar)

(defmacro :function (&rest body)
  (if (->> body length (< 1))
      `(lambda () ,@body)
    (pcase (car body)
      ;; command symbol
      ((and v (pred commandp))
       `(lambda () (call-interactively (quote ,v))))
      ;; function symbol
      ((and v (pred symbolp))
       `(lambda () (,v)))
      ;; quoted command symbol
      ((and v (pred consp) (guard (eq 'quote (car v))) (pred commandp (cadr v)))
       `(lambda () (call-interactively ,v)))
      ;; quoted function symbol
      ((and v (pred consp) (guard (eq 'quote (car v))))
       `(lambda () (,(cadr v))))
      ;; body forms
      (_ `(lambda () ,@body) ))))

:command

Make interactive commands out of forms or a symbol.

e.g. (:command (message "Hello world.")) e.g. (:command foobar)

(defmacro :command (&rest body)
  (if (->> body length (< 1))
      `(lambda () (interactive) ,@body)
    (pcase (car body)
      ;; command symbol
      ((and v (pred commandp))
       `(lambda () (interactive) (call-interactively (quote ,v))))
      ;; function symbol
      ((and v (pred symbolp))
       `(lambda () (interactive) (,v)))
      ;; quoted command symbol
      ((and v (pred consp) (guard (eq 'quote (car v))) (pred commandp (cadr v)))
       `(lambda () (interactive) (call-interactively ,v)))
      ;; quoted function symbol
      ((and v (pred consp) (guard (eq 'quote (car v))))
       `(lambda () (interactive) (,(cadr v))))
      ;; body forms
      (_ `(lambda () (interactive) ,@body) ))))

:after

Defer some forms until the given package is loaded.

e.g. (:after org (message "Hello world"))

(defmacro :after (package &rest body)
  "A simple wrapper around `with-eval-after-load'."
  (declare (indent defun))
  `(with-eval-after-load ',package ,@body))

:hook

Register some forms or a symbol with a hook.

e.g. (:hook org-mode (message "hello world") e.g. (:hook org-mode foobar)

(defmacro :hook (hook-name &rest body)
  "A simple wrapper around `add-hook'"
  (declare (indent defun))
  (let* ((hook-name (format "%s-hook" (symbol-name hook-name)))
         (hook-sym (intern hook-name))
         (first (car body))
         (local (eq :local first))
         (body (if local (cdr body) body))
         (first (car body))
         (body (if (consp first)
                   (if (eq (car first) 'quote)
                       first
                     `(lambda () ,@body))
                 `',first)))
    `(add-hook ',hook-sym ,body nil ,local)))

:push

A wrapper around add-to-list.

e.g. (:push some-list 1 2 3)

(defmacro :push (sym &rest body)
  (declare (indent defun))
  (if (consp body)
      `(setq ,sym (-snoc ,sym ,@body))
    `(add-to-list ,sym ,body)))

:bind

Bind some forms or a symbol to a key.

e.g. (:bind "C-m" (message "Hello world." e.g. (:bind org-mode "C-m" (message "Hello world."))

(defmacro :bind (key &rest body)
  (declare (indent defun))
  (pcase key
    ;; kbd string resolving symbol
    ((and k (pred symbolp) (pred boundp) (guard (stringp (eval key))))
     `(global-set-key (kbd ,(eval key)) ,(eval `(:command ,@body))))
    ;; partial mode symbol
    ((pred symbolp)
     (let ((mode (intern (format "%s-map" key)))
           (key (eval (car body)))
           (body (eval `(:command ,@(cdr body)))))
       `(define-key ,mode (kbd ,key) ,body)))
    ;; global binding
    (_ `(global-set-key (kbd ,key) ,(eval `(:command ,@body))))))

global keybinds

default hydra

Open the default hydra.

(:bind "<f12>" (hera-start 'hydra-default/body))

major mode hydra

Open the current major-mode hydra if it exists, or the default hydra.

(:bind "<f13>" my/hydra-dwim)

org capture

Activate org capture.

(:bind "C-c c" org-capture)

magit status

Activate magit's git status.

(:bind "C-x g" magit-status)

treemacs

Toggle the file-browser sidebar.

(:bind "M-<f12>" treemacs-dwim)
(:bind "S-M-<f12>" (delete-window (treemacs-get-local-window)))

(:after treemacs
  (:bind treemacs-mode "f" treemacs-toggle-autopeek)
  (:bind treemacs-mode "C-p"
    (call-interactively 'treemacs-previous-line)
    (when treemacs-autopeek-mode
      (run-at-time "0.0 sec" nil 'call-interactively 'treemacs-peek)))
  (:bind treemacs-mode "C-n"
    (call-interactively 'treemacs-next-line)
    (when treemacs-autopeek-mode
      (run-at-time "0.0 sec" nil 'call-interactively 'treemacs-peek))))

meta n & p

Quickly navigate buffer blocks/paragraphs.

(:bind "M-p" backward-paragraph)
(:bind "M-n" forward-paragraph)

toggle contextual help

Toggle whether help buffers auto-update.

(:bind "C-c h" toggle-context-help)

aesthetics

vertical border

Make the border between windows visible.

(set-face-foreground 'vertical-border "gray")

blend in the fringes

Hide the default buffer margins.

(set-face-attribute 'fringe nil :background nil)

column number

Show column number in addition to line number.

(column-number-mode 1)

doom modeline

Use doom-modeline to ornament the modeline.

(use-package doom-modeline
  :ensure t
  :config
  (doom-modeline-def-modeline
   'my-modeline

   '(bar workspace-name window-number modals matches buffer-info remote-host selection-info)
   '(objed-state misc-info buffer-position major-mode process vcs checker))

  (doom-modeline-mode 1)
  (setq doom-modeline-height 35)
  (setq doom-modeline-bar-width 5)
  :init
  (defun setup-custom-doom-modeline ()
    (doom-modeline-set-modeline 'my-modeline 'default))
  (:hook doom-modeline-mode 'setup-custom-doom-modeline))

core settings

autosaves

Periodically save a copy of open files.

autosave every file buffer

(setq auto-save-default t)

save every 20 secs or 20 keystrokes

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

keep autosaves in a single place

(unless (file-exists-p my/autosaves-directory)
    (make-directory my/autosaves-directory))

(setq auto-save-file-name-transforms
      `((".*" ,my/autosaves-directory t)))

backups

Backups are created everytime a buffer is manually saved.

backup every save

(use-package backup-each-save
  :config (:hook after-save backup-each-save))

keep 10 backups

(setq kept-new-versions 10)

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)

keep backups in a single place

(unless (file-exists-p my/backups-directory)
  (make-directory my/backups-directory))

(setq backup-directory-alist
      `((".*" . ,my/backups-directory)))

(setq make-backup-files t)

cursor

box style

(setq-default cursor-type 'box)

blinking

(blink-cursor-mode 1)

disable

Disable various UI and other features for a more minimal experience.

menubar

(menu-bar-mode -1)

toolbar

(tool-bar-mode -1)

scrollbar

(scroll-bar-mode -1)

startup message

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

customizations file

Disable the customizations file so there's no temptation to use the customization interface.

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

editing

use spaces

(setq-default indent-tabs-mode nil)

visual fill-column

(use-package visual-fill-column
  :config
  (global-visual-fill-column-mode))

wrap lines at 79 characters

(setq-default fill-column 79)

autowrap in text-mode

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

ssh for tramp

Default method for transferring files with Tramp.

(setq tramp-default-method "ssh")

just-one-space

Reduce white-space to just one space.

minor-modes

whitespace-mode

Visually display trailing whitespace

(use-package whitespace
  :custom
  (whitespace-style
   '(face tabs newline trailing tab-mark space-before-tab space-after-tab))
  :config
  (global-whitespace-mode 1))

prettify-symbols-mode

Replace various symbols with nice looking unicode glyphs.

(global-prettify-symbols-mode 1)

electric-pair-mode

Automatically insert matching close-brackets for any open bracket.

(electric-pair-mode 1)

rainbow-delimeters-mode

Color parenthesis based on their depth, using the golden ratio (because why not).

(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 :commands rainbow-delimiters-mode :hook ...
  :init
  (setq rainbow-delimiters-max-face-count 16)
  (set-random-rainbow-colors 0.6 0.7 0.5)
  (:hook prog-mode 'rainbow-delimiters-mode))

show-paren-mode

Highlight the matching open or closing bracket.

(require 'paren)
(show-paren-mode 1)
(setq show-paren-delay 0)
(:after xresources
  (set-face-foreground 'show-paren-match (theme-color 'green))
  (set-face-foreground 'show-paren-mismatch "#f00")
  (set-face-attribute 'show-paren-match nil :weight 'extra-bold)
  (set-face-attribute 'show-paren-mismatch nil :weight 'extra-bold))

which-key-mode

Show possible followups after pressing a key prefix.

(use-package which-key
  :custom
  ;; sort single chars alphabetically P p Q q
  (which-key-sort-order 'which-key-key-order-alpha)
  (which-key-idle-delay 0.4)
  :config
  (which-key-mode))

company-mode

Show popup autocompletion.

(use-package company
  :config
  (global-company-mode))

shorten prompts

Shorten yes/no prompts to one letter.

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

zoom

Adjust font size in buffers or globally.

(use-package zoom-frm
  :straight (zoom-frm :type git
                      :host github
                      :repo "emacsmirror/emacswiki.org"
                      :files ("zoom-frm.el"))
  :config
  (dotimes (i my/default-zoom-level) (zoom-frm-in)))

cache

This speeds up unicode-fonts-setup after first run.

(use-package persistent-soft)

eval depth

Avoid elision (…) in messages.

(setq print-level 100
      print-length 9999
      eval-expression-print-level 100
      eval-expression-print-length 9999)

debug on error

Show tracebacks when errors happen.

(setq debug-on-error t)

direnv

emacs-direnv connects emacs to direnv to obtain directory-specific environment variables.

(use-package direnv
 :config
 (direnv-mode))

helpful

Alternative to the built-in Emacs help that provides much more contextual information.

(use-package helpful
    :straight (helpful :type git :host github :repo "Wilfred/helpful")
    :bind (("C-h s" . #'helpful-symbol)
           ("C-h c" . #'helpful-command)
           ("C-h f" . #'helpful-function)
           ("C-h v" . #'helpful-variable)
           ("C-h k" . #'helpful-key)
           ("C-h m" . #'helpful-mode)
           ("C-h C-h" . #'helpful-at-point)))

contextual help

toggle-context-help

(defun toggle-context-help ()
  "Turn on or off the context help.
Note that if ON and you hide the help buffer then you need to
manually reshow it. A double toggle will make it reappear"
  (interactive)
  (with-current-buffer (help-buffer)
    (unless (local-variable-p 'context-help)
      (set (make-local-variable 'context-help) t))
    (if (setq context-help (not context-help))
        (progn
           (if (not (get-buffer-window (help-buffer)))
               (display-buffer (help-buffer)))))
    (message "Context help %s" (if context-help "ON" "OFF"))))

context-help

(defun context-help ()
  "Display function or variable at point in *Help* buffer if visible.
Default behaviour can be turned off by setting the buffer local
context-help to false"
  (interactive)
  (let ((rgr-symbol (symbol-at-point))) ; symbol-at-point http://www.emacswiki.org/cgi-bin/wiki/thingatpt%2B.el
    (with-current-buffer (help-buffer)
     (unless (local-variable-p 'context-help)
       (set (make-local-variable 'context-help) t))
     (if (and context-help (get-buffer-window (help-buffer))
         rgr-symbol)
       (if (fboundp  rgr-symbol)
           (describe-function rgr-symbol)
         (if (boundp  rgr-symbol) (describe-variable rgr-symbol)))))))

advise symbol eldoc

(defadvice eldoc-print-current-symbol-info
  (around eldoc-show-c-tag activate)
  (cond
        ((eq major-mode 'emacs-lisp-mode) (context-help) ad-do-it)
        ((eq major-mode 'lisp-interaction-mode) (context-help) ad-do-it)
        ((eq major-mode 'apropos-mode) (context-help) ad-do-it)
        (t ad-do-it)))

projectile

Projectile offers a number of features related to project interaction. It can track the root directories and sibling files of files you edit automatically. Combined with Helm, you can very quickly navigate related files.

Projectile's default prefix is C-c p

(use-package projectile
  :config
  (setq projectile-enable-caching t)
  (projectile-mode t))

project discovery

(setq projectile-project-root-files-bottom-up
      '(".git" ".hg" "README.md" "README.org" "README")
      projectile-project-search-path my/projects-directory
      projectile-sort-order 'access-time)
(projectile-discover-projects-in-directory my/projects-directory)
(projectile-discover-projects-in-directory my/sources-directory)

helm

Menu and selection framework for finding files, switching buffers, running grep, etc.

(use-package helm
  :config
  (helm-mode 1)
  (require 'helm-config)
  (:bind "M-x" helm-M-x)
  (:bind "C-x C-f" helm-find-files)
  (:bind "C-x b" helm-mini)
  (:bind "C-c y" helm-show-kill-ring)
  (:bind "C-x C-r" helm-recentf))

ace jump

Quickly jump to any candidate with a short letter combo.

(use-package ace-jump-helm-line
  :config
  (:bind helm "C-;" ace-jump-helm-line))

helm-ag

(use-package helm-ag)

helm-descbinds

Use (C-h b / kbd-helm-descbinds) to inspect current bindings with Helm.

(use-package helm-descbinds
  :commands helm-descbinds
  :config
  (:bind "C-h b" helm-descbinds))

helm-flyspell

Check and correct spelling for some modes.

(use-package helm-flyspell
  :commands helm-flyspell-correct
  :config
  (:bind " M-SPC" helm-flyspell-correct)
  (:hook org-mode flyspell-mode)
  (:hook text-mode flyspell-mode)
  (:hook fundamental-mode flyspell-mode))

helm-projectile

Make Projectile use Helm for selections.

(use-package helm-projectile
    :config
    (projectile-cleanup-known-projects)
    (setq projectile-completion-system 'helm))

projectile-readme

Open readme of the current project.

(defun projectile-readme ()
    (interactive)
    (let ((file-name (-find (lambda (f) (s-matches? "^readme" f))
                            (projectile-current-project-files))))
      (find-file (concat (projectile-project-root) "/" file-name))))

hydra-projectile-dwim

Open the projectile hydra when in a project. Otherwise, select a project.

(defun hydra-projectile-dwim ()
    (interactive)
    (if (string= "-" (projectile-project-name))
        (helm-projectile)
      (hydra-projectile/body)))

auto full frame

Make Helm always full height.

(defvar helm-full-frame-threshold 0.75)

(when window-system
  (defun helm-full-frame-hook ()
  (let ((threshold (* helm-full-frame-threshold (x-display-pixel-height))))
    (setq helm-full-frame (< (frame-height) threshold))))

  (:hook helm-before-initialize 'helm-full-frame-hook))

treemacs

Browse files in a sidebar.

(use-package treemacs
  :config
  (setq treemacs-width 25
        treemacs-follow-mode -1
        treemacs-tag-follow-mode -1
        treemacs-is-never-other-window t
        treemacs-follow-after-init t
        treemacs-icon-open-png   (propertize "⊖ " 'face 'treemacs-directory-face)
        treemacs-icon-closed-png (propertize "⊕ " 'face 'treemacs-directory-face))
  (define-key treemacs-mode-map [mouse-1]
    #'treemacs-single-click-expand-action))

(use-package treemacs-projectile)
(use-package treemacs-magit)

toggle-autopeek

(setq treemacs-autopeek-mode nil)

(defun treemacs-toggle-autopeek ()
  (interactive)
  (if treemacs-autopeek-mode
      (progn
        (setq treemacs-autopeek-mode nil)
        (message "Treemacs autopeek: OFF"))
    (setq treemacs-autopeek-mode t)
    (message "Treemacs autopeek: ON")))

treemacs-dwim

(defun treemacs-dwim ()
  (interactive)
  (pcase (treemacs-current-visibility)
    ((or 'none 'exists)
     (setq treemacs-previous-window (list (selected-frame) (selected-window)))
     (treemacs))
    ((and 'visible (guard (not (s-contains? "Treemacs-Scoped"
                                            (buffer-name (current-buffer))))))
     (setq treemacs-previous-window (list (selected-frame) (selected-window)))
     (treemacs-select-window))
    (_ (select-frame (car treemacs-previous-window))(select-window (cadr treemacs-previous-window)))))t

yasnippet

Expand interactive snippets.

(use-package yasnippet
  :config
  (setq yas-snippet-dirs `(,my/yas-directory))
  (yas-global-mode 1))

magit

The best git frontend there is.

(use-package magit)

org-mode

A souped up markup with tasking, scheduling and aggregation features.

straight.el fixes

Fix some issues with straight.el and org until that is resolved.

fix-org-git-version

(defun fix-org-git-version ()
  "The Git version of org-mode.
  Inserted by installing org-mode or when a release is made."
  (require 'git)
  (let ((git-repo (expand-file-name
                   "straight/repos/org/" user-emacs-directory)))
    (string-trim
     (git-run "describe"
              "--match=release\*"
              "--abbrev=6"
              "HEAD"))))

fix-org-release

(defun fix-org-release ()
  "The release version of org-mode.
  Inserted by installing org-mode or when a release is made."
  (require 'git)
  (let ((git-repo (expand-file-name
                   "straight/repos/org/" user-emacs-directory)))
    (string-trim
     (string-remove-prefix
      "release_"
      (git-run "describe"
               "--match=release\*"
               "--abbrev=0"
               "HEAD")))))

installation

(use-package org
  :config
  ;; these depend on the 'straight.el fixes' above
  (defalias #'org-git-version #'fix-org-git-version)
  (defalias #'org-release #'fix-org-release)
  (require 'org-habit)
  (require 'org-capture)
  (require 'org-tempo))

look

theme customizations

(when window-system
  (use-package org-beautify-theme
    :after (org)
    :config
    (setq org-fontify-whole-heading-line t)
    (setq org-fontify-quote-and-verse-blocks t)
    (setq org-hide-emphasis-markers t)))

pretty symbols

Add a hook to set the pretty symbols alist.

(setq my/org-pretty-symbols nil)
(:hook org-mode
  (setq-local prettify-symbols-alist my/org-pretty-symbols))

indent by header level

Hide the heading asterisks. Instead indent headings based on depth.

(:hook org-mode 'org-indent-mode)

pretty heading bullets

Use nice unicode bullets instead of the default asterisks.

(use-package org-bullets
  :init
  (:hook org-mode 'org-bullets-mode)
  :config
  (setq org-bullets-bullet-list '("◉" "○" "✸" "•")))

pretty priority cookies

Instead of the default [#A] and [#C] priority cookies, use little unicode arrows to indicate high and low priority. [#B], which is the same as no priority, is shown as normal.

(:push my/org-pretty-symbols
  '("[#A]" . "⇑")
  '("[#C]" . "⇓"))
;; only show priority cookie symbols on headings.
(defun nougat/org-pretty-compose-p (start end match)
  (if (or (string= match "[#A]") (string= match "[#C]"))
      ;; prettify asterisks in headings
      (org-match-line org-outline-regexp-bol)
    ;; else rely on the default function
    (funcall #'prettify-symbols-default-compose-p start end match)))


(:hook org-mode (setq-local prettify-symbols-compose-predicate
                            #'nougat/org-pretty-compose-p))

pretty heading ellipsis

Show a little arrow for collapsed headings.

(:after org
  (setq org-ellipsis " ▿"))

prettify source blocks

(:push my/org-pretty-symbols
  '("#+begin_src" . ">>")
  '("#+end_src" . "·"))

dynamic tag position

(defun org-realign-tags ()
  (interactive)
  (setq org-tags-column (- 0 (window-width)))
  (org-align-tags t))

;; (:hook window-configuration-change 'org-realign-tags)

feel

show all headings on startup

(setq org-startup-folded 'content)

don't fold blocks on open

(setq org-hide-block-startup nil)

auto-fill paragraphs

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

resepect content on insert

Don't split existing entries when inserting a new heading.

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

use helpful for help links

(advice-add 'org-link--open-help :override
            (lambda (path) (helpful-symbol (intern path))))

ensure one-line between headers

When you save, this section will ensure that there is a one-line space between each heading. This helps with the background color of code-blocks not showing up on folded headings.

;; (defun my/org-no-line-before-headlines ()
;;   (beginning-of-buffer)
;;   (while (re-search-forward "\n\\{3,\\}\\*" nil t)
;;     (replace-match "\n\n*")))

;; (defun my/org-one-line-after-headlines ()
;;   (beginning-of-buffer)
;;   (while (re-search-forward "^\\*+.*\n\\{2,\\}" nil t)
;;     (outline-previous-heading)
;;     (end-of-line)
;;     (forward-char)
;;     (while (looking-at "^[:space:]*$")
;;       (kill-line))))

;; (defun my/org-trim-headlines ()
;;   (let ((markers nil))
;;     (org-element-map (org-element-parse-buffer) '(headline)
;;       (lambda (paragraph)
;;         (let ((contents-end (org-element-property :contents-end paragraph))
;;               (post-blank (org-element-property :post-blank paragraph))
;;               (marker (make-marker)))
;;           (goto-char contents-end)
;;           (unless (eq 0 post-blank)
;;             (set-marker marker contents-end)
;;             (setq markers (append markers (list (cons marker post-blank))))))))
;;     (--each markers (save-excursion
;;                       (goto-char (car it))
;;                       (kill-line (- (cdr it) 1))))))

;; (defun my/org-element-type-at-point ()
;;   (car (org-element-at-point)))

;; (defun my/org-point-at-headline ()
;;   (let* ((element-type (my/org-element-type-at-point)))
;;     (eq 'headline element-type)))

;; (defun my/org-mark-elements (data types marker-prop &rest props)
;;   (let ((markers nil))
;;     (org-element-map data types
;;       (lambda (element)
;;         (let* ((marker (make-marker))
;;                (marker-pos (org-element-property marker-prop element))
;;                (prop-map (make-hash-table)))
;;           (when marker-pos
;;             (set-marker marker marker-pos)
;;             (--each props (map-put! prop-map it (org-element-property it element)))
;;             (setq markers (append markers (list (cons marker prop-map))))))))

;;     markers))

;; (defun my/org-visit-markers (markers)
;;   (--each markers
;;     (goto-char (car it))
;;     (sit-for 1)))

;; (defun my/org-visit-elements (types &optional data)
;;   (setq data (or data (org-element-parse-buffer)))
;;   (my/org-visit-markers (my/org-mark-elements data types :begin)))

;; (defun my/org-trim-headlines ()
;;   (save-excursion
;;     (--each (my/org-mark-elements (org-element-parse-buffer) '(headline) :begin :pre-blank)
;;       (let* ((prop-map (cdr it))
;;              (pre-blank (map-elt prop-map :pre-blank)))
;;         (when (> pre-blank 1)
;;           (goto-char (car it))
;;           (end-of-line)
;;           (forward-char)
;;           (sit-for 1)
;;           (kill-line (max 0 pre-blank)))))))

;; (defun my/org-trim-paragraphs ()
;;   (save-excursion
;;     (--each (my/org-mark-elements (org-element-parse-buffer) '(paragraph section src-block) :contents-end :post-blank)
;;       (let* ((prop-map (cdr it))
;;              (post-blank (map-elt prop-map :post-blank)))
;;         (if (> post-blank 1)
;;             (progn (goto-char (car it))
;;              (sit-for 1)
;;              (kill-line (max 0 (- post-blank 1))))
;;           (when (eq 0 post-blank)
;;             (goto-char (car it))
;;             (end-of-line)
;;             (forward-char)
;;             (open-line 1)))))))

;; (defun my/org-cleanup ()
;;   (interactive)
;;   (my/org-trim-headlines)
;;   (my/org-trim-paragraphs))


;; (:after org
;;   (:hook org-mode
;;     (:hook before-save :local
;;       (my/org-cleanup))))

todo keywords

boilerplate

  • make-state-model
    (defun todo-make-state-model (name key props)
      (append (list :name name :key key) props))
    
  • parse-state-data
    (defun todo-parse-state-data (state-data)
      (-let* (((name second &rest) state-data)
              ((key props) (if (stringp second)
                               (list second (cddr state-data))
                             (list nil (cdr state-data)))))
        (todo-make-state-model name key props)))
    
  • make-sequence-mode
    (defun todo-make-sequence-model (states)
      (mapcar 'todo-parse-state-data states))
    
  • parse-sequences-data
    (defun todo-parse-sequences-data (sequences-data)
      (mapcar 'todo-make-sequence-model sequences-data))
    
  • todo-keyword-name
    (defun todo-keyword-name (name key)
      (if key (format "%s(%s)" name key) name))
    
  • keyword-name-forstate
    (defun todo-keyword-name-for-state (state)
      (todo-keyword-name (plist-get state :name)
                         (plist-get state :key)))
    
  • is-done-state
    (defun todo-is-done-state (state)
      (equal t (plist-get state :done-state)))
    
  • is-not-done-state
    (defun todo-is-not-done-state (state)
      (equal nil (plist-get state :done-state)))
    
  • org-sequence
    (defun todo-org-sequence (states)
      (let ((active (seq-filter 'todo-is-not-done-state states))
            (inactive (seq-filter 'todo-is-done-state states)))
        (append '(sequence)
                (mapcar 'todo-keyword-name-for-state active)
                '("|")
                (mapcar 'todo-keyword-name-for-state inactive))))
    
  • org-todo-keywords
    (defun todo-org-todo-keywords (sequences)
      (mapcar 'todo-org-sequence (todo-parse-sequences-data sequences)))
    ;; (todo-org-todo-keywords todo-keywords)
    
  • org-todo-keyword-faces
    (defun todo-org-todo-keyword-faces (sequences)
      (cl-loop for sequence in (todo-parse-sequences-data sequences)
               append (cl-loop for state in sequence
                               for name = (plist-get state :name)
                               for face = (plist-get state :face)
                               collect (cons name face))))
    ;; (todo-org-todo-keyword-faces todo-keywords)
    
  • prettify-symbols-alist
    (defun todo-prettify-symbols-alist (sequences)
      (cl-loop for sequence in (todo-parse-sequences-data sequences)
               append (cl-loop for state in sequence
                               for name = (plist-get state :name)
                               for icon = (plist-get state :icon)
                               collect (cons name icon))))
    ;; (todo-prettify-symbols-alist todo-keywords)
    
  • finalize-agenda-for-state
    (defun todo-finalize-agenda-for-state (state)
      (-let (((&plist :name :icon :face) state))
        (beginning-of-buffer)
        (while (search-forward name nil 1)
          (let* ((line-props (text-properties-at (point)))
                 (line-props (org-plist-delete line-props 'face)))
            (call-interactively 'set-mark-command)
            (search-backward name)
            (call-interactively 'kill-region)
            (let ((symbol-pos (point)))
              (insert icon)
              (beginning-of-line)
              (let ((start (point))
                    (end (progn (end-of-line) (point))))
                (add-text-properties start end line-props)
                (add-face-text-property symbol-pos (+ 1 symbol-pos) face))))))
      (beginning-of-buffer)
      (replace-regexp "[[:space:]]+[=]+" ""))
    

keywords

(setq todo-keywords
      ;; normal workflow
      '((("DOING" "d" :icon "🏃" :face org-doing-face)
         ("TODO" "t" :icon "□ " :face org-todo-face)
         ("DONE" "D" :icon "✓ " :face org-done-face :done-state t))
        ;; auxillary states
        (("SOON" "s" :icon "❗ " :face org-soon-face)
         ("SOMEDAY" "S" :icon "🛌" :face org-doing-face)))
      org-todo-keywords (todo-org-todo-keywords todo-keywords)
      org-todo-keyword-faces (todo-org-todo-keyword-faces todo-keywords))

(--map (:push my/org-pretty-symbols it)
       (todo-prettify-symbols-alist todo-keywords))

org agenda finalization

(setq my/todo-sequences-data (todo-parse-sequences-data todo-keywords))
(:hook org-agenda-finalize
  (--each my/todo-sequences-data
    (-each it 'todo-finalize-agenda-for-state)))

sorting

(defun my/todo-sort (a b)
  (let* ((a-state (get-text-property 0 'todo-state a))
         (b-state (get-text-property 0 'todo-state b))
         (a-index (-elem-index a-state todo-keyword-order))
         (b-index (-elem-index b-state todo-keyword-order)))
    (pcase (- b-index a-index)
      ((and v (guard (< 0 v))) 1)
      ((and v (guard (> 0 v))) -1)
      (default nil))))

(setq org-agenda-cmp-user-defined 'my/todo-sort
      todo-keyword-order '("DOING" "SOON" "TODO" "SOMEDAY" "DONE"))

org-capture

org-directory

org-directory is the default location for captures.

(setq org-directory my/org-directory)

set default notes file

(setq org-default-notes-file my/notes-file-name)

automatically visit new capture

(:after org
  (:push org-capture-after-finalize-hook 'org-capture-goto-last-stored))

capture templates


org-babel

babel languages

  • ob-csharp
    (use-package ob-csharp
      :straight (ob-csharp :type git
                           :host github
                           :repo "thomas-villagers/ob-csharp"
                           :files ("src/ob-csharp.el"))
      :config
      (:push org-babel-load-languages '(csharp . t)))
    
  • ob-fsharp
    (use-package ob-fsharp
      :straight (ob-fsharp :type git
                           :host github
                           :repo "zweifisch/ob-fsharp"
                           :files ("ob-fsharp.el"))
      :config
      (:push org-babel-load-languages '(fsharp . t)))
    

enable languages

(setq org-babel-load-languages
      '((shell . t)
        (emacs-lisp . t)
        (python . t)
        (js . t)
        (csharp . t)
        (fsharp . t)))

default header args

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

disable evaluation prompts

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

install babel handlers

(:hook after-init
  (org-babel-do-load-languages 'org-babel-load-languages
                               org-babel-load-languages))

helm-org

(use-package helm-org)

helm-org-rifle

Quickly search through the current org buffer.

(use-package helm-org-rifle)

org-projectile

(use-package org-projectile
  :config
  (org-projectile-per-project)
  (setq org-projectile-per-project-filepath "notes.org")
  (:push org-capture-templates
    (org-projectile-project-todo-entry
     :capture-character "l"
     :capture-heading "Linked Project TODO"))
  (:push org-capture-templates
    (org-projectile-project-todo-entry
     :capture-character "p")))

org-projectile-helm

(use-package org-projectile-helm
  :after org-projectile
  :bind (("C-c n p" . org-projectile-helm-template-or-project)))

super-agenda

Provides better organization of the agenda view.

narrow agenda by header

Hitting C-<return> on a header will narrow the agenda to that file.

(setq my/org-agenda-nested nil)
(define-key org-agenda-mode-map (kbd "C-<return>")
  (lambda () (interactive)
    (unless my/org-agenda-nested
      (setq my/org-agenda-nested t)
      (with-current-buffer (marker-buffer (get-text-property (point) 'org-marker))
        (org-agenda nil "a" "<")))))

;; pop back to main agenda view, or quit
(defun my/org-agenda-quit ()
  (interactive)
  (org-agenda-quit)
  (when my/org-agenda-nested
    (setq my/org-agenda-nested nil)
    (org-agenda nil "a")))

(define-key org-agenda-mode-map (kbd "q") 'my/org-agenda-quit)

transform agenda items

Clean up the item display, including the project and file names.

(defun org-agenda-transformer (it)
  (-let* (((blank todo rest) (s-split-up-to
                              "[[:blank:]]+"
                              (substring-no-properties it) 2))
          (buffer-name (->> it
                            (get-text-property 0 'org-marker)
                            (marker-buffer)
                            (buffer-file-name)))
          (file-name (->> buffer-name f-filename))
          (project-name (->> buffer-name (f-parent) (f-base)))
          (text (s-collapse-whitespace
                 (format "  %s %s/%s: %s" todo project-name file-name rest))))
    (set-text-properties 0 (- (length text) 1) (text-properties-at 0 it) text)
    text))

(defun org-agenda-schedule-transformer (it)
  (-let* (((_ _ _ rest) (s-split-up-to
                         "[[:blank:]]+"
                         (substring-no-properties it) 3))
          (todo (get-text-property 0 'todo-state it))
          (buffer-name (->> it
                            (get-text-property 0 'org-marker)
                            (marker-buffer)
                            (buffer-file-name)))
          (file-name (->> buffer-name f-filename))
          (project-name (->> buffer-name (f-parent) (f-base)))
          (text (s-collapse-whitespace
                 (format "  %s %s/%s: %s" todo project-name file-name rest))))
    (set-text-properties 0 (length text) (text-properties-at 0 it) text)
    text))

group items by project & filename

(defun org-agenda-group-items (item)
  (-when-let* ((new-marker (make-marker))
               (marker (or (get-text-property 0 'org-marker item)
                           (get-text-property 0 'org-hd-marker item)))
               (file-path (->> marker marker-buffer buffer-file-name))
               (file-name (f-filename file-path))
               (directory (f-dirname file-path))
               (directory-name (f-filename
                                (or (projectile-root-bottom-up file-path) directory)))
               (heading (s-replace "//" "/" (format "%s/%s" directory-name file-name))))
    (set-marker new-marker 1 (marker-buffer marker))
    (propertize heading 'org-marker new-marker 'org-hd-marker new-marker)))

agenda layout

(defun get-agenda-commands ()
  '(("a" "agenda view"
     ((agenda "" ((org-agenda-overriding-header "")
                  (org-agenda-span 'day)
                  (org-super-agenda-groups
                   '((:name "Today"
                            :transformer (org-agenda-schedule-transformer it)
                            :scheduled today)
                     (:name "Overdue"
                            :transformer (org-agenda-schedule-transformer it)
                            :scheduled past)))))
      (alltodo "" ((org-agenda-overriding-header "")
                   (org-super-agenda-groups
                    '(;; (:name "Today" :time-grid t :date today :todo "TODAY" :scheduled today)
                      (:discard (:scheduled today :scheduled past))
                      (:name "Active" :todo "DOING" :transformer (org-agenda-transformer it))
                      (:name "Important" :tag "Important" :priority "A")
                      (:name "Some Day" :todo "SOMEDAY" :transformer (org-agenda-transformer it) :order 5)
                      (:name "Todo" :auto-map org-agenda-group-items :todo "TODO")))))
      ))))

org-agenda

Aggregate tasks across multiple org files.

super agenda mode

Enhance the agenda with dynamic groups.

(use-package org-super-agenda
  :config
  (setq org-agenda-custom-commands (get-agenda-commands))
  (org-super-agenda-mode))

don't format todo keywords

(setq org-agenda-todo-keyword-format ""
      org-agenda-prefix-format '((todo . "  %(org-get-todo-state)")))

sorting strategy

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

agenda files

Add files to agenda aggregation.

(setq org-agenda-files (->> (append (f-glob (my/get-org-file "*/*.org"))
                                    (f-glob (my/get-project-directory "*/*.org")))
                            (-filter 'f-exists?)))

org-fragtog

Automatically preview LaTex fragments.

(use-package org-fragtog
  :config
  (:hook org-mode 'org-fragtog-mode))

linkmarks

Use org as the bookmark backend.

(use-package linkmarks
  :straight (linkmarks :type git :host github :repo "dustinlacewell/linkmarks"))

org-ql

(use-package org-ql)

helm-org-walk

(use-package helm-org-walk
  :straight (helm-org-walk :type git :host github :repo "dustinlacewell/helm-org-walk"))

quiet file-local variables

(setq safe-local-variable-values '((org-confirm-elisp-link-function . nil)))

outshine

Collapse sections in various modes like elisp.

(use-package outshine
  :init (defvar outline-minor-mode-prefix "\M-#")
  :config (setq outshine-use-speed-commands t)
  :hook ((emacs-lisp-mode . outshine-mode) (nix-mode . outshine-mode)))

language support

flycheck

Show syntax errors for programming languages.

(use-package flycheck)

elisp

macrostep

Interactively expand macros.

(use-package macrostep
  :straight (macrostep :type git :host github :repo "joddie/macrostep")
  :config
  (:bind emacs-lisp-mode "C-c e" macrostep-expand))

lispy-mode

(use-package lispy
  :init
  (:hook emacs-lisp-mode (lispy-mode 1))
  (:hook lisp-interaction-mode (lispy-mode 1))
  :config
  (:bind lispy-mode ":" self-insert-command)
  (:bind lispy-mode "[" lispy-open-square)
  (:bind lispy-mode "]" lispy-close-square))

markdown-mode

All the internet uses it.

(use-package markdown-mode
  :commands (markdown-mode gfm-mode)
  :mode (("README\\.md\\'" . gfm-mode)
         ("\\.md\\'" . markdown-mode)
         ("\\.markdown\\'" . markdown-mode))
  :config (setq markdown-command "multimarkdown"))

python

elpy

(use-package elpy)

jedi

Jedi is an auto-completion server for Python.

(use-package jedi
  :init
  (progn
    (:hook python-mode jedi:setup)
    (setq jedi:complete-on-dot t)))

javascript

  • js2-mode
    (use-package js2-mode
      :config
      (:push auto-mode-alist '("\\.js\\'" . js2-mode))
      (:hook js2-mode js2-imenu-extras-mode)
      (define-key js2-mode-map (kbd "C-k") #'js2r-kill)
      (define-key js-mode-map (kbd "M-.") nil)
      (setq js2-include-node-externs t)
      (setq js2-include-browser-externs t))
    
  • xref-js2
    (use-package xref-js2
      :config
      (:hook js2-mode
        (:hook xref-backend-functions :local xref-js2-xref-backend)))
    
  • js2-refactor
    (use-package js2-refactor
      :config
      (:hook js2-mode js2-refactor-mode)
      (js2r-add-keybindings-with-prefix "C-c C-r"))
    
  • company-tern
    (use-package company-tern
      :config
      (setq tern-command '("npx" "tern"))
      (:push company-backends 'company-tern)
      (:hook js2-mode
        (tern-mode)
        (company-mode))
    
      ;; Disable completion keybindings, as we use xref-js2 instead
      (define-key tern-mode-keymap (kbd "M-.") nil)
      (define-key tern-mode-keymap (kbd "M-,") nil))
    

typescript

(use-package typescript-mode
  :config
  (flycheck-add-mode 'typescript-tslint 'web-mode))
  • tide
    (defun setup-tide-mode ()
      (interactive)
      (direnv-mode)
      (direnv-update-environment)
      (tide-setup)
      (flycheck-mode +1)
      (setq flycheck-check-syntax-automatically '(save mode-enabled))
      (eldoc-mode +1)
      (tide-hl-identifier-mode +1)
      (company-mode +1))
    
    (use-package tide
      :config
      (:hook before-save tide-format-before-save)
      (:hook typescript-mode setup-tide-mode)
      (:push auto-mode-alist '("\\.tsx\\'" . web-mode))
      (:hook web-mode
        (when (string-equal "tsx" (file-name-extension buffer-file-name))
          (setup-tide-mode)))
    
      (:hook web-mode
        (when (string-equal "tsx" (file-name-extension buffer-file-name))
          (setup-tide-mode)))
    
      (flycheck-add-mode 'typescript-tslint 'web-mode))
    

nim

(use-package nim-mode)

yaml

(use-package yaml-mode
  :config
  (:push auto-mode-alist '("\\.yml\\'" . yaml-mode)))

web-mode

(use-package web-mode
  :demand t
  :config
  (:push auto-mode-alist '("\\.tsx\\'" . web-mode))
  (:push auto-mode-alist '("\\.html?\\'" . web-mode))
  (:push web-mode-engines-alist '(("django"    . "\\.html\\'"))))

go-mode

(use-package go-mode
  :config (:hook go-mode
            (:hook before-save gofmt-before-save)
            (setq tab-width 4)
            (setq indent-tabs-mode 1)))

csharp

(use-package csharp-mode)

(:hook csharp-mode :local
  (company-mode)
  (flycheck-mode)

  (setq c-syntactic-indentation t)
  (c-set-style "ellemtel")
  (setq c-basic-offset 4)
  (setq truncate-lines t)
  (setq tab-width 4)

  (electric-pair-local-mode 1))

fsharp

(use-package fsharp-mode
  :config
  (require 'eglot)
  (:push auto-mode-alist '("\\.fs[iylx]?$" . fsharp-mode)))

tooling support

docker

(use-package dockerfile-mode
  :mode "Dockerfile\\'")

nix

(use-package nix-mode
  :straight (nix-mode :type git :host github :repo "NixOS/nix-mode")
  :mode "\\.nix\\'"
  :config
  (remove-hook 'before-save-hook #'nix-mode-format))

nix-sandbox

(use-package nix-sandbox)

elfeed

boilerplate

(defun advice-unadvice (sym)
  "Remove all advices from symbol SYM."
  (interactive "aFunction symbol: ")
  (advice-mapc (lambda (advice _props) (advice-remove sym advice)) sym))

(defun elfeed-font-size-hook ()
  (buffer-face-set '(:height 1.35)))

(defun elfeed-visual-fill-hook ()
  (visual-fill-column-mode--enable))

(defun elfeed-show-refresh-advice (entry)
  (elfeed-font-size-hook)
  (visual-fill-column-mode 1)
  (setq word-wrap 1)
  (elfeed-show-refresh))

(defun elfeed-show ()
  (interactive)
  (elfeed)
  (delete-other-windows))

setup

(use-package elfeed
  :bind (("C-x w" . elfeed-show))
  :config
  (:hook elfeed-search-update elfeed-font-size-hook)
  (advice-unadvice 'elfeed-show-entry)
  (advice-add 'elfeed-show-entry :after 'elfeed-show-refresh-advice))

(use-package elfeed-org
  :after (elfeed)
  :config
  (elfeed-org)
  (setq rmh-elfeed-org-files (list (my/get-org-file "notes.org"))))

misc

Miscellaneous packages that don't really need their own section.

htmlize

Allows org codeblocks to be syntax highlighted on html export.

(use-package htmlize)

emacsql

(use-package emacsql-sqlite)

gist

(use-package gist
  :straight (gist :type git :host github :repo "defunkt/gist.el"))

poker.el

(use-package poker
  :straight (poker :type git :host github :repo "mlang/poker.el"))

decide-mode

(use-package decide
  :straight (decide :type git :host github :repo "lifelike/decide-mode"))

lojban

;; (eval `(use-package sutysisku
;;    :demand t
;;    :straight (sutysisku :local-repo ,(my/get-project-directory "sutysisku.el/"))))

hydra

Hydra provides customizable interactive command palettes.

pretty-hydra

Pretty-hydra provides a macro that makes it easy to get good looking hydras.

(use-package pretty-hydra
  :demand t
  :straight (pretty-hydra :type git :host github
                          :repo "jerrypnz/major-mode-hydra.el"
                          :branch "c6554ea"
                          :files ("pretty-hydra.el")))

major-mode-hydra

Major-mode-hydra associates hydras with major-modes.

(use-package major-mode-hydra
  :straight (major-mode-hydra :type git :host github
                              :repo "jerrypnz/major-mode-hydra.el"
                              :branch "c6554ea"
                              :files ("major-mode-hydra.el")))

hera

Hera lets hydras form a stack.

(use-package hera
  :demand t
  :straight (hera :type git :host github :repo "dustinlacewell/hera"))

:hydra

Macro for defining Hydras.

boilerplate

  • inject-hint
    (defun :hydra/inject-hint (symbol hint)
      (-let* ((name (symbol-name symbol))
              (hint-symbol (intern (format "%s/hint" name)))
              (format-form (eval hint-symbol))
              (string-cdr (nthcdr 1 format-form))
              (format-string (string-trim (car string-cdr)))
              (amended-string (format "%s\n\n%s" format-string hint)))
        (setcar string-cdr amended-string)))
    
  • make-head-hint
    (defun :hydra/make-head-hint (head default-color)
      (-let (((key _ hint . rest) head))
        (when key
          (-let* (((&plist :color color) rest)
                  (color (or color default-color))
                  (face (intern (format "hydra-face-%s" color)))
                  (propertized-key (propertize key 'face face)))
            (format " [%s]: %s" propertized-key hint)))))
    
  • make-hint
    (defun :hydra/make-hint (heads default-color)
      (string-join
       (cl-loop for head in heads
                for hint = (:hydra/make-head-hint head default-color)
                do (pp hint)
                collect hint) "\n"))
    
  • clear-hint
    (defun :hydra/clear-hint (head)
      (-let* (((key form _ . rest) head))
        `(,key ,form nil ,@rest)))
    
  • add-exit-head
    (defun :hydra/add-exit-head (heads)
      (let ((exit-head '("SPC" (hera-pop) "to exit" :color blue)))
        (append heads `(,exit-head))))
    
  • add-heads
    (defun :hydra/add-heads (columns extra-heads)
      (let* ((cell (nthcdr 1 columns))
             (heads (car cell))
             (extra-heads (mapcar ':hydra/clear-hint extra-heads)))
        (setcar cell (append heads extra-heads))))
    
    

macro

(defmacro :hydra (name body columns &optional extra-heads)
  (declare (indent defun))
  (-let* (((&plist :color default-color :major-mode mode) body)
          (extra-heads (:hydra/add-exit-head extra-heads))
          (extra-hint (:hydra/make-hint extra-heads default-color))
          (body (plist-put body :hint nil))
          (body-name (format "%s/body" (symbol-name name)))
          (body-symbol (intern body-name))
          (mode-body-name (major-mode-hydra--body-name-for mode))
          (mode-support
           `(when ',mode
              (defun ,mode-body-name () (interactive) (,body-symbol)))))
    (:hydra/add-heads columns extra-heads)
    (when mode
      (cl-remf body :major-mode))
    `(progn
       (pretty-hydra-define ,name ,body ,columns)
       (:hydra/inject-hint ',name ,extra-hint)
       ,mode-support
       )))

tests

;; (macroexpand-all `(:hydra hydra-test (:color red :major-mode fundamental-mode)
;;    ("First"
;;     (("a" (message "first - a") "msg a" :color blue)
;;      ("b" (message "first - b") "msg b"))
;;     "Second"
;;     (("c" (message "second - c") "msg c" :color blue)
;;      ("d" (message "second - d") "msg d")))))

;; (:hydra hydra-test (:color red :major-mode fundamental-mode)
;;    ("First"
;;     (("a" (message "first - a") "msg a" :color blue)
;;      ("b" (message "first - b") "msg b"))
;;     "Second"
;;     (("c" (message "second - c") "msg c" :color blue)
;;      ("d" (message "second - d") "msg d"))))

default hydra

hydra-bookmarks

(:hydra hydra-bookmarks (:color blue)
  ("Bookmarks" (("n" (linkmarks-capture) "new")
                ("b" (linkmarks-select) "browse")
                ("e" (find-file my/bookmarks-file-name)))))

hydra-help

Many of the Emacs help facilities at your fingertips!

(:hydra hydra-help (:color blue)
  ("Describe"
   (("c" describe-function "function")
    ("p" describe-package "package")
    ("m" describe-mode "mode")
    ("v" describe-variable "variable"))
   "Keys"
   (("k" describe-key "key")
    ("K" describe-key-briefly "brief key")
    ("w" where-is "where-is")
    ("b" helm-descbinds "bindings"))
   "Search"
   (("a" helm-apropos "apropos")
    ("d" apropos-documentation "documentation")
    ("s" info-lookup-symbol "symbol info"))
   "Docs"
   (("i" info "info")
    ("n" helm-man-woman "man")
    ("h" helm-dash "dash"))
   "View"
   (("e" view-echo-area-messages "echo area")
    ("l" view-lossage "lossage")
    ("c" describe-coding-system "encoding")
    ("I" describe-input-method "input method")
    ("C" describe-char "char at point"))))

hydra-mark

(defun unpop-to-mark-command ()
  "Unpop off mark ring. Does nothing if mark ring is empty."
  (when mark-ring
    (setq mark-ring (cons (copy-marker (mark-marker)) mark-ring))
    (set-marker (mark-marker) (car (last mark-ring)) (current-buffer))
    (when (null (mark t)) (ding))
    (setq mark-ring (nbutlast mark-ring))
    (goto-char (marker-position (car (last mark-ring))))))

(defun push-mark ()
  (interactive)
  (set-mark-command nil)
  (set-mark-command nil))

(:hydra hydra-mark (:color pink)
  ("Mark"
   (("m" push-mark "mark here")
    ("p" (lambda () (interactive) (set-mark-command '(4))) "previous")
    ("n" (lambda () (interactive) (unpop-to-mark-command)) "next")
    ("c" (lambda () (interactive) (setq mark-ring nil)) "clear"))))

hydra-registers

(:hydra hydra-registers (:color pink)
  ("Point"
   (("r" point-to-register "save point")
    ("j" jump-to-register "jump")
    ("v" view-register "view all"))
   "Text"
   (("c" copy-to-register "copy region")
    ("C" copy-rectangle-to-register "copy rect")
    ("i" insert-register "insert")
    ("p" prepend-to-register "prepend")
    ("a" append-to-register "append"))
   "Macros"
   (("m" kmacro-to-register "store")
    ("e" jump-to-register "execute"))))

hydra-window

(use-package ace-window)
(winner-mode 1)

(:hydra hydra-window (:color red)
  ("Jump"
   (("h" windmove-left "left")
    ("l" windmove-right "right")
    ("k" windmove-up "up")
    ("j" windmove-down "down")
    ("a" ace-select-window "ace"))
   "Split"
   (("q" split-window-right "left")
    ("r" (progn (split-window-right) (call-interactively 'other-window)) "right")
    ("e" split-window-below "up")
    ("w" (progn (split-window-below) (call-interactively 'other-window)) "down"))
   "Do"
   (("d" delete-window "delete")
    ("o" delete-other-windows "delete others")
    ("u" winner-undo "undo")
    ("R" winner-redo "redo")
    ("t" nougat-hydra-toggle-window "toggle"))))

hydra-zoom

(:hydra hydra-zoom (:color red)
  ("Buffer"
   (("i" text-scale-increase "in")
    ("o" text-scale-decrease "out"))
   "Frame"
   (("I" zoom-frm-in "in")
    ("O" zoom-frm-out "out")
    ("r" toggle-zoom-frame "reset" :color blue))))

hydra-nix

(:hydra hydra-nix (:color blue)
  ("Nix" (("p" (progn (find-file (my/get-org-file "nixpkgs.org"))
                      (helm-org-in-buffer-headings)
                      (recenter-top-bottom 1)
                      (org-narrow-to-subtree)
                      (sit-for 5)
                      (widen)) "nixpkgs"))))

hydra-docs

(:hydra hydra-docs (:color blue)
  ("Docs" (("n" (hera-push 'hydra-nix/body) "Nix"))))

hydra-notes

(require 'helm-org-walk)

(:hydra hydra-notes (:color blue)
  ("Notes"
   (("o" (helm-org-walk '(4)) "open")
    ("s" (helm-org-rifle-org-directory) "search")
    ("S" (helm-org-rifle-occur-org-directory) "occur")
    ("q" (helm-org-ql-org-directory) "query")
    ("n" (helm-org-walk my/notes-file-name) "notes"))))

hydra-gist

(:hydra hydra-gist (:color blue)
  ("Gist" (("p" (gist-region-or-buffer) "public")
           ("P" (gist-region-or-buffer-private) "private")
           ("b" (browse-url "https://gist.github.com/dustinlacewell") "browse"))))

hydra-projectile

(:hydra hydra-projectile (:color blue)
  ("Open"
   (("o" (helm-projectile-switch-project) "project")
    ("p" (helm-projectile) "project asset")
    ("f" (helm-projectile-find-file-dwim) "file")
    ("b" (helm-projectile-switch-to-buffer) "buffer")
    ("r" (projectile-readme) "readme")
    ("w" (hydra-treemacs/body) "workspace"))
   "Do"
   (("s" (helm-projectile-ag) "search")
    ("c" (org-projectile-helm-template-or-project) "capture"))
   "Cache"
   (("C" projectile-invalidate-cache "clear cache")
    ("x" (projectile-remove-known-project) "remove this project")
    ("X" (projectile-cleanup-known-projects) "cleanup missing"))))

hydra-default

(:hydra hydra-default (:color blue)
  ("Open"
   (("a" (org-agenda nil "a") "agenda")
    ("p" (hera-push 'hydra-projectile/body) "projectile")
    ("o" (hera-push 'hydra-notes/body) "org")
    ("b" (hera-push 'hydra-bookmarks/body) "bookmarks"))
   "Emacs"
   (("h" (hera-push 'hydra-help/body) "help")
    ("m" (hera-push 'hydra-mark/body) "mark")
    ("w" (hera-push 'hydra-window/body) "windows")
    ("z" (hera-push 'hydra-zoom/body) "zoom")
    ("r" (hera-push 'hydra-registers/body) "registers"))
   "Misc"
   (("d" (hera-push 'hydra-docs/body) "docs")
    ("g" (hera-push 'hydra-gist/body) "gist"))))

modal hydras

boilerplate

hydra-dwim

Open hydra for current major mode if one exists, otherwise the default hydra.

(defun my/hydra-dwim ()
  (interactive)
  (let* ((mode major-mode)
        (orig-mode mode))
    (catch 'done
      (while mode
        (let ((hydra (major-mode-hydra--body-name-for mode)))
          (when (fboundp hydra)
            (hera-start hydra)
            (throw 'done t)))
        (setq mode (get mode 'derived-mode-parent)))
      (hera-start 'hydra-default/body))))

hydra-yank-pop

(:hydra hydra-yank-pop (:color red)
  ("Yank/Pop"
   (("y" (yank-pop 1) "previous")
    ("Y" (yank-pop -1) "next")
    ("l" helm-show-kill-ring "list" :color blue))))

(:bind "C-y"
  (yank)
  (hydra-yank-pop/body))

hydra-elisp

(:hydra hydra-elisp (:color blue :major-mode emacs-lisp-mode)
  ("Execute"
   (("d" eval-defun "defun")
    ("b" eval-current-buffer "buffer")
    ("r" eval-region "region"))
   "Debug"
   (("D" edebug-defun "defun")
    ("a" edebug-all-defs "all definitions" :color red)
    ("A" edebug-all-forms "all forms" :color red)
    ("x" macrostep-expand "expand macro"))))

hydra-treemacs

(:hydra hydra-treemacs (:color red)
  ("Workspace"
   (("o" treemacs-switch-workspace "open")
    ("n" treemacs-create-workspace "new")
    ("k" treemacs-delete-workspace "kill")
    ("r" treemacs-rename-workspace "rename"))))

hydra-org

hydra-org-goto-first-sibling

(defun hydra-org-goto-first-sibling () (interactive)
       (org-backward-heading-same-level 99999999))

hydra-org-goto-last-sibling

(defun hydra-org-goto-last-sibling () (interactive)
       (org-forward-heading-same-level 99999999))

hydra-org-parent-level

(defun hydra-org-parent-level ()
  (interactive)
  (let ((o-point (point)))
    (if (save-excursion
          (beginning-of-line)
          (looking-at org-heading-regexp))
        (progn
          (call-interactively 'outline-up-heading)
          (org-cycle-internal-local))
      (progn
        (call-interactively 'org-previous-visible-heading)
        (org-cycle-internal-local)))
    (when (and (/= o-point (point))
               org-tidy-p)
      (call-interactively 'hydra-org-tidy))))

hydra-org-child-level

(defun hydra-org-child-level ()
  (interactive)
  (org-show-entry)
  (org-show-children)
  (when (not (org-goto-first-child))
    (when (save-excursion
            (beginning-of-line)
            (looking-at org-heading-regexp))
      (next-line))))

hydra-org

(:hydra hydra-org (:color red :major-mode org-mode)
  ("Shift"
   (("K" org-move-subtree-up "up")
    ("J" org-move-subtree-down "down")
    ("h" org-promote-subtree "promote")
    ("l" org-demote-subtree "demote"))
   "Travel"
   (("p" org-backward-heading-same-level "backward")
    ("n" org-forward-heading-same-level "forward")
    ("j" hydra-org-child-level "to child")
    ("k" hydra-org-parent-level "to parent")
    ("a" hydra-org-goto-first-sibling "first sibling")
    ("e" hydra-org-goto-last-sibling "last sibling"))
   "Perform"
   (("t" (org-babel-tangle) "tangle" :color blue)
    ("e" (org-html-export-to-html) "export" :color blue)
    ("b" helm-org-in-buffer-headings "browse")
    ("r" (lambda () (interactive)
           (helm-org-rifle-current-buffer)
           (org-cycle)
           (org-cycle)) "rifle")
    ("w" helm-org-walk "walk")
    ("v" avy-org-goto-heading-timer "avy")
    ("L" org-toggle-link-display "toggle links"))))

hydra-js

(defun js2r-toggle-async ()
  (interactive)
  (if (string-equal "async" (thing-at-point 'word))
      (progn
        (search-forward "function")
        (backward-word)))
  (js2r-toggle-function-async))

(:hydra hydra-js (:color blue :major-mode js2-mode)
  ("Eval"
   (("f" js2-eval-defun "function")
    ("e" js2-eval "expression"))
   "Extract"
   (("f" js2r-extract-function "function")
    ("m" js2r-extract-method "method")
    ("v" js2r-extract-var "var")
    ("l" js2r-extract-let "let")
    ("c" js2r-extract-let "const"))
   "Funcs"
   (("a" js2r-toggle-arrow-function-and-expression "arrow" :color red)
    ("A" js2r-toggle-async "async" :color red)
    ("O" js2r-arguments-to-object "object param"))
   "Vars"
   (("r" js2r-rename-var "rename")
    ("i" js2r-inline-var "inline"))
   "Misc"
   (("k" js2r-kill "kill" :color red)
    ("t" js2r-string-to-template "string to template")
    ("l" js2r-log-this "log expr")
    ("w" js2r-wrap-buffer-in-iife "wrap in iife")
    ("G" js2r-inject-global-in-iife "global for iife"))))

linux configuration

preamble

(when (string-equal system-type "gnu/linux")

nixos

NixOS' ZSH module drops some PATH modification stuff in ~/.config/zsh/.zshrc which causes the following message on startup:

You appear to be setting environment variables ("PATH") in your .bashrc or .zshrc:
those files are only read by interactive shells, so you should instead set
environment variables in startup files like .profile, .bash_profile or .zshenv.
Refer to your shell's man page for more info.

Customize `exec-path-from-shell-arguments` to remove "-i" when done, or disable
`exec-path-from-shell-check-startup-files` to disable this message.

The following line prevents the warning above:

(setq exec-path-from-shell-check-startup-files nil)

theme

(load-file "~/.config/wpg/templates/theme.el")
(enable-theme 'xresources)

automatically update theme

(defun theme-callback (event)
  (load-file "~/.config/wpg/templates/theme.el")
  (enable-theme 'xresources))

(require 'filenotify)
(setq theme-watch-handle
      (file-notify-add-watch
       "/home/ldlework/.config/wpg/templates/theme.el" '(change) 'theme-callback))

default font

(setq powerline-height 32)
(set-face-attribute 'default nil :family "New Times Roman" :weight 'light)

unicode fonts

(use-package unicode-fonts
  :config
  (unicode-fonts-setup)
  (set-face-attribute 'default nil :font "Source Code Pro")
  ;(set-fontset-font "fontset-default" 'unicode "Consolas" nil)
  (set-fontset-font "fontset-default" 'unicode "DejaVu Sans Mono" nil)
  (set-fontset-font "fontset-default" 'unicode "Symbola" nil)
 )

core

minor modes

  • ispell-minor-mode
    (setq ispell-program-name (concat my/home-directory ".nix-profile/bin/aspell"))
    

fix tooltips

(setq x-gtk-use-system-tooltips nil)

helm

theme customizations

(set-face-attribute
 'helm-selection nil
 :inherit t
 :background (theme-color 'blue)
 :foreground (theme-color 'background)
 :height 1.0
 :weight 'ultra-bold
 :inverse-video nil)

(set-face-attribute
 'helm-source-header nil
 :inherit nil
 :underline nil
 :background (theme-color 'background)
 :foreground (theme-color 'light-red)
 :height 1.9)

(set-face-attribute
 'helm-header nil
 :inherit nil
 :height 0.8
 :background (theme-color 'background)
 :foreground (theme-color 'cyan))

(set-face-attribute
 'helm-separator nil
 :height 0.8
 :foreground (theme-color 'light-red))

(set-face-attribute
 'helm-match nil
 :weight 'bold
 :foreground (theme-color 'green))

filter nix wrappers

(require 'helm-external)
(setq helm-external-commands-list
      (seq-filter (lambda (v) (not (string-match "^\\." v)))
                  (helm-external-commands-list-1 'sort)))

postamble


)

csharp

  • omnisharp
    (use-package omnisharp)
    
    (:after company
      (:push company-backends #'company-omnisharp))
    
    (defun my-linux-csharp-mode-setup ()
      (omnisharp-mode)
      (local-set-key (kbd "C-c r r") 'omnisharp-run-code-action-refactoring)
      (local-set-key (kbd "C-c C-c") 'recompile))
    
    (:hook csharp-mode my-linux-csharp-mode-setup t)
    

windows configuration

preamble

(when (string-equal system-type "windows-nt")

theme

(use-package dracula-theme)

postamble

)

postfix

(setq org-html-htmlize-output-type 'css)
(setq org-html-head-include-default-style nil)

Created: 2020-09-23 Wed 13:00

Validate