UP | HOME

emacs setup

Table of Contents

1. Introduction

Roland Conybeare's curated emacs setup. As of 6oct2023 using emacs version 29.0.91, installed via the nix package manager.

~/.emacs symlinks to ~/proj/env/dotfiles/emacs from git repo https://github.com/Rconybea/env.git; using this to streamline account setup on new hosts.

2. .emacs

2.1. misc

Compatibility with some Common Lisp idioms

(require 'cl-loaddefs)

2.2. package bootstrapping

(require 'package)
;;(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))

(let ((repos '(("melpa" . "https://melpa.org/packages/")
               ;;("marmalade" . "http://marmalade-repo.org/packages/")
               ;;("elpa"      . "http://elpa.gnu.org/packages/")
               ;;("elpa" . "http://tromey.com/elpa/")
               ;;("melpa"    . "http://melpa.milkbox.net/packages/")
               )))
  (while repos
    (add-to-list 'package-archives (car repos) t)
    (setq repos (cdr repos))))

(package-initialize)

(when (not package-archive-contents)
  (package-refresh-contents))

(unless (package-installed-p 'use-package)
  (package-refresh-contents)
  (package-install 'use-package))

And some packages to autofetch

;; ----------------------------------------------------------------
;; list of emacs packages to auto-install
;; (superseded by (use-package)...?  I may be confused about this)
;; ----------------------------------------------------------------
;;
(defvar roland/packages
  '(surround treemacs highlight rg treemacs-projectile treemacs-magit
             lsp-mode lsp-treemacs lsp-ivy magit company flycheck exec-path-from-shell projectile yasnippet))

;; install missing packages if they are whitelisted in roland/packages
;;
(dolist (p roland/packages)
  (when (not (package-installed-p p))
    (package-install p)))

2.3. quality-of-life details

2.3.1. qol: display

;; grab a little bit of extra screen real estate

;; disable scrollbars
(if (fboundp 'scroll-bar-mode)
    (scroll-bar-mode -1))
;; disable toolbars
(if (fboundp 'tool-bar-mode)
    (tool-bar-mode -1))
;; disable menu bar
(if (fboundp 'menu-bar-mode)
    (menu-bar-mode -1))

;; prevent initial emacs splash screen
(setq inhibit-splash-screen t)

2.3.2. qol: yes-or-no

;; don't like having to type 'yes' or 'no';  'y' or 'n' will do

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

2.3.3. qol: nuke ctrl-Z

;; C-z bound to suspend frame is annoying,  because
;; it gets typed by accident too often.
;; C-x C-z remains available as an alternative binding
(global-unset-key (kbd "C-z"))
(global-unset-key (kbd "<return>"))

2.3.4. qol: shortcut to bury current buffer

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

2.3.5. qol: navigation

(global-set-key (kbd "C-x t") 'beginning-of-buffer)
(global-set-key (kbd "C-x e") 'end-of-buffer)

;; keeping window hopping to current frame allows for satellite frames that are
;; mostly display-only (e.g. magit status)
;;
(global-set-key (kbd "C-x o") 'other-window)
(global-set-key (kbd "C-x p") (lambda () (interactive) (other-window -1)))
;;(global-set-key "\C-xo" 'next-multiframe-window)
;;(global-set-key "\C-xp" 'previous-multiframe-window)

(global-set-key (kbd "S-<left>") 'windmove-left)
(global-set-key (kbd "S-<right>") 'windmove-right)
(global-set-key (kbd "S-<up>") 'windmove-up)
(global-set-key (kbd "S-<down>") 'windmove-down)

2.3.6. qol: kill

;; instead of kill-region,
;; prefer consistency with emacs-like keybindings e.g. gnu readline
(global-set-key (kbd "C-w") 'backward-kill-word)

;; instead of C-w..
(global-set-key (kbd "C-x C-k") 'kill-region)
(global-set-key (kbd "C-c C-k") 'kill-region)

This tweak allows using zap on necessary punctuation characters, without swallowing them.

;; use zap-up-to-char instead of zap-to-char
(autoload 'zap-up-to-char "misc"
  "kill up to, but not including ARG'th occurence of CHAR.
\(fn arg char)"
  'interactive)
(global-set-key "\M-z" 'zap-up-to-char)

2.3.7. qol: query replace

;; M-x qr --> M-x query-replace
;; M-x qrr --> M-x query-replace-regexp

(defalias 'qr 'query-replace)
(defalias 'qrr 'query-replace-regexp)

2.3.8. qol: mouse-3 on macos

;; mouse-3 annoying on macbook 11.3

(if (eq system-type 'darwin)
    (global-unset-key [mouse-3]))

2.3.9. qol: mouse avoidance

Don't let mouse icon obscure cursor

(mouse-avoidance-mode 'animate)

Make cursor and mouse easier to see

(set-mouse-color "red")
(set-cursor-color "red")

2.4. package: try

;; the 'try' package lets us try a package without permanently installing it.
;; use
;;  M-x try ${nameofpackage}
;;
(use-package try :ensure t)

2.5. package: which-key

;; the 'which-key' package displays popup with key bindings following
;; currently entered incomplete command
;;
(use-package which-key :ensure t :config (which-key-mode))

2.6. backup file location

;; backup location
;; (don't clutter filesystem with emacs backup files)

(let ((backup-dir "~/tmp/emacs/backups")
      (auto-saves-dir "~/tmp/emacs/autosaves"))
  ;; create dirs if needed
  (dolist (dir (list backup-dir auto-saves-dir))
    (when (not (file-directory-p dir))
      (make-directory dir t)))
  (setq backup-directory-alist `(("." . ,backup-dir))
        auto-save-file-name-transforms `((".*" ,auto-saves-dir t))
        auto-save-list-file-prefix (concat auto-saves-dir ".saves-")
        ;; tramp-backup-directory-alist `((".*" . ,backup-dir))
        ;; tramp-auto-save-directory auto-saves-dir
        )
  (setq backup-by-copying t
        delete-old-versions t
        version-control t
        kept-new-versions 5
        kept-old-versions 2)
  )

2.7. prefer ibuffer

;; ----------------------------------------------------------------
;; replace list-buffers (^x^b) with ibuffer
;; ibuffer is much better

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

2.8. package: surround

Region selection based on matching parens

;; ----------------------------------------------------------------
;; M-' i (   mark text up to but excluding surrounding ().
;;           M-' ( will do the same
;; M-' o (   mark surrounding ()  can use either ( or ) to identify paren type
;;           M-' ) will do the same
;; M-' s {   surround word-around-point with {}
;; M-' d (   delete innermost surrounding pair of ()'s
;; M-' k (   kill surrounding text up to but not including innermost surrounding () pair
;; M-' K (   kill surrounding text including innermost surrounding () pair
;;
(use-package surround :ensure t
  :bind-keymap ("M-'" . surround-keymap))

2.9. package: highlight

Manual highlighting

;; ----------------------------------------------------------------
;; highlight.el
;;
;;   C-x X u r    unhighlight region
;;
(use-package highlight
  :ensure t)

2.10. track current directory reliably in *shell* buffers

;; ----------------------------------------------------------------
;; Minor mode to track directory via /proc.
;; Will not work on macos!
;;

(defun shell-procfs-dirtrack (str)
    ;;; shell-procfs-dirtrack --- use /proc/PID/cwd to track current working directory
    ;;; for emacs shell buffers.  From emacswiki "Shell Dirtrack by Procfs"
    ;;;
  (prog1 str
    (when (string-match comint-prompt-regexp str)
      (let ((directory (file-symlink-p
                        (format "/proc/%s/cwd"
                                (process-id
                                 (get-buffer-process
                                  (current-buffer)))))))
        (when (file-directory-p directory)
          (cd directory))))))

;; have to enable this
;;
(define-minor-mode shell-procfs-dirtrack-mode
  "Track shell directory by inspecting procfs."
  nil nil nil
  (cond (shell-procfs-dirtrack-mode
         (when (bound-and-true-p shell-dirtrack-mode)
           (shell-dirtrack-mode 0))
         (when (bound-and-true-p dirtrack-mode)
           (dirtrack-mode 0))
         (add-hook 'comint-preoutput-filter-functions
                   'shell-procfs-dirtrack nil t))
        (t
         (remove-hook 'comint-preoutput-filter-functions
                      'shell-procfs-dirtrack t))))

;; ----------------------------------------------------------------
;; command shell.
;;
;; invoking emacs from
;;   nix develop -i
;; interferes with its ability to figure out suitable shell
;; tell emacs to use bash here
;;
(setq-default shell-file-name "bash")

2.11. dynamic abbreviations

For consciously correcting typos

;; ----------------------------------------------------------------
;;
(global-set-key (kbd "C-<tab>") #'dabbrev-expand)
(define-key minibuffer-local-map (kbd "C-<tab>") #'dabbrev-expand)

2.12. package: flycheck

Flycheck runs in the background. It automatically (and subtly) highlights syntax errors as you type. Preferred over built-in flyspell.

;; enable flycheck

;; see:
;;   https://www.flycheck.org/en/latest/user/quickstart.html#flycheck-quickstart
;;
;; keybindings:
;;   C-c ! v              show version + configuration info for this buffer
;;   C-c ! n / C-c ! p    inspect errors on this line
;;   C-c ! l              show errors for this buffer

(global-flycheck-mode)

(if (eq system-type 'darwin)
    ;; need this for flycheck on macos only..
    (exec-path-from-shell-initialize))

2.13. package: ripgrep

Projectile uses this automagically if available

;; ----------------------------------------------------------------
;; ripgrep for search
;; see [[rg.el.readthedocs.io/en/latest/configuration.html]]
;;
;;  rg-executable-per-connection
;;  rg-custom-type-aliases
;;  rg-prioritized -type-aliases
;;  rg-default-alias-fallback
;;  rg-command-line-flags
;;  rg-group-result
;;  rg-show-columns
;;  rg-ignore-case
;;  rg-hide-command t
;;  rg-keymap-prefix "C-c s"
;;  rg-use-transient-menu t
;;  rg-show-header t
;;  rg-buffer-name "rg"
;;  rg-ignore-ripgreprc t
;;  ...and more...
;;
;; See also rg-isearch  to use ripgrep with isearch
;;
(use-package rg
  :ensure t
  :init (progn
          (rg-enable-default-bindings)
          ;; want see rg command to debug it :)
          (setq rg-hide-command nil)
          ;; need filename to report line number for navigation with C-x ` to work
          (setq rg-command-line-flags nil)
          ;;(setq rg-use-transient-menu t)  etc
          ;; trying to make it easy to visit match results in context
          (setq rg-group-result nil)
          )
  )

2.14. completion assist

2.14.1. completion: ivy

;; ----------------------------------------------------------------
;; ivy (completion assist)
;;
;; keybindings:
;;   M-x     ivy-mode         disable/enable toggle
;;   C-x b   ivy-switch-buffer
;;   C-c v   ivy-push-view
;;   C-c V   ivy-pop-view
;;   ivy-resume           resume last ivy completion
;;
;; in minibuffer:
;;   C-n   ivy-next-line             next line
;;   C-p   ivy-prev-line             previous line
;;   M-<   ivy-beginning-of-buffer   first candidate
;;   M->   ivy-end-of-buffer         last candidate
;;   C-v   ivy-scroll-up-command     scroll up ivy-height lines
;;   M-v   ivy-scroll-down-command   scroll down ivy-height lines
;;
;; actions (on selected choice):
;;   c-M or RET   ivy-done               default action + exit minibuffer
;;   M-o          ivy-dispatching-done   present menu of choices
;;   C-j          ivy-alt-done           for file name, cd into selected dir candidate
;;   TAB          ivy-partial-or-done    completion on current input
;;   C-M-j        ivy-immediate-done     exit with current candidate (exact spelling)
;;   C-'          ivy-avy                use avy to select from current candidate page
;;
;;   C-M-m        ivy-call               non-exiting version of ivy-done
;;   C-M-o        ivy-dispatching-call   non-exiting version of ivy-dispatching-done
;;
;;   C-o          hydra-ivy/body         invoke hydra menu with short key bindings
(use-package ivy
  :ensure t
  :init (progn

          (setq ivy-use-virtual-buffers t)
          (setq ivy-count-format "(%d/%d) ")
          (ivy-mode 1)
          (global-set-key (kbd "C-x b") 'ivy-switch-buffer)
          ;; or (bind-key "C-x b" 'ivy-switch-buffer)
          )
  )

2.15. C++

2.15.1. c++: indenting + tabs

;; in general want to expand tabs,  + use 4 characters

(setq-default indent-tabs-mode nil)
(setq-default tab-width 4)

;; used in style-specific modes, e.g. c++-mode
;; other styles: gnu | k&r | bsd | whitesmith | stroustrup | ellemtel | user
;;
;; C-c . <style>    choose predefined style
;;(defvar 'c-default-style)
;;(defvar 'c-basic-offset)

(setq-default c-default-style "linux")
(setq-default c-basic-offset 4)

(defun disable-tabs ()
  "Function to disable tab insertion; intended to be attached via c++-mode-hook."
  (if (boundp 'indent-tabs-mode)
      (setq indent-tabs-mode nil)
    (error "Expected variable [indent-tabs-mode] to be defined")
    ))

;; in general disable tabs (expand to spaces)
(add-hook 'c++-mode-hook 'disable-tabs)

2.15.2. c++: whitespace

;; will be using customized whitespace mode,  that just highlights trailing spaces
;;
(add-hook 'c++-mode-hook 'whitespace-mode)

;; tabs:  visible tab characters
;; trailing: visible trailing whitespace
;;
(setq-default whitespace-style '(face tabs tab-mark trailing))

;; 124 is | character
(setq-default whitespace-display-mappings
              '((tab-mark 9 [124 9] [92 9])))

(defun untabify-except-makefiles ()
  "Replace tabs with spaces except in makefiles."
  (unless (derived-mode-p 'makefile-mode)
    (prog1 nil
      (untabify (point-min) (point-max))
      (delete-trailing-whitespace)
      )))

(add-hook 'c++-mode-hook
          #'(lambda ()
              (add-hook 'before-save-hook #'untabify-except-makefiles)))
(add-hook 'nix-mode-hook
          #'(lambda ()
              (add-hook 'before-save-hook #'untabify-except-makefiles)))

2.15.3. cmake editing

;; cmake mode
;;
(use-package cmake-mode
  :ensure t
  :init (progn
          (setq cmake-tab-width 4)
          )
  )

2.15.4. c++ completion

(use-package company
  :config
  (progn
    (setq company-minimum-prefix-length 1) ;; LOOKS LIKE MUST HAVE THIS for company to activate
    (setq company-idle-delay 0.1) ;; default 0.2
    ))

(add-hook 'c++-mode-hook 'company-mode)

2.15.5. compilation buffer

For example, to respond to cmake colored output

;; ----------------------------------------------------------------
;; xterm-color
;;
;; more modern package relative to ansi-color
;; see [[https://stackoverflow.com/questions/3072648/cucumbers-ansi-colors-messing-up-emacs-compilation-buffer]]
;;
(defun my/advice-compilation-filter (f proc string)
  "colorize buffer output"
  (funcall f proc (xterm-color-filter string)))

(defvar compilation-environment)

(use-package xterm-color
  :ensure t
  :init (progn (setq compilation-environment '("TERM=xterm-256color"))
               (advice-add 'compilation-filter :around #'my/advice-compilation-filter))
  )

2.16. Development

2.16.1. dev: snippets

;; snippets!
;; (see http://capitaomorte.github.io/yasnippet,
;;  http://githib.com/capitaomorte/yasnippet)
;; snippets in ~/.emacs.d/elpa/yasnippet-20200604.246/snippets
;;
;; summary:
;;  M-x yas-expand -- try to expand snippet before point.  Usually TAB
;;  M-x yas-load-directory -- load a directory of snippets
;;  M-x yas-activate-extramode -- add snippets for a particular mode, in the current buffer
;;  M-x yas-insert-snippet -- insert snippet at point
;;  M-x yas-visit-snippet-file -- prompt for snippet expansion,  but go to definition
;;  M-x yas-new-snippet -- create a new snippet in ~/.emacs.d/snippets, function of major mode
;;  M-x yas-load-snippet-buffer -- load the snippet you are editing C-c C-c in snippet-mode
;;  M-x yas-tryout-snippet -- insert current snippet in a new empty buffer C-c C-t in snippet-mode
;;  M-x yas-describe-tables -- list known snippets
;;

(use-package yasnippet
  :ensure t
  :config (yas-global-mode 1))

(add-hook 'c++-mode-hook 'yas-minor-mode)
(add-hook 'cmake-mode-hook 'yas-minor-mode)
(add-hook 'emacs-lisp-mode-hook 'yas-minor-mode)

(global-set-key (kbd "C-<return>") 'yas-expand)
(global-set-key (kbd "C-c & C-n") 'yas-new-snippet)
(global-set-key (kbd "C-c & C-v") 'yas-visit-snippet-file)

;;(yas-global-mode t)
;;;; yas-snippet-dirs
;;;; yas-installed-snippets-dir
;;;; (yas-reload-all)

2.16.2. dev: treemacs

;; ----------------------------------------------------------------
;; treemacs

(use-package treemacs
  :config
  (progn
    (setq treemacs-follow-after-init t
          treemacs-is-never-other-window t
          treemacs-width 40)
    (treemacs-follow-mode t)
    (treemacs-filewatch-mode t)
    (treemacs-git-mode 'simple)
    (treemacs-fringe-indicator-mode t)
    (treemacs-resize-icons 11)  ;; default is 22
    )
  :hook (after-init . treemacs)
  :bind (:map global-map ("C-M-t" . treemacs)))

2.16.3. dev: projectile

;; projectile

(defconst modi/rg-arguments
  `("--line-number" ; print line numbers
    "--smart-case"
    "--follow"      ; follow symlinks
    "--mmap"        ; use memory-map optimization where available
    )
  "default rg args used in the `projectile` package.")

(defun modi/advice-projectile-use-rg (mode)
  "always use `rg' to establish project file list"
  (mapconcat 'identity
             (append '("\\rg") ; unalised version of rg
                     modi/rg-arguments
                     '("--null"   ; null-separated results
                       "--files"  ; locate + print file names (don't search them)
                       ))
             " "))

(use-package projectile
  :ensure t
  :init (progn (projectile-mode +1)
               ;;(setq projectile-indexing-method 'native) -- want to use rg below
               (setq projectile-project-compilation-dir "")
               (setq projectile-enable-caching t)
               ;; using rg to identify project source files:
               ;; - honors .gitignore
               ;; - fast, too!
               (when (executable-find "rg")
                 (progn
                   (advice-add 'projectile-get-ext-command
                               :override #'modi/advice-projectile-use-rg))))
  :bind (:map projectile-mode-map
              ("s-p" . projectile-command-map)
              ("C-c p" . projectile-command-map)))

2.16.4. dev: treemacs+projectile integration

;; ----------------------------------------------------------------
;; treemacs-projectile

;; use
;;  M-x treemacs-add-project-to-workspace     make project (directory tree) showup in treemacs
;;
(use-package treemacs-projectile
  :ensure t
  )

2.16.5. dev: language server protocol (LSP)

;; ----------------------------------------------------------------
;; language-server-protocol

;; +format-with-lsp: provided by lsp-mode
(defvar +format-with-lsp)
(defvar lsp-clients-clangd-args)

;; see:
;;   https://emacs-lsp.github.io/lsp-mode

(defun disable-lsp-format ()
  "Disable clang-driven formatting via lsp to prefer emacs-native formatting."
  (setq +format-with-lsp nil))

(use-package lsp-mode
  :hook   (lsp-mode . (lambda ()
                        ;; from [[https://github.com/emacs-lsp/lsp-mode/issues/1532]] (mar 2020)
                        (let ((lsp-keymap-prefix "C-c l"))  ; default s-l collides with windows key
                          (lsp-enable-which-key-integration))))
  :config (progn
            ;; reminder: :config runs *after* package is loaded
            (define-key lsp-mode-map (kbd "C-c l") lsp-command-map)

            (setq lsp-enable-indentation nil) ; disable clang indentation -- doesn't preserve newlines!
            (setq lsp-enable-on-type-formatting nil)
            (setq lsp-headerline-breadcrumb-enable t)  ;; enable path-to-this-file headerline
            (setq lsp-modeline-diagnostics-enable t)
            (setq lsp-completion-provider :capf)
            (setq lsp-completion-show-detail t) ;; rhs of completion popup
            (setq lsp-completion-show-kind t)
            ;; semgrep crashing incessantly, looks like can't resolve host semgrep.dev?
            ;; *semgrep-ls::stderr* contains
            ;;   TLS to non-TCP currenlty unsupported: host=semgrep.dev endp=(Unknown "name resolution failed")
            (setq lsp-disabled-clients '(semgrep-ls))
            ;;(setq lsp-signature-render-documentation t) ;; include documentation with signatures
            (add-hook 'c++-mode-hook #'lsp-deferred)
            (add-hook 'c++-mode-hook #'disable-lsp-format)
            ;; additional clangd args:
            ;;   --compile-commands-dir=path/to/compile_comamnds.json
            ;; (ed: prefer better discoverable with build-specific symlink in git root for each project)
            ;;   --fallback-style
            ;; (ed: could use this instead of writing .clang-format)
            ;;   --header-insertions=iwyu|never
            ;;   --enable-config
            ;; (ed: docs at
            ;;   --pch-storage=disk|memory
            (setq lsp-clients-clangd-args '("-j=4" "-background-index" "-log=error")))
  ;;(add-hook 'python-mode-hook #'lsp)
  )

;; ----------------------------------------------------------------

;; lsp-ui-sideline:  show info associated with symbols toward rhs of current line
;;   lsp-ui-sideline-show-diagnostics
;;   lsp-ui-sideline-show-hover
;;   lsp-ui-sideline-show-code-actions
;;   lsp-ui-sideline-update-mode
;;   lsp-ui-sideline-delay
;;
;; lsp-ui-peek
;;  examples
;;   (define lsp-ui-mode-map [remap xref-find-definitions] #'lsp-ui-peek-find-definitions)
;;   (define lsp-ui-mode-map [remap xref-find-references] #'lsp-ui-peek-find-references)
;;   (lsp-ui-peek-jump-backward)
;;   (lsp-ui-peek-jump-forward)
;;   (lsp-ui-peek-find-workspace-symbol "pattern 0")
;;   (lsp-ui-peek-find-custom 'base "$cquery/base")
;;
;;   lsp-ui-peek-enable
;;   lsp-ui-peek-show-directory
;;
;; lsp-ui-doc
;;   lsp-ui-doc-enable
;;   lsp-ui-doc-position
;;   lsp-ui-doc-delay
;;   lsp-ui-doc-show-with-cursor
;;   lsp-ui-doc-show-with-mouse
;;
;; lsp-ui-imenu
;;   lsp-ui-imenu-kind-position
;;   lsp-ui-imenu-buffer-position
;;   lsp-ui-imenu-window-width
;;   lsp-ui-imenu-window-fix-width
;;   lsp-ui-imenu--custome-mode-line-format
;;   lsp-ui-imenu-auto-refresh
;;
(use-package lsp-ui
  :ensure t
  :config
  (setq lsp-ui-sideline-show-diagnostics t)
  (setq lsp-ui-doc-enable t)    ;; hover dialogs
  (setq lsp-ui-doc-show-with-cursor t)
  )

2.16.6. dev: lsp+ivy integration

;; ----------------------------------------------------------------
;; LSP ivy integration

;; M-x lsp-ivy-worskpace-symbol
;; M-x lsp-ivy-global-workspace-symbol

(use-package lsp-ivy
  :ensure t
  :bind (:map global-map
              ("M-g s" . lsp-ivy-workspace-symbol)
              ("M-g S" . lsp-ivy-global-workspace-symbol))
  )

2.16.7. dev: git integration

;; ----------------------------------------------------------------
;; git integration
;;  C-x M-g j   magit status buffer for current repo
;;
;; in status buffer:
;;   d change diff parameters
;;      -w  suppress whitespace-only diffs
;;      C-x extended menu
;;           C-s save settings for future sessions
;;
(use-package magit
  :ensure t
  :init (progn (bind-key "C-x M-g j" 'magit-status)))

2.16.8. dev: nix mode

Syntax highlighting in nix files

;; ----------------------------------------------------------------
;; nix mode (syntax highlighting etc for .nix files)
;;
(use-package nix-mode :ensure t)

2.16.9. dev: yaml mode

Syntax highlighting in yaml files

;; yaml mode (syntax highlighting etc for .yml, .yaml files)
;;
(use-package yaml-mode :ensure t
  :init (progn (add-to-list 'auto-mode-alist '("\\.yml\\'" . yaml-mode))))

2.17. org-mode

2.17.1. org: publishing

;; org-mode publishing
;;
(require 'ox-publish)

;; syntax highlighting in html exporter
;;
(use-package htmlize :ensure t)

;; publishing setup -- this is a kitchen sink -- it's intended to cover
;; everything we want org-mode to publish
;;
(setq org-publish-project-alist
      '(
        ("org-howto"
         :components ("org-howto-notes" "org-howto-static"))

        ("org-howto-notes"
         :base-directory "~/proj/org-howto"
         :base-extension "org"
         :publishing-directory "~/proj/public_html/org-howto"
         :recursive t
         :publishing-function org-html-publish-to-html
         :headling-levels 4
         :auto-preamble t
         )

        ("org-howto-static"
         :base-directory "~/proj/org-howto"
         :base-extension "md\\|css\\|html\\|js\\|svg\\|ico\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf"
         :publishing-directory "~/proj/public_html/org-howto"
         :recursive t
         :publishing-function org-publish-attachment
         )

        ;; ----- non-public org stuff -----

        ("org-private"
         :components ("org-private-notes" "org-private-static"))

        ("org-private-notes"
         :base-directory "~/proj/org-private"
         :base-extension "org"
         :publishing-directory "~/proj/private_html/org-private"
         :recursive t
         :publishing-function org-html-publish-to-html
         :headling-levels 4
         :auto-preamble t
         )

        ("org-private-static"
         :base-directory "~/proj/org-private"
         :base-extension "md\\|css\\|html\\|js\\|svg\\|ico\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf"
         :publishing-directory "~/proj/private_html/org-private"
         :recursive t
         :publishing-function org-publish-attachment
         )
        ))

2.17.2. babel setup

For literate programming – execute code blocks, written in various languages, embedded in .org files

;; ----------------------------------------------------------------
;; org-mode babel setup
;; (execute dot/ditaa/bash/c++ etc. from .org code blocks)
;;
;; see
;;   [[https://orgmode.org/worg/org-contrib/babel/intro.html]]
;;   [[https://orgmode.org/worg/org-contrib/babel/languages/index.html]]
;;
;; code block:
;;   #+begin_src ${language} ${switches} ${headerarguments}
;;     ${body}
;;   #+end_src
;;
;; use header argument
;;   :results output
;; to insert code-block output (i.e. contents of stdout) into .org file below code block
;;
;; use
;;   :results value
;; to just take value of last statement
;;
;; use
;;   :session
;; to share language sub-process across code-blocks.
;;
;; can use
;;   #+name: foo
;; to name an org-mode table;  then:
;;   #+begin_src ... :var myfreevar=foo
;;     ...
;;   #+end_src
;; with body mentioning myfreevar;  .org will substitute foo
;;
;; can do inline coode block with
;;   src_<${lang}>{${code}} or src_<${lang}[${args}]{${code}}
;; e.g.
;;   src_python[:session]{10*x}
;;
;; notes:
;;   [C-c C-v] org-babel prefix
;;   [C-c C-v b] -- evaluate code blocks in buffer
;;   [C-c C-v s] -- evaluate code blocks in subtree
;;   [C-c C-v e] -- evaluate code block at point
;;   [C-c '] M-x org-edit-src-code -- puts code block in new buffer with appropriate mode activated
;;   [C-c M-b p] M-x org-babel-expand-src-block -- show expanded code block prior to evaluation
;;   (org-babel-lob-ingest "path/to/file.org") to share code blogs as 'library'

(org-babel-do-load-languages
 'org-babel-load-languages
 '(
   (C . t)    ; C,C++,D [[https://orgmode.org/worg/org-contrib/babel/languages/ob-doc-C.html]]
   (ditaa .t) ; "diagrams through ascii art"
   (dot . t)  ; graphviz [[https://orgmode.org/worg/org-contrib/babel/languages/ob-doc-dot.html]] see also graphviz-dot-mode
   ))

For ditaa, need some paths. Nix makes these obscure, need to dig out the parts we want

;; extracted from command like
;;   cat $(which ditaa) | sed -n -e '/^exec/s: -jar .*$::p' | sed -e 's:^exec ::'
;; expecting result like
;;  /nix/store/6g5q62wafkpg0p3w854vc9yxy9bsnwi5-openjdk-19.0.2+7/bin/java
;;
;; the /usr/bin paths + /nix/store/.*ditaa.jar exprs for shmecurity
;;
(setq org-babel-ditaa-java-cmd
      (shell-command-to-string "/usr/bin/cat $(which ditaa) | /usr/bin/sed -n -e '/^exec/s: -jar .*\$::p' | /usr/bin/sed -e 's:^exec ::' | /usr/bin/tr -d '\n'"))

;; extracted from command like
;;   cat $(which ditaa) | grep ' -jar ' | sed -e 's:^exec.* -jar "::' -e 's:" "\$@"::'
;; expecting result like
;;   /nix/store/in2hf1xcxr1i4rijw0g423kawr3624pv-ditaa-0.11.0/lib/ditaa.jar
;;
;; the /usr/bin paths + /nix/store/.*ditaa.jar exprs for shmecurity
;;
(setq org-ditaa-jar-path
      (shell-command-to-string "/usr/bin/cat $(which ditaa) | /usr/bin/grep ' -jar ' | /usr/bin/sed -e 's:^exec.* -jar \"::' -e 's:\" \"\\$@\"::' | /usr/bin/grep '^/nix/store/.*ditaa.jar' | /usr/bin/tr -d '\n'"))
;; (setq org-ditaa-eps-jar-path "path/to/related/jar") ;; not using, but if so would need it

2.18. email

2.18.1. incoming mail

Using notmuch for incoming mail (notmuch configured using notmuch setup)

;; ----------------------------------------------------------------
;; local email storage
;;
;; 1. sync email (e.g. fetch remote emails) with
;;   $ mbsync -a
;;
;; 2. apply rules locally (move emails to folders etc.)  with
;;   $ cleanmbox
;;
;; 3. sync email again (e.g. propagate changes back to remote mailboxes)
;;   $ mbsync -a
;;
;; 4. to read email from local copy,  use
;;   M-x notmuch
;;
;;   show       ;; to see everything
;;
;; use [M-x customize-group RET notmuch RET] to customize
;; use [M-x notmuch] to invoke
;; in a [*notmuch-hello*] buffer,  use
;;   folder:gmail/.health
;; to see messages in
;;   ${MAILDIR}/gmail/.health

(require 'notmuch)

2.18.2. outgoing mail

(require 'smtpmail)

(setq user-mail-address "alice@gmail.com"
      user-full-name "Alice Exampleton")

(setq message-send-mail-function 'smtpmail-send-it)

(setq smtpmail-stream-type 'starttls
      smtpmail-smtp-server "smtp.gmail.com"
      smtpmail-smtp-service 587
      )

;; prevent send buffers accumulating
(setq message-kill-buffer-on-exit t)

Note: smtpmail also requires credentials in ~/.authinfo:

machine smtp.gmail.com login rconybeare@gmail.com port 587 password somegoogleapppassword

2.19. eye candy: theme selection

;; ----------------------------------------------------------------
;; theme selection

(load-theme 'misterioso t)

2.19.1. eye candy: default font

;; ----------------------------------------------------------------
;; default font

;; note:
;;   C-x C-+             increase buffer text size
;;   C-x C-=             something buffer text size
;;   C-x C--             decrease buffer text size
;;   C-x C-0             restore default buffer text size
;;   M-x describe-font   display font canonical name + stats in a new buffer
;;

;; to install inconsolata on ubuntu:
;;   $ sudo apt-get -y install fonts-inconsolata
;; for nix,  can use package
;;   nixpkgs.inconsolata-lgc

(add-to-list
 'default-frame-alist
 '(font . "-PfEd-Inconsolata-medium-normal-normal-*-14-*-*-*-m-0-iso10646-1"))

2.19.2. eye candy: initial window height

(defvar target-frame-width (+ 1 (* 2 135)))

(defun set-frame-size-from-screen-resolution ()
  "Infer suitable default frame size.
Reminder: no such thing as .Xresources under WSL."
  (interactive)
  (if (display-graphic-p)
      (progn
        (let* ((horizontal-room (/ (x-display-pixel-width)
                                   (frame-char-width)))
               (using-width (min target-frame-width
                                 horizontal-room)))
          (set-frame-width (selected-frame)
                           using-width)
          ;;(add-to-list 'default-frame-alist (cons 'width using-width))
          )
        ;; subtracting 200 pixels of vertical space
        (let* ((vertical-padding 100)
               (vertical-room (/ (- (x-display-pixel-height)
                                    vertical-padding)
                                 (frame-char-height))))
          (set-frame-height (selected-frame)
                            vertical-room)
          ;;(add-to-list 'default-frame-alist (cons 'height vertical-room))
          ))))

(set-frame-size-from-screen-resolution)

2.20. emacsclient

EDITOR=emacsclient to adopt running emacs session when unix wants something edited. For example when running git commit from shell.

;; + set EDITOR=emacsclient in shell

(server-start)

Author: Roland Conybeare

Created: 2024-09-08 Sun 18:01

Validate