Skip to content

jmccarrell/literate-emacs.d

Repository files navigation

Emacs Configuration

Introduction

About This File

After many years of emacs evolution via hunting and pecking, I finally refactored my existing emacs setup in the literate style. I continue to follow the lead of many fine emacs devotees, among them:

To all of these contributors, I doff my cap in salute. Your published work has inspired and – to be honest – at times terrified me as I have contemplated cutting and splicing together these various styles in search of my own.

I would also like to publicly acknowledge certain emacs contributors of the YouTube genre, among them:

I have drawn inspiration to adopt new ways of doing some very old tricks from these fine folks.

In this refactor, I have the following major goals:

  • create a more functional emacs leveraging recent innovation
  • KISS: smaller is better; only add where there is significant reason to
  • use the literate programming style
    • learn babel and tangle in the process
  • learn more emacs-lisp; even become competent?
  • add ivy, swiper and counsel in addition to – or perhaps replace? – helm
    • it turns out I have chosen to replace helm with ivy, avy, swiper and counsel.

as well as a host of smaller, clean-up type goals.

Which emacs on Mac OS X?

Since at least the summer of 2019, I have preferred GNU Emacs for Mac OS X installed by homebrew.

brew cask install emacs

This installs emacs and emacsclient, which provide my emacs foundation.

I always invoke emacs from the iterm shell, so it will inherit the environment variables that get setup in my login shells. In particular, the emacs launcher script from GNU Emacs for Mac OS X sets PATH well.

zsh Shell Support

I have been a zsh user for some time, but I also maintain a reasonable bash setup. In this config, I expect to add support for zsh idioms over time, especially as zsh is the default shell on MacOS Catalina.

Record Startup Timing

Record the elapsed time of starting up emacs.

My classic configuration took about 2.1 seconds to load.

(defconst emacs-start-time (current-time))

(unless noninteractive
  (message "Loading %s..." load-file-name))

General Settings

Emacs Directories

I prefer Howard’s style of defining where to store eveything, so I shamelessly stole it.

(defconst jwm/emacs-directory (concat (getenv "HOME") "/.emacs.d"))

(defun jwm/emacs-subdirectory (d) (expand-file-name d jwm/emacs-directory))

;; initialize some directories if needed
(let* ((subdirs '("elisp" "backups"))
       (fulldirs (mapcar (lambda (d) (jwm/emacs-subdirectory d)) subdirs)))
  (dolist (dir fulldirs)
    (when (not (file-exists-p dir))
      (message "Make directory: %s" dir)
      (make-directory dir))))

Custom Settings

Explicitly store and load my custom settings.

(setq custom-file (expand-file-name "settings.el" jwm/emacs-directory))
(when (file-exists-p custom-file)
  (load custom-file t))

Helpful Predicates

Some useful predicates in customization…

(defun jwm/mac-p ()
  (and (eq 'ns (window-system))
       (eq 'darwin system-type)))

(defun jwm/personal-mac-p ()
  (and (jwm/mac-p)
       (file-exists-p (concat (getenv "HOME") "/jdocs"))))

Modernizing Emacs

Another section I lifted straight from Howard. The description is his text.

With a long history of working on small machines without gigabytes of RAM, we might as well let Emacs be the beast it has always dreamed.

First, let’s increase the cache before starting garbage collection:

(setq gc-cons-threshold 50000000)

Found here how to remove the warnings from the GnuTLS library when using HTTPS… increase the minimum prime bits size:

(setq gnutls-min-prime-bits 4096)

Personal Information

(setq user-full-name "Jeff McCarrell"
      user-mail-address (cond
                         (t "jeff@mccarrell.org")))

Emacs Server and PATH.

Start emacs server on my main windowed emacs.

(when (window-system)
  (server-start))

I considered using exec-path-from-shell, but some experimentation shows that the only added benefit I get is to set MANPATH. At the moment, I don’t consider that enough of a win.

exec-path is reasonably set by my usual method of invoking emacs from iTerm, which also benefits from the emacs ruby launcher script from GNU Emacs for Mac OS X.

exec-path
;; =>
("/usr/local/bin" "/usr/bin" "/bin" "/usr/sbin" "/sbin" "/Users/jeff/.pyenv/shims" "/Users/jeff/bin"
 "/Applications/Emacs.app/Contents/MacOS/bin-x86_64-10_14"
 "/Applications/Emacs.app/Contents/MacOS/libexec-x86_64-10_14"
 "/Applications/Emacs.app/Contents/MacOS/libexec"
 "/Applications/Emacs.app/Contents/MacOS/bin")

Here is what I see from exec-path-from-shell

(exec-path-from-shell-initialize)
;; =>
(("MANPATH" . "/usr/share/man:/usr/local/share/man:/Library/TeX/Distributions/.DefaultTeX/Contents/Man:/usr/local/opt/coreutils/libexec/gnuman")
 ("PATH" . "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/jeff/.pyenv/shims:/Users/jeff/bin"))

which is not sufficiently better IMO.

Package Initialization

Package Manager

Circa 2023, I need to be able to use my emacs config in an offline world.

Roughly, my plan is to create a container of my environemnt, of course including my emacs. So I need a way to locally cache all of the packages I reference here. Luckily enough, there is elpa-mirror.

It looks like I can cache the packages using the elpa-mirror recipe. I choose to store these cached packages in $HOME/tmp/elpa-mirror.

To build the archives, follow the recipe in elpa-mirror.

One time, put a copy of the elpa-mirror code where we can use it:

pushd ~/thirdparty
git clone git@github.com:redguardtoo/elpa-mirror.git

Then build the local cache:

mkdir -p $HOME/tmp/elpa-mirror && \
    emacs --batch -l $HOME/.emacs.d/init.el \
          -l $HOME/thirdparty/elpa-mirror/elpa-mirror.el \
          --eval='(setq elpamr-default-output-directory "~/tmp/elpa-mirror")' \
          --eval='(elpamr-create-mirror-for-installed)'

Ensure the org repository and melpa are searched for packages in the online case. Use cached packages in the offline case.

(require 'package)

(let ((cached-package-dir (concat (getenv "HOME") "/tmp/elpa-mirror/")))

  (cond
   ((file-directory-p cached-package-dir)
    ;; elpa-mirror says to use this:
    ;; myelpa is the ONLY repository now, dont forget trailing slash in the directory
    (setq package-archives '(("elpa-mirror" . cached-package-dir))))
   (t
    (unless (assoc-default "org" package-archives)
      (add-to-list 'package-archives '("org" . "https://orgmode.org/elpa/") t))
    (unless (assoc-default "melpa" package-archives)
      (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)))))

(package-initialize)

Use M-x package-refresh-contents to reload the list of packages as needed.

Use-Package

Prefer use-package more or less as a more convenient way of customizing emacs. It does a whole lot more than that. My usage is fairly shallow.

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

(setq use-package-verbose t)
(setq use-package-always-ensure t)

(require 'use-package)

Howard’s Recommended Emacs Lisp Libs

Again following Howard here. Add in these supporting libraries to ease emacs lisp development. dash for a modern list api, s for string manipulation, and f for file manipulation.

I should note that seq is bundled with emacs and seems to be a replacement for dash.

(use-package dash
  :config (eval-after-load "dash" '(dash-enable-font-lock)))

(use-package s)

(use-package f)

Variables

Prefer Minimal Emacs

(setq inhibit-startup-message t)
;; needed for emacs23
(setq inhibit-splash-screen t)
(setq initial-scratch-message "")

;; Don't beep at me
(setq visible-bell t)

;; get rid of all of the backup files; that is what revision control is for.
(setq backup-before-writing nil)
(setq make-backup-files nil)

;; screen real estate is for text, not widgets
(when (window-system)
  (tool-bar-mode 0)
  (when (fboundp 'horizontal-scroll-bar-mode)
    (horizontal-scroll-bar-mode -1))
  (scroll-bar-mode -1))

Prefer utf8 Everywhere

Follow Grant’s lead here

;; prefer utf-8 encoding in all cases.
(let ((lang 'utf-8))
  (set-language-environment lang)
  (prefer-coding-system lang))

Tabs

I prefer spaces over tabs in all cases. Source. I guess I don’t write many Makefiles any more.

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

Tab for completion is wired deeply into my emacs-fingers.

(setq-default tab-always-indent 'complete)

Line Wrapping

In general, I prefer to see the entire line of text in the window even when it is wider than the frame. Thus I prefer Visual Line Mode. And, in general, I want emacs to wrap my text for me. I chose 108 as a relatively arbitrary line width value that works well for me. Hollerith cards are dead! Long live Hollerith cards.

;; Hollerith cards have had their day. Norming to 80 characters seems like a poor use of screen real estate
;; to me. I can't form a particular argument for 108, other than: it is larger than 72 and seems to fit
;; better.
(setq-default fill-column 108)
(auto-fill-mode)
(global-visual-line-mode)

Terminal and Shell (zsh) Settings

Reduce Startup Time for zsh

I find the battle to keep shell startup time crisp to be never ending. I have come to regard it in the same vein as spring cleaning or maybe dental visits for teeth cleaning: useful, but never my first choice.

As of this writing, my interactive zsh startup times are on the order of 1 second:

❯ time zsh -i -c exit
zsh -i -c exit  0.68s user 0.36s system 97% cpu 1.066 total

which is too long. Especially for counsel-grep. So configure the arguments given to zsh by emacs when attempting to run processes via shell-command and friends to ignore reading initialization files.

That this works is dependent on already having a reasonable environment configured – including and most especially PATH – so that we don’t need to re-execute all of that lovely zsh initialization code every time emacs wants to run a process.

-f is an alias for -conorcs

The following section on -conorcs is true, and valid. In addition, =-f= will suppress reading RC files:

-f
  NO_RCS

What is -conorcs?

As described in zsh invocation, zsh allows concatenation of single character command line options. We want both

  • -c
  • -o norcs

and as a single argument to reduce potential issues with whitespace evaluation at the emacs shell boundary. We can express that as -conorcs.

The intuition about this setting is reinforced by measurement:

M-x shell-command time zsh -onorcsc exit
zsh -onorcsc exit  0.00s user 0.00s system 69% cpu 0.004 total

and

M-x shell-command time zsh -cf exit
zsh -f -c exit  0.00s user 0.00s system 64% cpu 0.006 total

Which leads to:

(defun jwm/shell-is-zsh-p ()
  (string-suffix-p "zsh" shell-file-name))

(when (jwm/shell-is-zsh-p)
  (setq shell-command-switch "-cf"))

Effect

After this setting is in place, response time for emacs executed processes improve, for me by 3 orders of magnitude.

M-x shell-command time date
Sun Mar 29 10:33:10 PDT 2020
date  0.00s user 0.00s system 66% cpu 0.002 total

Your mileage will vary depending on the contents of your zsh dot files.

What about bash?

An alternative would be to switch to bash, which from my measurements and current config shows shorter startup time. This difference is no doubt entirely due to my zsh initialization scripts.

M-x shell-command time bash -c exit
bash -c exit  0.00s user 0.00s system 69% cpu 0.004 total

Other Misc Settings

Various settings I have come to prefer over the years.

;; always end a file with a newline
(setq require-final-newline t)

;; delete the region when typing, just like as we expect nowadays.
(delete-selection-mode t)

;; highlight the matching parenthesis
(show-paren-mode t)

;; Answering just 'y' or 'n' will do
(defalias 'yes-or-no-p 'y-or-n-p)

;; revert buffers automatically when underlying files are changed externally
(global-auto-revert-mode t)

;; no disabled functions
(setq disabled-command-function nil)

Display Settings

Color Theme

After much experimentation, I have come to prefer zenburn. Over the years, I have used my own color theme, which I used to be quite proud of, and then solarized. Now I have come to prefer zenburn. It works well for emacs running in the terminal as well.

For reference, here is how Sacha overrides and customizes her use of solarized.

(use-package zenburn-theme
  :init (load-theme 'zenburn t))

Themes I have experimented with

First of all, peach melba is a convenient way to preview themes to try out.

Mostly to keep myself from repeating work, here is a partial list of themes I have tried and found inferior to zenburn, mostly due to their handling of org-mode.

Font

I prefer a little bigger (14 point) font on my personal laptop, especially on my large monitor at home.

Here is how Xah Lee sets his frame font

And a 2019 blog post comparing fonts that led me to the font Hack. Install Hack via homebrew:

brew cask install caskroom/fonts/font-hack

and use it for all frames:

(defun jwm/font-exists-p (f)
  (and (window-system)
       (member f (font-family-list))))

(when (window-system)
  (let ((preferred-font
         (cond
          ((and (jwm/font-exists-p "Hack") (jwm/mac-p)) "Hack-14")
          (t "Monaco-12"))))
    (message "setting Jeff preferred font %s" preferred-font)
    (set-frame-font preferred-font t t)))

Adjust font size to display resolution

With the broad range of pixels per inch offered by laptops, 4k displays, etc. a mechanism to adjust the font size based on screen resolution is helpful. WJCFerguson seems to have provided just such a capability via textsize

The customizations help me with my specific font size choices;

My macbook with retina display at max resolution reports 344 mm wide by 2056 pixels for a pixel density of 0.17. My preference is for the 15 point Hack font. My 27inch Dell 4k monitor reports 598 mm wide by 3840 pixel width == pixel density of 0.16. I seem to prefer Hack-20 on the 27inch Dell. My 32inch Dell 4k monitor reports 701 mm wide by 3840 pixel width for a pixel density of 0.18. I prefer Hack-17 there.

(use-package textsize
  :init (textsize-mode)
  :config
  (customize-set-variable 'textsize-monitor-size-thresholds
                          '((0 . 0) (400 . 5) (650 . 2)))
  (customize-set-variable 'textsize-pixel-pitch-thresholds
                          '((0 . 0))))

To help understand and manage the choices made by textsize, here is a helper function that dumps to the message buffer some relevant info. Invoke this from the frame of interest, ie, I have 3 physical monitors in my home setup; running this on a frame on each monitor gives the results for that single monitor.

(defun jwm/dump-frame-textsize-metrics ()
  "Dump selected frame metrics from the currently selected frame to the *Message* buffer.
Intended to be helpful for debugging the choices textsize makes for a given monitor/display."
  (interactive)
  (let (f (selected-frame))
    (message "emacs frame geometry: X Y WIDTH HEIGHT: %s" (frame-monitor-attribute 'geometry f))
    (message "emacs monitor size WIDTH HEIGHT mm: %s" (frame-monitor-attribute 'mm-size f))
    (message "textsize monitor size  mm: %d" (textsize--monitor-size-mm f))
    (message "textsize monitor size pix: %d" (textsize--monitor-size-px f))
    (message "pixel pitch %.02f" (textsize--pixel-pitch f))
    (message "textsize default points %d" textsize-default-points)
    (message "textsize frame offset %d"
             (or (frame-parameter f 'textsize-manual-adjustment) 0))
    (message "pixel pitch adjustment %d"
             (textsize--threshold-offset textsize-pixel-pitch-thresholds
                                         (textsize--pixel-pitch f)))
    (message "monitor size adjustment %d"
             (textsize--threshold-offset textsize-monitor-size-thresholds
                                         (textsize--monitor-size-mm f)))
    (message "text size chosen: %d" (textsize--point-size f))
    (message "default-font: WIDTHxHEIGHT %dx%d" (default-font-width)(default-font-height))
    (message "resultant text area in chars WIDTHxHEIGHT %dx%d"
             (frame-width f)(frame-height f))
    (message "default face font %s" (face-attribute 'default :font))
    )
  nil)

To make a session-permanent adjustment on a per-frame basis, invoke this function

(defun jwm/adjust-textsize-frame-offset (arg)
  "Supply a per-frame, persistent integer offset (positive or negative) to the textsize font size calculation.
In effect, adjusts the pixel size of the frame font up or down by the offset value."
  (interactive "P")
  (let ((f (selected-frame))
        (offset arg))
    (textsize-modify-manual-adjust f offset)))

Diminish

Manage how minor modes affect the mode line.

(use-package diminish
  :init (diminish 'visual-line-mode))

Whitespace Mode

This is another copy and paste from Howard. It makes it easier to see whitespace when necessary.

(use-package whitespace
  :bind ("C-c T w" . whitespace-mode)
  :init
  (setq whitespace-line-column nil
        whitespace-display-mappings '((space-mark 32 [183] [46])
                                      (newline-mark 10 [9166 10])
                                      (tab-mark 9 [9654 9] [92 9])))
  :config
  (set-face-attribute 'whitespace-space       nil :foreground "#666666" :background nil)
  (set-face-attribute 'whitespace-newline     nil :foreground "#666666" :background nil)
  (set-face-attribute 'whitespace-indentation nil :foreground "#666666" :background nil)
  :diminish whitespace-mode)

Selection

expand-region

I have grown to prefer expand-region. This is directly modified/cribbed from Howard’s config.

My usage has not yet grown to benefit from Howard’s additions; maybe some day.

(use-package expand-region
  :ensure t
  :config
  (defun ha/expand-region (lines)
    "Prefix-oriented wrapper around Magnar's `er/expand-region'.

     Call with LINES equal to 1 (given no prefix), it expands the
     region as normal.  When LINES given a positive number, selects
     the current line and number of lines specified.  When LINES is a
     negative number, selects the current line and the previous lines
     specified.  Select the current line if the LINES prefix is zero."
    (interactive "p")
    (cond ((= lines 1)   (er/expand-region 1))
          ((< lines 0)   (ha/expand-previous-line-as-region lines))
          (t             (ha/expand-next-line-as-region (1+ lines)))))

  (defun ha/expand-next-line-as-region (lines)
    (message "lines = %d" lines)
    (beginning-of-line)
    (set-mark (point))
    (end-of-line lines))

  (defun ha/expand-previous-line-as-region (lines)
    (end-of-line)
    (set-mark (point))
    (beginning-of-line (1+ lines)))

  ;; jwm: however, I can't seem to get C-= from my mac keyboard.
  ;;   so prefer C-@
  :bind ("C-@" . ha/expand-region))

Window Movement

I prefer ace-window

(use-package ace-window
  :bind (("M-o" . ace-window))
  :config
  (setq aw-dispatch-always t)
  (setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)))

I did experiment with eyebrowse. However, its default binding C-c C-w conflicts with org-refile, so it had to go.

Key Bindings

Option and Command Modifier Keys on a Mac

Howard maps the option and command keys on mac hardware to different emacs key symbols to allow him that many more possible key bindings, like this:

(setq mac-option-modifier 'meta)
(setq mac-command-modifier 'super)

And I did much the same thing. However, I think I prefer to keep meta closest to the space bar.

I leave right-option for the OS X combiner keys, like √ from right-option v.

And I prefer to switch between emacs frames using the standard mac gesture Command-`, although since adding ace-window, I suppose I need Command-` less than before it.

(when (jwm/mac-p)
  (setq mac-command-modifier 'meta)
  (setq mac-option-modifier 'super)
  (setq mac-right-option-modifier 'none)

  ;; mirror the mac user gesture for switching emacs frames
  ;;  this supports my habit of using two emacs frames side by side.
  (bind-key "M-`" 'other-frame)

  ;; prevent my thumb from triggering this menu on the trackpad when in open laptop mode
  ;;  ie, when I am working on the train
  (bind-key [C-down-mouse-1] 'ignore))

Global Key Bindings

I really like Howards global key binding approach, which leverages John Wiegley’s bind-key that is part of use-package.

Recall that there are several power features of bind-key, such as rebinding, adding keys to a specific map etc. Here are John’s pointers in the comments of the package.

(bind-keys
 ;; long time bindings I have preferred
 ("C-c u" . revert-buffer)
 ("C-M-g" . goto-line)

 ;; perhaps turn these on when/if I bring in Howards font size functions
 ;; ("s-C-+" . ha/text-scale-frame-increase)
 ;; ("A-C-+" . ha/text-scale-frame-increase)
 ;; ("s-C-=" . ha/text-scale-frame-increase)
 ;; ("A-C-=" . ha/text-scale-frame-increase)
 ;; ("s-C--" . ha/text-scale-frame-decrease)
 ;; ("A-C--" . ha/text-scale-frame-decrease)
 )

Block wrappers

Again, I am cribbing this pretty much straight from Howards’ config

Insert pairs

While the M-( binding to insert-pair is great, I often need to wrap with other characters:

But not M-` for me; I prefer that to switch frames, as in the os x convention.

And also, not M-< either; I prefer to use that for beginning/end-of-buffer. I wonder what keybinding Howard uses for that?

(global-set-key (kbd "M-[") 'insert-pair)
(global-set-key (kbd "M-{") 'insert-pair)
;; (global-set-key (kbd "M-<") 'insert-pair)
(global-set-key (kbd "M-'") 'insert-pair)
;; (global-set-key (kbd "M-`") 'insert-pair)
(global-set-key (kbd "M-\"") 'insert-pair)

wrap-region

Quoting Howard:

But wrap-region is even more flexible. In most editors, selecting text and typing anything replaces the selected text (see the delete-selection-mode), but in this case, we can do something different… like wrapping:

(use-package wrap-region
  :ensure   t
  :config
  (wrap-region-global-mode t)
  (wrap-region-add-wrappers
   '(("(" ")")
     ("[" "]")
     ("{" "}")
     ("<" ">")
     ("'" "'")
     ("\"" "\"")
     ("" ""   "q")
     ("" ""   "Q")
     ("*" "*"   "b"   org-mode)                 ; bolden
     ("*" "*"   "*"   org-mode)                 ; bolden
     ("/" "/"   "i"   org-mode)                 ; italics
     ("/" "/"   "/"   org-mode)                 ; italics
     ("~" "~"   "c"   org-mode)                 ; code
     ("~" "~"   "~"   org-mode)                 ; code
     ("=" "="   "v"   org-mode)                 ; verbatim
     ("=" "="   "="   org-mode)                 ; verbatim
     ("_" "_"   "u" '(org-mode markdown-mode))  ; underline
     ("**" "**" "b"   markdown-mode)            ; bolden
     ("*" "*"   "i"   markdown-mode)            ; italics
     ("`" "`"   "c" '(markdown-mode ruby-mode)) ; code
     ("`" "'"   "c"   lisp-mode)                ; code
     ))
  :diminish wrap-region-mode)

which-key

I have come to appreciate the exploration of the key maps that which-key enables.

Howard extensively customizes the display of which-key. I find that interesting, and maybe something to pursue one day.

(use-package which-key
  :config
  :diminish which-key-mode
  :config

  ;; prefer to show the entire command name with no truncation.
  ;;  some of those projectile command names exceed the default value of 27, eg
  ;;  projectile-toggle-between-implementation-and-test
  (setq which-key-max-description-length nil)
  (which-key-mode 1))

Hooks

Whitespace Cleanup Hook

I want to run whitespace-cleanup on save-buffer scoped to specific scopes I manage. In general, I don’t want to run it in shared spaces – especially code spaces – where rewriting some existing whitespace convention might cause unintended changes.

But, after living with this functionality for a while, I’m not sure I prefer it, so disable it.

(defvar jwm/run-whitespace-cleanup-on-save-p nil
  "run whitespace-cleanup on buffer-save. set to t where desired in file or directory local scopes.")

(defun jwm/save-buffer-whitespace-cleanup-hook ()
  "run whitespace-cleanup when enabled by jwm/run-whitespace-cleanup-on-save-p."
  (when jwm/run-whitespace-cleanup-on-save-p
    (whitespace-cleanup)))

(add-hook 'before-save-hook 'jwm/save-buffer-whitespace-cleanup-hook)

Dired

Start simple. The main keystrokes I want to train my fingers to execute are:

keyscommandwhat it does
C-x ddiredstart dired
C-x C-jdired-jumpJump to Dired buffer corresponding to current buffer.
C-x 4 C-jdired-jump-other-window

This config is copied from bbatsov

But apparently dired is a non-package package – whatever that means. So to correct this startup error:

Error (use-package): Failed to install dired: Package ‘dired-’ is unavailable

I have added :ensure nil, following this advice

(use-package dired
  :ensure nil
  :config
  ;; dired - reuse current buffer by pressing 'a'
  ;; (put 'dired-find-alternate-file 'disabled nil)

  ;; always delete and copy recursively
  ;; (setq dired-recursive-deletes 'always)
  ;; (setq dired-recursive-copies 'always)

  ;; if there is a dired buffer displayed in the next window, use its
  ;; current subdir, instead of the current subdir of this dired buffer
  (setq dired-dwim-target t)

  ;; enable some really cool extensions like C-x C-j (dired-jump)
  (require 'dired-x))

Search

ag

;; ag config derived from danielmai's config
(use-package ag
  :commands ag)

ripgrep

I get ripgrep as a dependency of projectile-ripgrep. Since I am using (apparently) the bone-stock configuration, there is nothing in this section.

Ivy, Avy, Swiper, Projectile

Projectile Config

I choose to pattern my config for these related packages after abo-abo, the author. Well, it turns out abo-abo has a pretty baroque way of loading his configuration.

So combine the approach used by bbatsov with that used by Daneil’s projectile config.

(use-package projectile
  :diminish projectile-mode
  :commands (projectile-mode projectile-switch-project)
  :bind (("C-c p p" . projectile-switch-project)
         ("C-c p s s" . projectile-ag)
         ("C-c p s r" . projectile-ripgrep))
  :init
  (setq projectile-completion-system 'ivy)
  :config
  (define-key projectile-mode-map (kbd "s-p") 'projectile-command-map)
  (define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)
  (setq projectile-keymap-prefix (kbd "C-c p"))
  (projectile-global-mode t))

Counsel Projectile

(use-package counsel-projectile
  :config
  (counsel-projectile-mode))

Ivy, Counsel, Avy and Swiper Config

(use-package ivy
  :diminish (ivy-mode . "")
  :config
  (ivy-mode 1)
  ;; add ‘recentf-mode’ and bookmarks to ‘ivy-switch-buffer’.
  (setq ivy-use-virtual-buffers t)
  ;; show both the index and count of matching items
  (setq ivy-count-format "%d/%d "))

(use-package counsel
  :bind (("C-c j" . counsel-git-grep)
         ("C-c k" . counsel-rg)
         ("C-c K" . counsel-ag)))

(use-package swiper
  :config
  (global-set-key "\C-s" 'swiper))

this is stolen from jwiegley’s config

(use-package avy
  :bind* ("C-." . avy-goto-char-timer)
  :config
  (avy-setup-default))

Git

I would like to consider git-gutter-fringe someday.

Magit

Indispensible. One of the two killer apps for emacs IMO, org-mode being the other.

Howard does additional customization that I should consider someday.

My usage is considerably simpler.

(use-package magit
  :defer t
  :bind ("C-x g" . magit-status))

Org

Org Configuration

Org Global Key Bindings

(bind-keys
 ;; org mode wants these default global bindings set up.
 ("C-c l" . org-store-link)
 ("C-c c" . org-capture)
 ("C-c a" . org-agenda))

Circa spring 2020, C-c b gives me:

command-execute: Wrong type argument: commandp, org-iswitchb

so I have removed that key-binding.

Where to Look for Org Info

(setq org-directory
      (cond (t "~/jwm/todo")))

;; The default place to put notes for capture mode
(setq org-default-notes-file
      (concat org-directory
              (cond (t "/todo.org"))))

;; where I store periodic reminders, ie, ticklers in GTD-talk
(defvar jwm/org-tickler-file (concat org-directory "/tickler.org"))

Agenda Files

Recall that if org-agenda-files is a single file name, then that symbol names a file which is read for the list of agenda files to manage.

Further, the functions org-agenda-file-to-front and org-remove-file can be used to manage that list.

This is the mechanism I want to utilize here.

(setq org-agenda-files (jwm/emacs-subdirectory "org-agenda-files-list"))

Templates, Tasks, Refiling

I lifted the tickler capture entry from Nicolas Petton.

;; capture template.
(setq org-capture-templates
      '(("t" "Todo" entry (file+headline org-default-notes-file "Tasks")
         "* TODO %?\n %i\n  %a\n")
        ("T" "Tickler" entry (file+headline jwm/org-tickler-file "Tickler")
         "* %i%?\n %U\n")
        ("j" "Journal" entry (file+datetree "~/pdata/journal.org")
         "* %?\nEntered on %U\n  %i\n  %a\n")))
;; Jeff task states
(setq org-todo-keywords
      '((sequence
         "TODO(t)"
         "NEXT(n!)"
         "DOING(g!)"
         "WAITING(w@/!)"
         "|" "DONE(d!)"
         "CANCELLED(c@)"
         "DEFERRED(D@)")))
;; I prefer 2 levels of headlines for org refile targets
;;  this matches well with my TASKS/PROJECTS high level
;; Allow refiling into any of my current projects,
;;  as named by org-agenda-files.
(setq org-refile-targets '((org-agenda-files . (:maxlevel . 2))))

Save Org Files Periodically

Stolen from John Weigley.

(defun save-org-mode-files ()
  (dolist (buf (buffer-list))
    (with-current-buffer buf
      (when (eq major-mode 'org-mode)
        (if (and (buffer-modified-p) (buffer-file-name))
            (save-buffer))))))

(run-with-idle-timer 25 t 'save-org-mode-files)

Org babel

I stole much of this from Daniel Mai.

(use-package ob-restclient)

(org-babel-do-load-languages
 'org-babel-load-languages
 '((python . t)
   (C . t)
   (calc . t)
   (java . t)
   (ruby . t)
   (lisp . t)
   (scheme . t)
   (shell . t)
   (sql . t)
   (sqlite . t)
   (js . t)
   (restclient . t)))

(defun my-org-confirm-babel-evaluate (lang body)
  "Do not confirm evaluation for these languages."
  (not (or (string= lang "C")
           (string= lang "java")
           (string= lang "python")
           (string= lang "emacs-lisp")
           (string= lang "sql")
           (string= lang "sqlite"))))
(setq org-confirm-babel-evaluate 'my-org-confirm-babel-evaluate)

Programming Support

yasnippets

(use-package yasnippet
  :config
  (use-package yasnippet-snippets)
  (yas-reload-all)
  (add-hook 'prog-mode-hook #'yas-minor-mode)
  (add-hook 'org-mode-hook #'yas-minor-mode))
(use-package auto-yasnippet
  :after yasnippet
  :bind (("s-w" . aya-create)
         ("s-y" . aya-expand)))

WIP C Style

According to cc-styles.el, the function c-add-style is the preferred way to define C style.

Use the function c-add-style to add new styles or modify existing styles (it is not a good idea to modify existing styles – you should create a new style that inherits the existing style).

Here is the GNU manual on adding styles.

Clearly, more to do here.

crux

I use bbatsov’s crux at times, especially C-c n.

(use-package crux
  :bind
  (
   ("C-c n" . crux-cleanup-buffer-or-region)
   ;; ("C-S-RET" . crux-smart-open-line-above)
   ;; ("M-o" . crux-smart-open-line)
   ("C-c d" . crux-duplicate-current-line-or-region)
   ("C-c M-d" . crux-duplicate-and-comment-current-line-or-region)
   ("C-c C-r" . crux-rename-file-and-buffer)))

js2 and json mode

I lifted this straight from Howard’s config. Not that I write much javascript.

(use-package js2-mode
  :init
  (setq-default js-indent-level 2
                js2-global-externs (list "window" "module" "require" "buster" "sinon" "assert" "refute" "setTimeout" "clearTimeout" "setInterval" "clearInterval" "location" "__dirname" "console" "JSON" "jQuery" "$"))
  (add-to-list 'auto-mode-alist '("\\.js$" . js2-mode))
  (add-to-list 'auto-mode-alist '("\\.es6$" . js2-mode)))

I spend more time dealing with various blobs of json, so follow the lead of spacemacs for json-mode.

(use-package json-mode)

python

Lets try elpy stock and see what happens. So far, so good.

(use-package elpy
  :ensure t
  :init
  (elpy-enable))

Integration with the Outside World

docker and dockerfile

Surveying my field of emacs devotees for their docker integrations, it seems to me that these two are worthy of study:

In this case, I am going to follow John Wiegley’s approach here.

Circa Sep 2023, I dropped packages docker-compose and docker-tramp as I don’t use them.

(use-package docker
  :bind ("C-c d" . docker)
  :diminish)

(use-package dockerfile-mode
  :mode "Dockerfile[a-zA-Z.-]*\\'")

Report Startup Timing

from John Weigley.

;;; Post initialization

(let ((elapsed (float-time (time-subtract (current-time)
                                          emacs-start-time))))
  (message "Loading %s...done (%.3fs)" load-file-name elapsed))

(add-hook 'after-init-hook
          `(lambda ()
             (let ((elapsed (float-time (time-subtract (current-time)
                                                       emacs-start-time))))
               (message "Loading %s...done (%.3fs) [after-init]"
                        ,load-file-name elapsed)))
          t)

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published