EOS: Writing Module

Table of Contents

(provide 'eos-writing)

Used more often than you'd think…

(defun lod ()
  "Well. This is disappointing."
  (insert "ಠ_ಠ"))

(global-set-key (kbd "C-c M-d") #'lod)

Installing Markup Languages

Let's install markdown mode, since all of Github seems enamored with it.

(use-package markdown-mode
  :ensure t
  :mode (("\\`README\\.md\\'" . gfm-mode)
         ("github\\.com.*\\.txt\\'" . gfm-mode)
         ("\\.md\\'"          . markdown-mode)
         ("\\.markdown\\'"    . markdown-mode))
  (setq markdown-enable-wiki-links t
        markdown-italic-underscore t
        markdown-make-gfm-checkboxes-buttons t
        markdown-gfm-additional-languages '("sh"))
  (add-hook 'markdown-mode-hook #'flyspell-mode))

YAML is used all over the place

(use-package yaml-mode
  :ensure t)

Elasticsearch uses asciidoc everywhere for documentation, so let's install it

(use-package adoc-mode
  :ensure t)

Functions for Skeleton Code

Skeletons are kind of like yasnippet, but they don't mess with my keybindings all over the place and take forever to load ಠ_ಠ

(require 'skeleton)

(define-skeleton eos/org-header
  "Insert a standard header for org-mode files"
  "Title: "
  "#+TITLE: " str \n
  "#+AUTHOR: " (user-full-name) \n
  "#+EMAIL: " user-mail-address \n
  "#+SETUPFILE: ~/eos/setupfiles/default.setup

| *Author* | {{{author}}} ({{{email}}})    |
| *Date*   | {{{time(%Y-%m-%d %H:%M:%S)}}} |

* Introduction
" \n)

(define-skeleton eos/org-wrap-elisp
  "Wrap text with #+BEGIN_SRC / #+END_SRC for the emacs-lisp code"
  > "#+BEGIN_SRC emacs-lisp" \n
  > _ \n
  > "#+END_SRC" \n)

(define-skeleton eos/org-wrap-source
  "Wrap text with #+BEGIN_SRC / #+END_SRC for a code type"
  "Language: "
  > "#+BEGIN_SRC " str \n
  > _ \n
  > "#+END_SRC" \n)

(define-skeleton eos/es-make-index
  "Insert boilerplate to create an index with `es-mode' syntax"
  "Index name: "
  "POST /" str \n
  "{" \n
  > "\"settings\": {" \n
  > "\"index\": {" \n
  > "\"number_of_shards\": 1," \n
  > "\"number_of_replicas\": 0" \n
  > "}" \n ;; index
  > "}," \n ;; settings
  > "\"mappings\": {" \n
  > "\"" (skeleton-read "Type name: ") "\": {" \n
  > "\"properties\": {" \n
  > "\"body\": {" \n
  > "\"type\": \"string\"" \n
  > "}" \n ;; body
  > "}" \n ;; properties
  > "}" \n ;; type
  > "}" \n ;; mappings
  > "}" \n)

(define-skeleton eos/java-try-catch
  "Wrap code in a Java try/catch"
  > "try {" \n
  > _
  > "} catch (Exception e) {" \n
  > "throw e;" \n
  > "}" \n)

And now let's add a hydra for the skeletons

(defhydra eos/hydra-skeleton nil
  "Insert Skeleton"
  ("e" eos/org-wrap-elisp "Wrap as elisp" :exit t)
  ("s" eos/org-wrap-source "Wrap as source" :exit t)
  ("i" eos/es-make-index "ES Index" :exit t)
  ("h" eos/org-header "Org Header" :exit t)
  ("t" eos/java-try-catch "Wrap with try/catch" :exit t))

Inserting Abbreviations with Abbrev-mode

(use-package abbrev
  :init (add-hook 'after-init-hook 'abbrev-mode)
  :diminish abbrev-mode
    '(("ooc" "out of curiosity" nil 0)))
    '(("<el" "" 'eos/org-wrap-elisp 0))))


I'm playing around with Synosaurus for thesaurus lookup in Emacs, it's bound to C-c s

(use-package synosaurus
  :ensure t
  (setq-default synosaurus-backend 'synosaurus-backend-wordnet)
  (add-hook 'after-init-hook #'synosaurus-mode))

Numbering rectangles

Let's say you have a list like:

First Item
Second Item
Third Item
Fourth Item

And you want to number it to look like:

1. First Item
2. Second Item
3. Third Item
4. Fourth Item

This function allows you to hit C-x r N and specify the pattern and starting offset to number lines in rectangular-selection mode:

(defun number-rectangle (start end format-string from)
  "Delete (don't save) text in the region-rectangle, then number it."
   (list (region-beginning) (region-end)
         (read-string "Number rectangle: "
                      (if (looking-back "^ *") "%d. " "%d"))
         (read-number "From: " 1)))
    (goto-char start)
    (setq start (point-marker))
    (goto-char end)
    (setq end (point-marker))
    (delete-rectangle start end)
    (goto-char start)
    (loop with column = (current-column)
          while (and (<= (point) end) (not (eobp)))
          for i from from   do
          (move-to-column column t)
          (insert (format format-string i))
          (forward-line 1)))
  (goto-char start))

(global-set-key (kbd "C-x r N") 'number-rectangle)

Nice Writing/Viewing helpers

There's a nice helper from Endless Parentheses that defines a do-what-I-mean version of the narrow-or-widen so I don't have to keep remembering which is which

(defun eos/narrow-or-widen-dwim (p)
  "Widen if buffer is narrowed, narrow-dwim otherwise.
Dwim means: region, org-src-block, org-subtree, or
defun, whichever applies first. Narrowing to
org-src-block actually calls `org-edit-src-code'.

With prefix P, don't widen, just narrow even if buffer
is already narrowed."
  (interactive "P")
  (declare (interactive-only))
  (cond ((and (buffer-narrowed-p) (not p)) (widen))
         (narrow-to-region (region-beginning)
        ((derived-mode-p 'org-mode)
         ;; `org-edit-src-code' is not a real narrowing
         ;; command. Remove this first conditional if
         ;; you don't want it.
         (cond ((ignore-errors (org-edit-src-code) t)
               ((ignore-errors (org-narrow-to-block) t))
               (t (org-narrow-to-subtree))))
        ((derived-mode-p 'latex-mode)
        (t (narrow-to-defun))))

(global-set-key (kbd "C-x C-n") #'eos/narrow-or-widen-dwim)

Pasting help

Export an HTML version of the buffer and scp it somewhere

(defvar fci-enabled? nil)
(defvar fc-enabled? nil)
(defun eos/surround-scpaste (fun &rest args)
  (if (and (boundp 'fci-mode) fci-mode)
        (setq fci-enabled? t)
        (fci-mode -1))
    (setq fci-enabled? nil))
  (if (and (boundp flycheck-mode) flycheck-mode)
        (setq fc-enabled? t)
        (flycheck-mode -1))
    (setq fc-enabled? nil))
  (let ((result (apply fun args)))
    (when fci-enabled?
      (fci-mode 1))
    (when fc-enabled?
      (flycheck-mode 1))

(install-pkgs '(scpaste))
(require 'scpaste)
(setq scpaste-http-destination "http://writequit.org/paste"
     scpaste-scp-destination "writequit.org:www/paste"
     scpaste-user-name "dakrone"
     scpaste-user-address "http://writequit.org/")
;; Disable fill-column-indicator while scpasting
(advice-add 'scpaste :around #'eos/surround-scpaste)

Escaping strings

String-edit is a nice mode for editing strings without dealing with all the escaping. I bind this to C-c " to make it similar to Org-mode's C-c ' binding.

(use-package string-edit
  :ensure t
  :bind (("C-c \"" . string-edit-at-point)))

Respecting whitespace with fill-column and whitespace-mode

Emacs needs to respect whitespace. This means no trailing spaces, keep things within 80-columns (or whatever the fill-column is) and all that.

I have to hack something around this to make it fixed for org->html exports

(use-package fill-column-indicator
  :ensure t
  ;; fix for org -> html export
  (defun fci-mode-override-advice (&rest args))
  (use-package org)
  (advice-add 'org-html-fontify-code :around
              (lambda (fun &rest args)
                (advice-add 'fci-mode :override #'fci-mode-override-advice)
                (let ((result  (apply fun args)))
                  (advice-remove 'fci-mode #'fci-mode-override-advice)

  (defvar eos/fci-disabled nil)
  (make-variable-buffer-local 'eos/fci-disabled)
  ;; Add a hook that disables fci if enabled when the window changes and it
  ;; isn't wide enough to display it.
  (defun eos/maybe-disable-fci ()
    ;; Disable FCI if necessary
    (when (and fci-mode
               (< (window-width) (or fci-rule-column fill-column)))
      (fci-mode -1)
      (setq-local eos/fci-disabled t))
    ;; Enable FCI if necessary
    (when (and eos/fci-disabled
               (eq fci-mode nil)
               (> (window-width) (or fci-rule-column fill-column)))
      (fci-mode 1)
      (setq-local eos/fci-disabled nil)))

  (defun eos/add-fci-disabling-hook ()
    (add-hook 'window-configuration-change-hook
  (add-hook 'prog-mode-hook #'eos/add-fci-disabling-hook))

There's also the built-in whitespace mode, which I use to highlight things over the fill-column (80 in most programming modes, 140 in Java :P)

;; A subset, only training lines and whitespace
(setq whitespace-style '(spaces tabs newline space-mark tab-mark newline-mark))

(setq whitespace-display-mappings
      ;; all numbers are Unicode codepoint in decimal.
      '(;;(space-mark 32 [183] [46])
        ;; (newline-mark 10 [172 10]) ;; the paragraph sign
        (newline-mark 10 [172 10]) ;; mathematical "not"
        (tab-mark 9 [187 9] [92 9])))

(defun eos/turn-on-whitespace-mode ()
  (setq-local whitespace-line-column fill-column)
  (whitespace-mode +1)
  (diminish 'whitespace-mode)
  (whitespace-newline-mode 1)
  (diminish 'whitespace-newline-mode))

(add-hook 'prog-mode-hook #'eos/turn-on-whitespace-mode)

And a helper to turn off whitespace mode when exporting org documents

(use-package org
  (advice-add 'org-html-fontify-code :around
              (lambda (fun &rest args)
                (whitespace-mode -1)
                (let ((result  (apply fun args)))
                  (whitespace-mode +1)

Highlighting indentation

There are basically two ways to do this, either with highlight-indent-guides or with highlight-indentation. The names are confusing, and they each have pros and cons, so it's hard to tell which is the better option…

(use-package highlight-indent-guides
  :ensure t
  :disabled t
  (setq highlight-indent-guides-method 'character)
  (add-hook 'prog-mode-hook #'highlight-indent-guides-mode))

Author: Lee Hinman

Created: 2017-08-10 Thu 13:40