Skip to content

Latest commit

 

History

History
2365 lines (2016 loc) · 72.5 KB

emacs-init.org

File metadata and controls

2365 lines (2016 loc) · 72.5 KB

Tom’s Emacs Init File

So this is fun. I can now use literate programming to configure my OS..err text editor.

Pleas note that a HUGE chunk of this file was shamelessly stolen from the snippets in https://writequit.org/eos. I am standing on the shoulders of giants.

Bootstrap

Let’s get started by adding my custom folders to my load path:

Debugging

(setq debug-on-error nil)

Package Management

Local Packages

Here’s where I install local packages:

(let ((default-directory  "~/.emacs.d/customizations/packages/"))
  (normal-top-level-add-subdirs-to-load-path))

(add-to-list 'load-path "~/.emacs.d/customizations/packages/")

To load these local packages, I add a code block to this document that looks something like this:

(if (not (require 'some-addon nil t))
    (message "Package `some-addon' not found")
  (some-additional-config t))

I even created the require-local-package.yasnippet snippet to help.

The nice thing about the code snippet above is that if the manually-installed package isn’t installed nothing will error out. A message is written to the Messages buffer just in case I want so see what wasn’t loaded.

However, for whatever reason, this doesn’t work if the parent folder name for the add-on ends in .el. For example, I have a package I like to use called sx.el. It simply won’t load using the code above.

So what do I need to do then? Well, this works just wonderfully:

(use-package some-addon ;; or whatever the main el package name is
  :load-path "customizations/packages/some-addon.el")

Please note - I’m not pointing at a file called some-addon.el. That’s the folder name.

Exwm Bootstrap

These values have to be set before starting exwm or calling package-initialize:

(setq mouse-autoselect-window t
      focus-follows-mouse t)

Proper Package Managers

I guess I’ll add the melpa stuff. All of the cool kids seem to be using the non-stable version of melpa, but I had a lot of issues with that, so I’m just using boring-old stable + org.

 (require 'package) ;; You might already have this line
 (add-to-list 'package-archives
		'("melpa" . "http://melpa.org/packages/")
		'("org" . "https://orgmode.org/elpa/"))
 (package-initialize) ;; You might already have this line

And finally, use-package:

;; This is only needed once, near the top of the file
(eval-when-compile
  (require 'use-package))

Quelpa

Quelpa is a package manager that you can use to install packages directly from Git. I try not to do this because it usually ends in tears sooner or later, but some cool packages (like Burly) require it.

First let’s install it:

(unless (package-installed-p 'quelpa)
  (with-temp-buffer
    (url-insert-file-contents "https://raw.githubusercontent.com/quelpa/quelpa/master/quelpa.el")
    (eval-buffer)
    (quelpa-self-upgrade)))

…and let’s integrate it with use-package:

(quelpa
 '(quelpa-use-package
   :fetcher git
   :url "https://github.com/quelpa/quelpa-use-package.git"))
(require 'quelpa-use-package)

Global Properties

Open everything in the preferred browser:

(cond
 ((string-equal system-type "windows-nt") ; Microsoft Windows
  (progn
    (setq browse-url-browser-function 'browse-url-generic
          browse-url-generic-program "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe")))
 ((string-equal system-type "gnu/linux") ; linux
  (progn
    (setq browse-url-browser-function 'browse-url-generic
          browse-url-generic-program "/usr/bin/chromium")))
 )

Now on to Org

System Path

Since I usually don’t start Emacs from the command line I need to append some of my custom apps to the Emacs path.

You need t update two variables: exec-path and PATH. Let’s first update exec-path:

(cond
 ((string-equal system-type "windows-nt")
  (progn
    (setq chocolatey-root "c:/ProgramData/chocolatey")
    (setq chocolatey-lib
          (concat chocolatey-root "/" "lib"))
    (setq chocolatey-bin
          (concat chocolatey-root "/" "bin"))
    (setq exec-path (append '("C:/Python27"
                              "c:/ProgramData/chocolatey/bin"
                              "C:/Users/tom.purl/AppData/Local/Programs/Python/Python36/Scripts")
                            exec-path))))
 ((string-equal system-type "gnu/linux")
  (progn
    (setq exec-path (append '("/home/tom/bin"
                              "/home/tom/local/bin"
                              "/run/current-system/sw/bin"
                              "/home/tom/scripts")
                            exec-path)))))

…and now PATH:

(setenv "PATH" (concat (getenv "PATH")))

Character Encoding

Defaults

UTF-8 is the only thing that makes sense for me.

; Shamelessly stolen from https://writequit.org/eos/eos-core.html
(set-charset-priority 'unicode)
(set-default-coding-systems 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-selection-coding-system 'utf-8)
(prefer-coding-system 'utf-8)
(cond
 ((string-equal system-type "windows-nt")
  (progn
    (setq default-process-coding-system '(utf-8-dos . utf-8-dos))))
 ((string-equal system-type "gnu/linux")
  (progn
    (setq default-process-coding-system '(utf-8-unix . utf-8-unix)))))

File Conversion

Sometimes it’s just easier to fix the carriage returns. Stolen from https://www.emacswiki.org/emacs/DosToUnix:

(defun tp/encoding/dos2unix ()
  "Not exactly but it's easier to remember"
  (interactive)
  (set-buffer-file-coding-system 'utf-8-unix 't))

Startup Stuff

I don’t want to see the startup screen. Just dump me into a scratch buffer.

(setq inhibit-startup-message t)

Exwm

Basic Setup

;; (use-package exwm
;;   :ensure t
;;   :config
;;   (progn
;;     (require 'exwm-config)
;;     (exwm-config-default)))

Systray

(require 'exwm-systemtray)
(exwm-systemtray-enable)

Randr Stuff (Multi-Monitor)

(require 'exwm-randr)
(add-hook 'exwm-randr-screen-change-hook
          (lambda ()
            (start-process-shell-command
             "xrandr" nil "xrandr --output HDMI-2 --auto --primary --output eDP-1 --auto --left-of HDMI-2 --mode")))
(setq exwm-randr-workspace-output-plist '(0 "HDMI-2" 1 "HDMI-2" 2 "eDP-1" 3 "eDP-1"))
(exwm-randr-enable)

Other config

(setq exwm-worspace-number 4)

Shortcuts

Display list of favorite apps to start in Helm

I’m lazy and don’t particularly want to press S-& every time I want to launch an application, especially if it’s an app I start 5 times a day, so why not create a Helm source to make things easier?

(setq tp/favorite-shell-programs
      '(("Firefox" . "firefox")
        ("Slack" . "slack")
        ("Terminal" . "konsole")
        ("KeePassXC" . "keepassxc")))

(setq tp/helm/favorite-shell-programs-sources
      `((name . "Launch commonly used programs")
        (candidates . ,(mapcar 'car tp/favorite-shell-programs))
        (action .
                (lambda (candidate)
                  (let
                      ((exe-name (cdr (assoc candidate tp/favorite-shell-programs))))
                    (start-process-shell-command exe-name nil exe-name))))))

EXWM Keymap

Let’s create a custom keymap for all of my new EXWM shortcuts.

  • “l” maps to “*l*aunch program”
  • ”s” maps to “list all *s*hell buffers”
    • For whatever reason this only works if you’re already visiting a shell buffer.
(define-prefix-command 'tp/exwm/key-map)
(define-key tp/exwm/key-map (kbd "r")
  (lambda()
    (interactive)
    (helm :sources '(tp/helm/favorite-shell-programs-sources))))
(define-key tp/exwm/key-map (kbd "s") 'helm-shell-prompts-all)
(global-set-key (kbd "\C-ce") tp/exwm/key-map)

Last Line :-)

;; (exwm-enable)

Eye Candy

Themes

Poet Theme

;; ;; This has to be installed manually
;; (if (not (require 'poet-theme nil t))
;;     (message "Package `poet-theme' not found")
;;   (load-theme 'poet 1))

Dracula

;; (use-package dracula-theme
;;   :ensure t)

Gotham

This is a nice dark theme but it doesn’t handle org-mode tables well.

;; (use-package gotham-theme
;;   :ensure t)
;; (load-theme 'gotham t)

Gruvbox

Man this is a great-looking, modular theme but like so many other awesome fonts it doesn’t support variable-pitch fonts.

(use-package gruvbox-theme
  :ensure t
  :config
  ;; (load-theme 'gruvbox-light-medium t)
  )

Soothe

This is a little too dark and funky for my tastes.

;; (if (not (require 'soothe-theme nil t))
;;     (message "Package `soothe-theme' not found")
;;   (load-theme 'soothe 1))

Solarized

(use-package solarized-theme
  :ensure t
  :config
  ;; make the fringe stand out from the background
  (setq solarized-distinct-fringe-background t)

  ;; Don't change the font for some headings and titles
  (setq solarized-use-variable-pitch t)

  ;; make the modeline high contrast
  (setq solarized-high-contrast-mode-line t)

  ;; Use less bolding
  (setq solarized-use-less-bold t)

  ;; Use more italics
  (setq solarized-use-more-italic t)

  ;; Use less colors for indicators such as git:gutter, flycheck and similar
  (setq solarized-emphasize-indicators nil)

  ;; Don't change size of org-mode headlines (but keep other size-changes)
  (setq solarized-scale-org-headlines nil)

  ;; Avoid all font-size changes
  (setq solarized-height-minus-1 1.0)
  (setq solarized-height-plus-1 1.0)
  (setq solarized-height-plus-2 1.0)
  (setq solarized-height-plus-3 1.0)
  (setq solarized-height-plus-4 1.0))
(load-theme 'solarized-light t)

Cloud

;; (use-package cloud-theme
;;   :ensure t)

Modus-Operandi

A lot of people seem to also like this light theme:

;; (use-package modus-operandi-theme
;;   :ensure t)
;; (load-theme 'modus-operandi t)

Acme

;; (use-package acme-theme
;;   :ensure t)
;; (load-theme 'acme t)

Centering Prose

Olivetti

This is a sweet minor mode that makes prose pages look much nicer.

(use-package olivetti
  :ensure t
  :config
  (add-hook 'org-mode-hook
            (lambda ()
              (olivetti-mode 1)
              (setq olivetti-body-width 84))))

Since the screen width for prose is now 100 I’m going to bump up paragraph width too:

(setq-default fill-column 85)

Since Olivetti breaks up long lines C-k (which maps to kill-line) doesn’t actually kill the entire line. Thanks to Xah yet again I have a solution:

(global-set-key (kbd "M-9") 'kill-whole-line)

Font

Windows-Specific Stuff

Big fonts + Windows makes Emacs something somthing (slow down terribly).

(cond
 ((string-equal system-type "windows-nt")
  (progn
    (setq inhibit-compacting-font-caches 1))))

Font Choices

(set-face-attribute 'default nil :family "InputMonoCondensed" :height 130)
(set-face-attribute 'fixed-pitch nil :family "InputMonoCondensed" :height 130)
(cond
 ((string-equal system-type "windows-nt")
  (progn
    (set-face-attribute 'variable-pitch nil :family "InputSansCondensed" :height 130)))
 ((string-equal system-type "gnu/linux")
  (progn
    (set-face-attribute 'variable-pitch nil :family "InputSansCondensedLight" :height 130))))

Viewing monospaced and non-monospaced fonts in the same document

Emacs has a great feature that allows you to view non-code text using a proportional font (like Helvetica) and code text using a non-proportional font (like Courier). You just have to run this below:

(add-hook 'text-mode-hook
          (lambda ()
            (variable-pitch-mode 1)))

For whatever reason I can never the name of the variable-pitch-mode function so here’s my own alias:

(defun tp/font/toggle-variable-pitch-mode ()
  (interactive)
  (variable-pitch-mode nil))

Emojify mode

Let’s make it globally accessible.

Actually, let’s turn it off for a little bit. I think it’s having way too big of an impact on performance.

;; (use-package emojify
;;   :ensure t
;;   :init
;;   (add-hook 'after-init-hook #'global-emojify-mode))
(use-package emojify
  :ensure t
  :mode ("\\.org\\'" . org-mode))

Status Line

Powerline

My pendulum has swung in the direction of using a fancier modeline, so I’m going to try telephone-line instead.

;; (use-package powerline
;;   :ensure t
;;   :config
;;   (powerline-default-theme))

Telephoneline

(use-package telephone-line
  :ensure t
  :config
  (telephone-line-mode 1))

Displaying The Time

And why not?

(setq display-time-format "%I:%M")
(display-time-mode)

Hide DOS EOL Char’s (^M)

This is thanks to https://stackoverflow.com/a/750933/1380901

(defun tp/file/remove-dos-eol ()
  "Do not show ^M in files containing mixed UNIX and DOS line endings."
  (interactive)
  (setq buffer-display-table (make-display-table))
  (aset buffer-display-table ?\^M []))

Toolbars And Menubars And Such

All of this is shamelessly stolen from https://writequit.org/eos/eos-core.html:

(when (functionp 'menu-bar-mode)
  (menu-bar-mode -1))
(when (functionp 'set-scroll-bar-mode)
  (set-scroll-bar-mode 'nil))
(when (functionp 'mouse-wheel-mode)
  (mouse-wheel-mode -1))
(when (functionp 'tooltip-mode)
  (tooltip-mode -1))
(when (functionp 'tool-bar-mode)
  (tool-bar-mode -1))
(when (functionp 'blink-cursor-mode)
  (blink-cursor-mode -1))

Line Numbers

Of course you need this!

Oh wait, according the Xah this really slows things down. I’m going to turn it off for now and see if that helps:

;; (global-linum-mode t)

Dumb Stuff

Nyan Cat Stuff

(use-package zone-nyan
  :defer t
  :ensure t)
;; (use-package nyan-mode
;;   :ensure t
;;   :init
;;   (add-hook 'after-init-hook #'nyan-mode)
;;   :config
;;   (nyan-start-animation))

Presentation Helpers

presentation mode

This is a cool way to control font size and such for sharing:

(use-package presentation
  :ensure t)

org-re-reveal

This plugin has changed my mother-flippin’ life.

./images/mother-flippin-mug.jpg

(use-package org-re-reveal
  :ensure t
  :config
  (setq org-re-reveal-root "../reveal.js")
  (setq org-re-reveal-title-slide "<h1>%t</h1><footer><h5>&copy;%a</h5></footer>"))

Make sure that your Org file has that directory beneath it.

Finding the Cursor More Easily

Beacon to the rescue!

(use-package beacon
  :ensure t
  :defer 120
  :config
  (beacon-mode 1))

YAS

Bootstrap

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

Install Snippets

(use-package yasnippet-snippets
  :ensure t)

ORG!!!!!!!!!!!!!!!

Global Varables

(cond
 ((string-equal system-type "windows-nt")
  (progn
    (setq org-directory "~/Roaming-Home/org/")))
 ((string-equal system-type "gnu/linux")
  (progn
    (setq org-directory "~/gtd/org/"))))

(setq org-log-done 'time)

Here are global properties that are available to each file. For more information on the “*_ALL” properties check this out:

 ;; Effort and global properties
 (setq org-global-properties
	'(
	  ("POM_Estimate_ALL". "n/a 1 2 3 4 5 6 7 8 9 10")
	  ("PRIORITIES" . "AAA AA A B C")))

Keymaps

(define-key global-map "\C-cl" 'org-store-link)
(define-key global-map "\C-ca" 'org-agenda)
(global-set-key (kbd "<f4>") 'set-org-agenda-files)
(add-hook 'org-mode-hook
          (lambda ()
            (local-set-key (kbd "<f5>") #'org-toggle-inline-images)
            (local-set-key (kbd "C-c n s") #'org-narrow-to-subtree)
            (local-set-key (kbd "C-c w") #'widen)))
(define-key global-map "\C-cc" 'org-capture)
(global-set-key (kbd "C-c h") 'open-org-html-file-in-browser)
(global-set-key (kbd "<f6>") (lambda() (interactive)(org-publish-current-file)))

I know this doesn’t work but I think I’m close:

(global-set-key (kbd "C-c C-x C-p") 'org-pomodoro)

Eye Candy

No extra lines between headers

Org sometimes adds an extra line between headers, which drives me nuts. This fixes that:

 (setq org-blank-before-new-entry
	'((heading . nil) (plain-list-item . nil)))

Org-specific variable pitch

A version of variable pitch mode for Org docs that makes them look a little better:

(use-package org-variable-pitch
  :ensure t)

Spell Checking

Configure Spell Checker Name

If you’re running this on Windows then set then tell emacs to use hunspell instead of ispell:

(cond
 ((string-equal system-type "windows-nt")
  (progn
    (setq ispell-program-name 
          (concat chocolatey-lib "/" "hunspell.portable/tools/bin/hunspell"))))
 )

Use flyspell in Org

Setup spell-checking in general and turn it on when you load org-mode:

(use-package flyspell
  :ensure t
  :init
  (add-hook 'org-mode-hook
            (lambda () (flyspell-mode 1))))

Navigation

Open links in the same window, from here:

(setq org-link-frame-setup (quote ((vm . vm-visit-folder-other-frame)
                                   (vm-imap . vm-visit-imap-folder-other-frame)
                                   (gnus . org-gnus-no-new-news)
                                   (file . find-file)
                                   (wl . wl-other-frame))))

Jump to a task’s LOGBOOK

(fset 'tp/org/jump-to-logbook
      (lambda (&optional arg) 
        "Keyboard macro."
        (interactive "p")
        (kmacro-exec-ring-item (quote ([19 108 111 103 98 return] 0 "%d")) arg)))
(global-set-key (kbd "\C-ck") 'tp/org/jump-to-logbook)

Move the last bullet in a sublist to the top of a sublist

I admit that this is a bit hacky because it requires the following:

  1. Your mouse pointer has to be on the parent bullet of the sub-list.
  2. The parent bullet needs another bullet at the same level beneath it.

However, it works really well for the intended purpose, which is taking sub-bullets created by a capture template and moving them to the top of a sub-list.

(defun tp/org/move-last-subbullet-to-top-of-sublist ()
  "Move the last sub-bullet to the top of the list of sub-bullets."
  (interactive)
  (org-forward-heading-same-level 1)
  (forward-line -1)
  (kill-visual-line 1)
  (org-backward-heading-same-level 1)
  (forward-line 1)
  (org-yank)
  (forward-line -1))

Org-agenda

Specify the files that can be used in an agenda

(defun set-org-agenda-files ()
  (interactive)
  (message "Saving all org buffers to keep agenda files list clean")
  (org-save-all-org-buffers)
  (setq org-agenda-files (list org-directory (concat org-directory "journal")))
  (message "Done setting org agenda files."))

(set-org-agenda-files)

Custom Views

Here’s my custom agenda view that uses “column view”.

(setq org-agenda-overriding-columns-format
      "%TODO %4PRIORITY(Pri.) %50ITEM(Task) %4POM_Estimate(Est.) %7POM_Pomodori(Poms) %12CLOCKSUM_T(Today's Time)")
(setq org-agenda-view-columns-initially t)
(setq org-agenda-custom-commands
      '(("p" "Pomodoro View"
         ((tags "+today")))
        ("c" "Daily Checklist"
         ((org-ql-block '(and (todo)
                              (tags "daily_checklist")
                              (scheduled :to today))
                        ((org-ql-block-header "Daily Checklist")))))
        ("A" "Remaining Agenda"
         ((org-ql-block '(and (todo "TODO")
                              (not (or (tags "today")
                                       (tags "daily_checklist")))
                              (or
                               (scheduled :to today)
                               (deadline auto)))
                        ((org-ql-block-header "Remaining Agenda")))))
        ))

This only shows today’s tasks in the agenda view by default:

(setq org-agenda-span 1)

Finally, this appears to be necessary to get the clocksum functions to run properly on startup:

(org-clock-sum)

Helpers

Removing the today tag from a todo

This function clears out the “today” tag from the tasks in my custom view above.

Note: This function is very brittle and will need to change if you make any changes to your org-agenda view.

(fset 'tp/org/remove-today-tag
      (lambda (&optional arg)
        "Keyboard macro."
        (interactive "p")
        (kmacro-exec-ring-item '([6 6 6 6 6 6 101 116 return 14 1] 0 "%d") arg))) 

Removing The Recorded Pomodoro Count

(fset 'tp/org/remove-pom-count
      (lambda (&optional arg)
        "Removes the pomodoro count from a task while viewing the agenda in column mode."
        (interactive "p")
        (kmacro-exec-ring-item
         (quote ([6 6 6 6 101 1 11 return 14 1] 0 "%d")) arg)))

Org-ql

I’m hoping to use this to help organize things:

(use-package org-ql
  :ensure t)

Org-Clock

Set your default parameters for clock reports when they are viewed i the agenda view:

(setq org-agenda-clockreport-parameter-plist
      '(:scope agenda-with-archives :formula % :maxlevel 10 :tags t :fileskip0 t :compact t :narrow 60 :score 0))

If I’m idle for more than X minutes then ask me what to do with the clock time:

(setq org-clock-idle-time 15)

Org-capture

Properties

(setq org-default-notes-file (concat org-directory "/notes.org"))

Templates

(setq org-capture-templates
      '(
        ("t" "Todo" entry (file+headline (lambda () (concat org-directory "inbox.org")) "In-Process") "* TODO %? %^g")
        ("w" "Work Log" entry (file+headline (lambda () (concat org-directory "/WorkLogs.org")) "On-Deck") "** %(create-org-link 1) %?")
        ("d" "Daily Review" entry (file+headline (lambda () (concat org-directory "/Personal_Reviews.org")) "Daily") "** %(create-org-link 1 \"Daily Review\") %?")
        ("k" "Weekly Review" entry (file+headline (lambda () (concat org-directory "/Personal_Reviews.org")) "Weekly") "** %(create-org-link 1 \"Weekly Review\") %?")
        ("s" "Start of Week Check-In" entry (file+headline (lambda () (concat org-directory "/Personal_Reviews.org")) "Weekly") "** %(create-org-link 1 \"Start of Week Check-In\") %?")
        ("r" "Research Note" entry (file+headline (lambda () (concat org-directory "/ResearchNotes.org")) "In-Process") "** %(create-org-link nil) %?")
        ("l" "Lessons Learned" entry (file+headline (lambda () (concat org-directory "/LessonsLearned.org")) "Drafts") "** %(create-org-link nil) %?")
        ("m" "Meeting Minute" entry (file+headline (lambda () (concat org-directory "/MeetingMinutes.org")) "In-Process") "** %(create-org-link 1) %?")
        ))

To-do Lists

Workflow States

(setq org-todo-keywords
      '((sequence "TODO(t)" "WAIT(w@/!)" "|" "DONE(d!)" "CANCELED(c@)")))

Misc Props

Have org measure todo completion percentage recursively. nil means that you want it to look recursively.

(setq org-hierarchical-todo-statistics nil)

Functions

Calculating Dates

(defvar org-link-date-stamp-format "%y%m%d"
  "Format of date stamps to use in Org links")
 
(defun add-date-stamp-to-file-name (org-link)
  "Add a date stamp to the file name portion of an org link"
  (replace-regexp-in-string ":" 
                            (concat ":" 
                                    (format-time-string org-link-date-stamp-format (current-time))
                                    "-") org-link))
 
(defun add-date-stamp-to-link-title (org-link)
  "Add a date stamp to the title portion of an org link"
  (replace-regexp-in-string "\\]\\[" 
                            (concat "][" 
                                    (format-time-string org-link-date-stamp-format (current-time)) 
                                    " - ") org-link))

Misc

This is just a minor utility function.

(defun escape-file-titles (title)
  "Take an arbitrary string and replace all of the bad chars with
  underscores"
  (replace-regexp-in-string " " "_" title))

Here’s a much better version of my create-org-link function courtesy of -> http://emacs.stackexchange.com/a/12166/8228

(defun create-org-link (addDate? &optional title)
  "Takes a human-readable title for a link and returns a
   nicely-formatted file link."
  (interactive)
  (unless title
    (setq title
          (read-string "Please enter a title: ")))
  (let ((plain-file-link
         (format "[[file:%s.org][%s]]" (escape-file-titles title) title)))
    (let ((formatted-file-link
           (if addDate?
               (add-date-stamp-to-file-name (add-date-stamp-to-link-title plain-file-link))
             plain-file-link)))
      (if (called-interactively-p)
          (insert formatted-file-link)
        formatted-file-link))))

Browser-related

(defun org-file-name-convert-to-html (org-file-name)
  "Convert an org file name into its HTML eqlivalent"
  (replace-regexp-in-string 
   "\\(.*\\)\\/org\\/\\(.*\\)\.org$" 
   "\\1/org/\\2.html" org-file-name))
 
(defun open-org-html-file-in-browser ()
  "Open the current html version of the current org file in a web
  browser."
  (interactive)
  (browse-url-of-file (org-file-name-convert-to-html (buffer-file-name))))

Auto Insertion

When creating new org files I like to insert a nicely-formatted title at the top that’s based on the file name. The code below does things like replace underscores with spaces so that a file name like “This_Is_Cool.org” will automatically have a title of “This Is Cool”.

(defun format-page-title-from-buffer-name ()
  "Takes a buffer name and returns a much more friendly looking
  title.
 
  Note: This function assumes that the create-org-link function
  replaces spaces with underscores"
  (interactive)
  (replace-regexp-in-string "\.org" ""
                            (replace-regexp-in-string "_" " "
                                                      (replace-regexp-in-string "\w-\w" " - " (buffer-name))))
  )
 
(defun org-file-header ()
  "Generate a header for an org mode file"
  (interactive)
  (let ((out (format "#+TITLE: %s
 
"
                     (format-page-title-from-buffer-name))))
    out))
 
(defun org-file-insert ()
  "Insert a header containing HTML boilerplate and a title and
   whatever else you want."
  (interactive)
  (insert (org-file-header)))
 
(add-hook 'find-file-hook 'auto-insert)
(define-auto-insert ".*\.org$" 'org-file-insert)
 
                                        ; Don't ask for confirmation if auto-insert is called non-interactively.
(setq auto-insert-query nil)

Org-publish

Bootstrap

(require 'ox-publish)

Projects

Since this is an alist I don’t know how to embed functions in it. Thats’s why I’ve replaced the org-directory var with the literal value.

(setq org-publish-project-alist
      '(
        ("org-notes"               ;Used to export .org file
         :base-directory "~/org/"  ;directory holds .org files 
         :base-extension "org"     ;process .org file only    
         :publishing-directory "~/org/"    ;export destination
         :recursive t
         :publishing-function org-html-publish-to-html
         :headline-levels 4               ; Just the default for this project.
         :auto-preamble t
         :auto-sitemap t                  ; Generate sitemap.org automagically...
         :sitemap-filename "sitemap.org"  ; ... call it sitemap.org (it's the default)...
         :sitemap-title "Sitemap"         ; ... with title 'Sitemap'.
         :export-creator-info nil    ; Disable the inclusion of "Created by Org" in the postamble.
         :export-author-info nil     ; Disable the inclusion of "Author: Your Name" in the postamble.
         :auto-postamble nil         ; Disable auto postamble 
         :table-of-contents t        ; Set this to "t" if you want a table of contents, set to "nil" disables TOC.
         :section-numbers nil        ; Set this to "t" if you want headings to have numbers.
         :html-postamble "    <p class=\"postamble\">Last Updated %d.</p> " ; your personal postamble
         :style-include-default nil  ;Disable the default css style
         :html-head "<link id='pagestyle' rel='stylesheet' type='text/css' href='static/css/org.css' />\n<link id='pagestyle' rel='stylesheet' type='text/css' href='static/css/custom.css' />"
         
         ("org-static"                ;Used to publish static files
          :base-directory "~/org/static/"
          :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf"
          :publishing-directory "~/org/"
          :recursive t
          :publishing-function org-publish-attachment
          )
         ("org" :components ("org-notes" "org-static"))) ;combine "org-static" and "org-static" into one function call
        ))

Exporting To (Github-Flavored) Markdown

(use-package ox-gfm
  :ensure t)

Yasnippet

(defun yas/org-very-safe-expand ()
  (let ((yas/fallback-behavior 'return-nil)) (yas/expand)))
 
(add-hook 'org-mode-hook
          (lambda ()
            (make-variable-buffer-local 'yas/trigger-key)
            (setq yas/trigger-key [tab])
            (add-to-list 'org-tab-first-hook 'yas/org-very-safe-expand)
            (define-key yas/keymap [tab] 'yas/next-field)))

Babel

Here’s the languages that I can interpret. Note that there’s a difference between the way that the shell language is loaded between older and newer versions of Emacs. This my hacky way of fixing it for now:

(cond
 ((string-equal system-type "windows-nt")
  (progn
    (org-babel-do-load-languages
     'org-babel-load-languages
     '((js . t)
       (emacs-lisp . t)
       (shell . t)
       (python . t)
       (dot . t)
       (plantuml . t)))))
 ((string-equal system-type "gnu/linux")
  (progn
    (org-babel-do-load-languages
     'org-babel-load-languages
     '((js . t)
       (emacs-lisp . t)
       (shell . t)
       (python . t)
       (dot . t)
       (plantuml . t))))))

I don’t want to manually confirm that code written in the following languages can be executed:

(defun my-org-confirm-evaluate (lang body)
  (and (not (string= lang "js"))
       (not (string= lang "dot"))
       (not (string= lang "python"))))
 
(setq org-confirm-babel-evaluate 'my-org-confirm-evaluate)

Here are my global src block headers. So far, all this does is ensure that the publishing process never executes the code in src block (unless it’s overrided at a lower lever of course).

(setq org-babel-default-header-args
      (cons '(:eval . "never-export")
            (assq-delete-all :eval org-babel-default-header-args)))

Tags

These are the tags that I will use the most when creating new tasks.

(cond
 ((string-equal system-type "windows-nt")
  (progn
    ;; Work-related tags
    (setq org-tag-alist '(
                          ("c_admin" . ?a)
                          ("c_coding" . ?c)
                          ("c_documentation" . ?d)
                          ("goal" . ?g)
                          ("c_hardware_troubleshooting" . ?h)
                          ("c_training" . ?i)
                          ("c_knowledge_transfer" . ?k)
                          ("c_manual_testing" . ?m)
                          ("c_monitoring" . ?n)
                          ("c_meetings" . ?e)
                          ("objective" . ?o)
                          ("c_hr" . ?r)
                          ("c_agile_process_stuff" . ?s)
                          ("today" . ?t)
                          ("c_system_maintenance" . ?z)))))
 ((string-equal system-type "gnu/linux")
  (progn
    (setq org-tag-alist '(
                          ("c_bills" . ?b)
                          ("c_chore" . ?c)
                          ("c_errand" . ?e)
                          ("c_self_care" . ?s)
                          ("today" . ?t))))))

Here’s the tags that I exclude from tag inheritance:

(setq org-tags-exclude-from-inheritance (quote ("crypt")))

Org bullets

Of course you need these 😄

(use-package org-bullets
  :ensure t
  :init
  (add-hook 'org-mode-hook (lambda () (org-bullets-mode 1))))

Images

This turns on inline images at startup:

(setq org-startup-with-inline-images t)

… and this scales them down when viewing them inline:

(setq org-image-actual-width t)

Journaling

org-journal

Bootstrap

This seems like a promising way to keep track of what I do throughout the day.

(use-package org-journal
  :ensure t
  :init
  (setq org-journal-dir (concat org-directory "journal"))
  (setq org-journal-date-format "%A, %d %B %Y")
  (setq org-journal-file-format "%Y%m%d.org"))

Keymap

UGH

Property Help

Inherited Properties

Here’s my list of properties that can be inherited. I like to keep this small so as not to adversely affect the speed of agenda searches.

(setq org-use-property-inheritance
      (list "FEATURE_NUM"
            "STORY_NUM"))

Zetteldeft

This seems to be the best way to implement a Zettelkasten system with Emacs.

(use-package deft
  :ensure t
  :init
  (setq deft-extensions '("org" "md" "txt")
        deft-use-filename-as-title t)
  (setq deft-directory (concat org-directory "/zd")))

(use-package zetteldeft
  :ensure t
  :after deft
  :init
  (setq zetteldeft-link-indicator "§"
        zetteldeft-id-format "%Y-%m-%d-%H%M"
        zetteldeft-id-regex "[0-9]\\{4\\}\\(-[0-9]\\{2,\\}\\)\\{3\\}"
        zetteldeft-tag-regex "[#@][a-z-]+")
  :config (zetteldeft-set-classic-keybindings))

Magit

Bootstrap

First, install magit:

Note: There might be an issue with putting defer and bind in the same use-package statement. So if things get super crazy consider that.

(use-package magit
  :ensure t
  :defer 120
  :bind ("C-c m s" . magit-mode))

I’m currently stuck in dependency hell here and the old version of magit doesn’t work so I’m just going to comment all of this out.

(cond
 ((string-equal system-type "windows-nt")
  (progn
(add-to-list 'exec-path "c:/Program Files/Git/bin")    
    )))

SSH Stuff

Windows

Pushing to an SSH repo using Windows is a bit tricky. Here’s what I did to make it work:

  1. Install the regular Git package.
  2. Install the PuTTY tools, including pageant and plink.
  3. Manage your SSH keys using pageant
    1. Ideally, load your git-related keys on Windows startup.

After all of that I only needed the following config:

(cond
 ((string-equal system-type "windows-nt")
  (progn
    (setenv "SSH_ASKPASS" "git-gui--askpass")
    (setenv "GIT_SSH" "C:/Program Files/PuTTY/plink.exe"))))

Linux

I nee to copy some environment variables from my shell in order to use ssh-agent. Please note that this also makes everything else (including rsync-dired) work with ssh-agent too.

TODO - Install this automatically

(cond
 ((string-equal system-type "gnu/linux")
  (progn
    (require 'exec-path-from-shell)
    (exec-path-from-shell-copy-env "SSH_AGENT_PID")
    (exec-path-from-shell-copy-env "SSH_AUTH_SOCK")
    )))

Keymaps

I like having my own custom keymap for Magit.

(progn
  (define-prefix-command 'tp/magit/key-map)
  (define-key tp/magit/key-map (kbd "s") 'magit-status)
  (define-key tp/magit/key-map (kbd "b") 'magit-branch-popup)
  (define-key tp/magit/key-map (kbd "c") 'magit-checkout)
  (define-key tp/magit/key-map (kbd "d") 'magit-diff-popup)
  ;; Show the git log for the current file.
  (define-key tp/magit/key-map (kbd "l") 'magit-log-buffer-file))
(global-set-key (kbd "\C-cm") tp/magit/key-map)

I’m also already using C-x gg as a shortcut to jump to the top of a buffer, so I’m not a huge fan of Magit using C-x g to run magit-status. So let’s nuke that:

(global-unset-key (kbd "C-x g"))

Completion

ido-ubiquitous

This is the package that auto-completes file names when you press C-x C-f.

(ido-mode 1)
(ido-everywhere 1)

smex

This package is a lot like ido-ubiquitous but it autocompletes values when you press M-x:

(use-package smex
  :ensure t
  :config
  (smex-initialize)
  ;; :bind (("M-x" . smex)
  ;;        ("M-X" . smex-major-mode-commands)
  ;;        ("C-c C-c M-x" . 'execute-extended-command))
  )

Since I started using helm I don’t think Smex does anything any more, but I’m afraid to delete it at this point :-)

Helm

Use helm for M-x function searching:

(use-package helm
  :ensure t
  :bind (("M-x" . helm-M-x)
         ("C-x b" . helm-mini)
         ("C-c x" . helm-all-mark-rings)))

Timestamp Stuff

(defvar current-date-time-format "%a %b %d %H:%M:%S %Z %Y"
  "Format of date to insert with `insert-current-date-time' func
See help of `format-time-string' for possible replacements")

(defvar current-date-format-for-org "** %m/%d/%Y"
  "Format of date to insert with `insert-current-date' func for org files.
See help of `format-time-string' for possible replacements")

(defvar current-date-format-for-links "%m-%d-%Y"
  "This format works better for HTML links than the org format.")

(defvar current-date-format "%m/%d/%Y"
  "Format of date to insert with `insert-current-date' func.
Note the weekly scope of the command's precision.")

(defvar current-time-format-for-org "*** %H:%M"
  "Format of date to insert with `insert-current-time' func for org files.
Note the weekly scope of the command's precision.")

(defvar current-time-format "%H:%M:%S"
  "Format of date to insert with `insert-current-time' func.
Note the weekly scope of the command's precision.")

(defvar current-time-format-no-delim "%H%M%S"
  "Format of date with no delimiters.")

(defun insert-current-date-for-org ()
  "insert the current date as a heading into an org file.
Uses `current-date-time-format' for the formatting the date/time."
  (interactive)
  (insert (format-time-string current-date-format-for-org (current-time)))
  (insert "\n")
  )

(defun insert-current-date-for-links ()
  "Insert the current date in a way that works in HTML
  links."
  (interactive)
  (insert (format-time-string current-date-format-for-links (current-time)))
  )

(defun get-current-date-for-links ()
  "Retrieves the current date in a way that works in HTML
  links."
  (interactive)
  (format-time-string current-date-format-for-links (current-time))
  )

(defun insert-current-date ()
  "insert the current date into current buffer.
Uses `current-date-time-format' for the formatting the date/time."
  (interactive)
  (insert (format-time-string current-date-format (current-time)))
  )

(defun get-current-date ()
  "Returns the current date. Uses `current-date-time-format` for the formatting of the date/time"
  (interactive)
  (format-time-string current-date-format (current-time)))

(defun insert-current-time-for-org ()
  "insert the current time as a heading into an org file."
  (interactive)
  (insert (format-time-string current-time-format-for-org (current-time)))
  (insert "\n")
  )

(defun insert-new-day-headings ()
  "insert the 'new day' heading into an org file"
  (interactive)
  (insert-current-date-for-org)
  (insert "\n")
  (insert-current-time-for-org)
  (insert "\n")
  )

(defun insert-current-date-time ()
  "insert the current date and time into current buffer.
Uses `current-date-time-format' for the formatting the date/time."
  (interactive)
  (insert "==========\n")
					  ;       (insert (let () (comment-start)))
  (insert (format-time-string current-date-time-format (current-time)))
  (insert "\n")
  )

(defun insert-current-time ()
  "insert the current time (1-week scope) into the current buffer."
  (interactive)
  (insert (format-time-string current-time-format (current-time)))
  )

(defun get-current-time ()
  "Returns the current time (1-week scope).."
  (interactive)
  (format-time-string current-time-format (current-time)))

(defun get-current-time-no-delim ()
  "Returns the current time with no delimiters."
  (interactive)
  (format-time-string current-time-format-no-delim (current-time)))

(global-set-key "\C-c\C-d" 'insert-current-date-time)
(global-set-key "\C-c\C-t" 'insert-current-time)

Vim Compat

Here’s some of the keystrokes from Vim that I still like to use.

This emulates Vim’s “gg top” mnemonic:

(global-set-key (kbd "C-x gg") 'beginning-of-buffer)
(global-set-key (kbd "C-x G")  'end-of-buffer)

Dev

Misc

Rainbow Delimiters

(use-package rainbow-delimiters
  :ensure t
  :hook (prog-mode . rainbow-delimiters-mode))

Linting

Flycheck relies on external programs to analyze your code. Here’s what you need to install for your favorite programming languages:

  • Python
    • pylint
  • Bash
    • shellcheck
(use-package flycheck
  :disabled
  :hook (after-init . global-flycheck-mode)
  )

Projectile

Projectile is a fantastic package that makes it easier to work within a project using Emacs.

I’m not a huge fan of it’s built-in prefix though so let’s fix that:

(use-package projectile
  :ensure t
  :init
  (setq projectile-keymap-prefix (kbd "C-c p"))
  :bind-keymap
  ("C-c p" . projectile-mode)
  :config
  (setq projectile-globally-ignored-directories
        (append '(".git" ".pytest_cache" ".vscode" "Output" "venv" "venv3" "node_modules")))
  (setq projectile-globally-ignored-files
        (append '("*~" "*#" "log.html" "output.xml" "report.html")))
  )

(use-package helm-projectile
  :ensure t
  :hook projectile-mode
  :config
  (setq projectile-completion-system 'helm)
  (helm-projectile-on))

Indent

This turns off tabs and replaces them with 4 spaces for most major modes:

(setq-default c-basic-offset 4)
(setq-default indent-tabs-mode nil)

Highlighting the Current Line

(global-hl-line-mode)

Powershell

(use-package powershell
  :ensure t
  :config
  (autoload 'powershell "powershell" "Run powershell as a shell within emacs." t) 
  )

Keymaps

(add-hook 'robot-mode-hook
          (lambda () (local-set-key (kbd "<f5>") #'robot-mode-find-kw)))

Hiding ^M Characters In Robot Files

(add-hook 'robot-mode-hook 'tp/file/remove-dos-eol)

Lisp

Paredit

Let’s just turn it on for everything 😄

(use-package paredit
  :ensure t
  :hook ((emacs-lisp-mode . enable-paredit-mode)
         (eval-expression-minibuffer-setup . enable-paredit-mode)
         (ielm-mode . enable-paredit-mode)
         (lisp-mode . enable-paredit-mode)
         (lisp-interaction-mode . enable-paredit-mode)
         (scheme-mode . enable-paredit-mode)))

Eshell

Awesh]]]ell

Holy crap is this cool, and it even works on Windows. If only I could install it from Melpa :-)

(use-package aweshell
  :load-path "customizations/packages/aweshell"
  :defer t)

Paren matching

These customizations make it easier to know where code blocks are.

(show-paren-mode 1)

Auto-Eval’ing Code

This seemed like a good idea but caused lots and lots of weirdness that kept me from closing Emacs.

;; (defun eval-emacs-lisp-buffer ()
;;   "Eval a buffer if it's major mode is emacs-lisp."
;;   (when (eq major-mode 'elisp-mode)
;;     (eval-buffer)))

;; (add-hook 'after-save-hook 'eval-buffer)

Autoit

Yet another package that we can’t install from melpa.

(cond
 ((string-equal system-type "windows-nt")
  (progn
    (require 'autoit-mode)
    (add-to-list 'auto-mode-alist '("\\.au3\\'" . autoit-mode)))))

Web

Running a web server

Overview

web-server is a great module that can interpret elisp or just serve up static files (which is how I use it). For me it provides a really easy way viewing HTML files in a browser in a “real” way.

Bootstrap

(use-package web-server
  :ensure t
  :defer t)

Convenience Functions

This function starts a server on port 9003 that serves up static content that’s located in the PWD (which is also your DOCROOT).

(defun tp/httpd/start-server-in-pwd ()
  (interactive)
  (setq httpd-port 8001)
  (setq httpd-root default-directory)
  (httpd-start)
  (message "Serving up files on port 8001."))

HTML

web-mode is awesome!

(use-package web-mode
  :ensure t
  :config
  (add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode)))

Python

Editing Pip Requirements Files

(use-package pip-requirements
  :ensure t)

Elpy

Let’s see if this works better for me than regular old python-mode:

(use-package elpy
  :ensure t
  :mode ("\\.py\\'" . python-mode)
  :init
  (add-hook 'python-mode-hook #'elpy-enable)
  :config
  ;; (setq python-shell-interpreter "jupyter"
  ;;       python-shell-interpreter-args "console --simple-prompt"
  ;;       python-shell-prompt-detect-failure-warning nil)
  ;; (add-to-list 'python-shell-completion-native-disabled-interpreters
  ;;              "jupyter")
  )

I turned off the jupyter stuff because a) it wasn’t really helping me and b) I’m having issues using it on Nixos that I’m sure are just user error.

Code Formatting

Overview

Apparently I need all of these :-/

autopep8

(use-package py-autopep8
  :ensure t
  :hook (elpy-mode . py-autopep8-enable-on-save))

Black

(use-package blacken
  :ensure t)

Jupyter Notebook

This looks kindof cool

(use-package ein
  :ensure t)

Docker

Let’s add support for Dockerfiles!

(use-package dockerfile-mode
  :ensure t
  :init
  (add-to-list 'auto-mode-alist '("Dockerfile\\'" . dockerfile-mode)))

Also, the docker porcelain is pretty freakin’ sweet:

(use-package docker
  :ensure t
  )

Stack Overflow

sx

This a Stack Exchange browser for Emacs. As of today (3/20/2019) the version in MELPA stable has a pretty major bug in it so I’m using HEAD from Github:

(use-package sx-load
  :disabled
  :load-path "customizations/packages/sx.el")

Common Lisp

Slime

(use-package slime
  :ensure t
  :mode "\\.cl\\'"
  :init
  (cond
   ((string-equal system-type "windows-nt")
    (progn
      (setq inferior-lisp-program "c:/who/knows")))
   ((string-equal system-type "gnu/linux")
    (progn
      (setq inferior-lisp-program "/usr/bin/sbcl")))))

Plain Old REST

Restclient!!!

This is a fantastic package for interacting with REST endpoints in an interactive way.

Verb

This also looks very promising. It integrates with org-mode.

(use-package verb
  :ensure t
  :after org
  :config (define-key org-mode-map (kbd "C-c C-r") verb-command-map))

PlantUML

YAML

(use-package yaml-mode
  :ensure t
  :init
  (add-to-list 'auto-mode-alist '("\\.yml\\'" . yaml-mode)))

Bash

shx

This is a great shell mode helper that gives you a bunch of extras:

(use-package shx
  :ensure t
  :config
  (shx-global-mode))

emacs-bash-completion

This gives you bash completion when using shell mode.

(use-package bash-completion
  :ensure t
  :config
  (progn
    (autoload 'bash-completion-dynamic-complete
      "bash-completion"
      "BASH completion hook")
    (add-hook 'shell-dynamic-complete-functions
              'bash-completion-dynamic-complete)))

Clojure

Cider

(use-package cider
  :ensure t)

NixOS Stuff

Syntax highlighting

(use-package nix-mode
  :ensure t)

Text Search

Ack

The ack Emacs plugin looked sweet but I couldn’t get it to work on Windows :-( Luckily the Silver Searcher worked!

Ag (The Silver Searcher) And Helm Swoop

Here’s the basics:

(use-package ag
  :ensure t)
(use-package helm-swoop
  :ensure t)

I thought it would be nice to access the ag-* functions using a Ctrl-c f prefix, and the code below does exactly that (thanks to Xah Lee once again).

I also added a few helm-swoop shortcuts since that’s also an excellent tool for searching files.

(progn
  (define-prefix-command 'tp/find/ag/key-map)
  ; Find a file in the current project
  (define-key tp/find/ag/key-map (kbd "p") 'projectile-ag)
  ; Find in the current buffer.
  (define-key tp/find/ag/key-map (kbd "b") 'helm-swoop)
  ; Find using all open buffers
  (define-key tp/find/ag/key-map (kbd "o") 'helm-org-rifle)
  ; Search all of your org buffers
  (define-key tp/find/ag/key-map (kbd "r") 'helm-org-rifle)
  ; And if you didn't trust any of these, try plain-old ag :-)
  (define-key tp/find/ag/key-map (kbd "a") 'ag)
  )

(global-set-key (kbd "\C-cf") tp/find/ag/key-map)

Wgrep

Why not? It looks so cool.

(use-package wgrep
  :ensure t)

Also, to be able to use this with ag I need to install the following:

(use-package wgrep-ag
  :ensure t)

Swiper and Ivy

The killer feather here is using Swiper instead of incremental search when hitting C-s:

(use-package swiper
  :ensure t
  :config
  (progn
    (ivy-mode 1)
    (setq ivy-use-virtual-buffers t)
    (setq enable-recursive-minibuffers t)
    (global-set-key "\C-s" 'swiper)
    (global-set-key (kbd "C-c C-r") 'ivy-resume)
    (global-set-key (kbd "<f6>") 'ivy-resume)
    (define-key minibuffer-local-map (kbd "C-r") 'counsel-minibuffer-history)))

Org-Rifle

This is pretty cool and handy when performing word searches across all of your open org buffers.

(use-package helm-org-rifle
  :ensure t)

Ediff Stuff

Syncthing Conflicts

This is a great package for comparing syncthing conflicts:

(use-package emacs-conflict
  :load-path "customizations/packages/emacs-conflicts"
  :bind
  (("C-c r r" . emacs-conflict-resolve-conflicts)))

Ediff In General

If at all possible I prefer to splitt my diff windows horizontally:

(setq ediff-split-window-function (quote split-window-horizontally))

Registers

Org

(set-register ?w (cons 'file (concat org-directory "/WorkLogs.org")))
(set-register ?i (cons 'file (concat org-directory "/index.org")))
(set-register ?m (cons 'file (concat org-directory "/MeetingMinutes.org")))
(set-register ?v (cons 'file (concat org-directory "/Personal_Reviews.org")))
(set-register ?j (cons 'file (concat org-directory "/journal/" (format-time-string "%Y%m%d") ".org")))

OS-Specific

(cond
 ((string-equal system-type "windows-nt")
  (progn
    (set-register ?p (cons 'file "c:/tools/cmder/config/user-profile.ps1"))
    (set-register ?g (cons 'file "c:/users/tom.purl/.gitconfig"))
    (set-register ?r (cons 'file "c:/users/tom.purl/git/braindump/index.org"))))
 ((string-equal system-type "gnu/linux")
  (progn
    (set-register ?g (cons 'file "~/.gitconfig"))
    (set-register ?r (cons 'file "~/gtd/kata-blog/index.org"))))
 )

Misc

(set-register ?e (cons 'file "~/.emacs.d/emacs-init.org"))

Log Editing / Viewing

Make mode load automatically Log.txt files

Make mode change file to RO

(use-package logview
  :disabled
  :ensure t
  )

Web Browsing

Make eww create a new buffer if executed from a non-=eww= buffer. This allows you to easily create more than one eww buffer. Also, I copied this from https://emacs.stackexchange.com/a/24477/8228, which was copied from Xah’s erogemacs tips (like a lot of stuff in this file).

;; Auto-rename new eww buffers
(defun xah-rename-eww-hook ()
  "Rename eww browser's buffer so sites open in new page."
  (rename-buffer "eww" t))
(add-hook 'eww-mode-hook #'xah-rename-eww-hook)

Scratch Buffer

Saving And Restoring The Buffer

Also stole from EOS:

(defun eos/core/save-persistent-scratch ()
  "Write the contents of *scratch* to the file name
`persistent-scratch-file-name'."
  (with-current-buffer (get-buffer-create "*scratch*")
    (write-region (point-min) (point-max) "~/.emacs.d/persistent-scratch")))

(defun eos/core/load-persistent-scratch ()
  "Load the contents of `persistent-scratch-file-name' into the
  scratch buffer, clearing its contents first."
  (interactive)
  (if (file-exists-p "~/.emacs.d/persistent-scratch")
      (with-current-buffer (get-buffer "*scratch*")
        (delete-region (point-min) (point-max))
        (insert-file-contents "~/.emacs.d/persistent-scratch"))))

(add-hook 'after-init-hook 'eos/core/load-persistent-scratch)
(add-hook 'kill-emacs-hook 'eos/core/save-persistent-scratch)

Syncing

I like to sync some of my files using Syncthing. The problem is when I do the following:

  1. Edit a file on my laptop and save and sync without killing the buffer.
  2. Edit the same file on my phone using Orgzly and sync.
  3. Sync everything on my laptop and visit the same buffer in Emacs.

At this point I would be looking at the version of the file from step 1 on my laptop. To view the step 2 updates I would need to manually revert the buffer, and chances are I wouldn’t know which buffers to revert.

I therefore am turning on global-auto-revert-mode to see if that helps.

(global-auto-revert-mode 1)

Markdown

First, let’s install the mode:

(use-package markdown-mode
  :mode "\\.md\\'"
  :ensure t)

Window Management

Functions

I just love this, it was stupid simple to write and I think I use it a least 10 times a day. It “moves” the current window into a new frame.

What does that mean? Let’s say you split your current window (which is called a frame in Emacs) into 2 using Ctrl-3 or something like that and then realize that you would really like to focus on the buffer in that “split” (which is called a window in Emacs). Wouldn’t it be great if you could just move it to a new frame?

(defun tp/wm/move-window-to-new-frame ()
  "Take the content of the current window and move it to its own
   frame"
  (interactive)
  (make-frame)
  (delete-window))

Eyebrowse

It’s kindof like tmux for Emacs but it doesn’t do quite as much.

(use-package eyebrowse
  :ensure t
  :config
  (validate-setq eyebrowse-mode-line-separator " "
                 eyebrowse-new-workspace t)
  (eyebrowse-mode t))

Dired Stuff

Dired-hacks

This includes all kinds of cool stuff:

(if (not (require 'dired-narrow nil t))
    (message "Package `dired-narrow' not found"))

(if (not (require 'dired-subtree nil t))
    (message "Package `dired-subtree' not found")
  (bind-key "<tab>" #'dired-subtree-toggle dired-mode-map)
  (bind-key "<backtab>" #'dired-subtree-cycle dired-mode-map))

Find Files By Name in Dired and Edit the Buffer

Method 1: Finding Filer Recursively

This only works on a system with find installed. It asks you for a directory and a search string, finds the files, and displays them in an editable dired buffer. It’s pretty freakin’ sweet and so handy.

(defun tp/dired/find-files-in-dired-and-edit ()
  (interactive)
  (find-name-dired
   (read-directory-name "Directory: ")
   (read-string "Search string: "))
  (wdired-change-to-wdired-mode))

Method 2: Using dired-narrow

I’m using the techniques from this video to “narrow” or filter the results of a dired buffer:

Make Dired Prettier

Git integration

I’m having lots of issues with this and will try to fix it later using dired-git.

Narrowing and Widening

(add-hook 'dired
          (lambda ()
            (local-set-key (kbd "C-c n s") #'dired-narrow-regexp)))

Rsync

This is a really good way of copying files remotely in an asynchronous way.

(use-package dired-rsync
  :ensure t
  :config
  (bind-key "C-c C-r" 'dired-rsync dired-mode-map))

Omitting Non-Interesting Files

I don’t like seeing all of the temp files in dired, so dired-x to the rescue!

(require 'dired-x)
(add-hook 'dired-mode-hook
          (lambda ()
            ;; Set dired-x buffer-local variables here.  For example:
            (dired-omit-mode 1)))

Buffer Management

ibuffer

First let’s set the keystrokes:

(global-set-key (kbd "C-x C-b") 'ibuffer) ;; Use Ibuffer for Buffer List

Next let’s group buffers:

(setq ibuffer-saved-filter-groups
      '(("home"
         ("emacs-config" (filename . "emacs-init.org"))
         ("Org" (or (mode . org-mode)
                    (filename . "OrgMode")
                    (name . "\*Org Agenda\*")))
         ("Dired" (or (mode . dired-mode)
                      (name . "\*Sunrise\*")))
         ("Dev" (or (mode . python-mode)
                    (mode . robot-mode)))
         ("Magit" (or (name . "\*magit")
                      (name . "magit")
                      (mode . magit-mode)))
         ("EXWM" (or (mode . exwm-mode)
                     (name . "\*EXWM\*")))
         ("Shells" (or (mode . eshell-mode)
                       (mode . shell-mode)
                       (mode . comint-mode))))
        ("eww" (or (mode . eww-mode)
                   (mode . eww-bookmark-mode)))
        ("Help" (or (name . "\*Help\*")
                    (name . "\*Apropos\*")
                    (name . "\*info\*")))))

(add-hook 'ibuffer-mode-hook
          '(lambda ()
             (ibuffer-switch-to-saved-filter-groups "home")))
(use-package burly
  :quelpa (burly :fetcher github :repo "alphapapa/burly.el"))

Blogging

ox-hugo

This is a super sweet package:

(use-package ox-hugo
  :ensure t
  :after
  ox)

Encryption

GPG

Easy GPG Assistant

This is Emac’s built-in interface GPG that I like to use to transparently encrypt entire files. When you use it you should put something like this at the top of your file:

# -*- mode:org; epa-file-encrypt-to: ("[email protected]") -*-

You can replace the email address with the public key’s id.

(require 'epa-file)
(epa-file-enable)
(setq epa-pinentry-mode 'loopback)

I hate to admit it but my current process for whole-file encryption goes like this:

  1. I add a line that looks something like this to the top of my file:
    • # -*- mode:org; epa-file-encrypt-to: ("5BF5A514D04978DD") -*-
  2. I then drop into the command line and run a command that looks something like this:
    • =gpg –output foo.org.gpg –encrypt –recipient 5BF5A514D04978DD foo.org
  3. I then test that I can open foo.org.gpg in Emacs seamlessly.

Pinentry

This makes it possible to use pinentry from exwm:

(use-package pinentry
  :ensure t
  :config
  (pinentry-start))

Org-mode

I use this to encrypt sections of org documents. You just have to tag the section with crypt.

(require 'org-crypt)
(org-crypt-use-before-save-magic)

(cond
 ((string-equal system-type "windows-nt")
  (progn
    (setq org-crypt-key "989889BA8447C29C")))
 ((string-equal system-type "gnu/linux")
  (progn
    (setq org-crypt-key "5BF5A514D04978DD"))))

Misc?

Timers

Chronos seems to do this really well, but unfortunately it isn’t available (as of 2/4/19) in Melpa Stable. So you’ll first want to download it and then do this:

(require 'chronos)

Notifications

This is definitely a work in progress :-)

(cond
 ((string-equal system-type "windows-nt")
  (progn
    (setq chronos-shell-notify-program "c:/users/tom.purl/AppData/Roaming/Documents/td/apps/snarl 5.0/tools/heysnarl"
          chronos-shell-notify-parameters '("notify?text=Important!&priority=1")
          chronos-expiry-functions '(chronos-buffer-notify
                                     chronos-shell-notify)))))

Jumping Between Buffers

ace-window works well for this.

(use-package ace-window
  :ensure t)
(global-set-key (kbd "C-]") 'ace-window)

Sound

For god’s sake, please don’t beep.

(setq ring-bell-function (lambda ()))

Trying Out New Packages

try is kindof fun:

(use-package try
  :ensure t)

Systems Monitoring

Because why not?

;; (use-package symon
;;   :ensure t)

Peg

I need this for orq-ql:

(use-package peg
  :ensure t)

Bookmarks

Since I run Emacs as a user service it is unceremoniously killed every time I log out of a session. My bookmarks are therefore never saved. This fixes that by saving my bookmarks every time I change one.

(setq bookmark-save-flag 1)

Text Navigation

Backspace to the beginning of the line

This is a nice little function that “de-indents” you back to the first column in a line:

(defun tp/backward-kill-line (arg)
  "Kill ARG lines backward."
  (interactive "p")
  (kill-line (- 1 arg)))
(global-set-key [M-delete] 'scratch/backward-kill-line)
(global-set-key [M-backspace] 'scratch/backward-kill-line)

Server stuff

(require 'server)
;; Start a server if (server-running-p) does not return t (e.g. if it
;; returns nil or :other)
(or (eq (server-running-p) t)
    (server-start))

(when (equal window-system 'w32)
  (setq server-use-tcp t))

(with-eval-after-load 'server
  (when (equal window-system 'w32)
    ;; Suppress error "directory  ~/.emacs.d/server is unsafe". It is needed
    ;; needed for the server to start on Windows.
    (defun server-ensure-safe-dir (dir) "Noop" t)))

Epub

nov looks like a pretty cool epub reader:

(use-package nov
  :ensure t
  :config
  (add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode)))

Annotation

I’d love to be able to transparently annotate EPUB and PDF files using Emacs. org-noter looks like a good way to do this:

(use-package org-noter
  :ensure t)

OS Stuff

helm-proc

This is a lot like top but managed using normal emacs conventions.

(use-package helm-proc
  :ensure t)

Multimedia

Bongo (MP3 Player)

This looks really very cool:

(use-package bongo
  :ensure t)

Macros

flac2mp3

It’s a royal pain in a shell script to loop over a list of files using the shell, so here’s what I do instead.

  1. find . -name "*flac*" > flac.sh
  2. Add a shebang and set -e to the top of flac.sh
  3. Execute this macro on every line after positioning the cursor in the first column.
(fset 'tp/mm/flac2mp3
      (lambda (&optional arg)
        "Keyboard macro."
        (interactive "p")
        (kmacro-exec-ring-item
         (quote ([67108896 5 134217847 1 102 102 109 112 101 103 32 45 105 32 34 5 34 32 45 97 98 32 51 50 48 107 32 45 109 97 112 95 109 101 116 97 100 97 116 97 32 48 32 45 105 100 118 backspace 51 118 50 95 118 101 114 115 105 111 110 32 51 32 34 25 backspace backspace backspace backspace 109 112 51 34 14 1] 0 "%d")) arg)))