Category Archives: emacs

Let’s have a virtual Emacs conference in August – help me make it happen!

Why August? It’s an arbitrary target, although it tickles my brain to think about celebrating my 32nd birthday with awesome people sharing awesome ideas. (Incidentally, I’ll also reach the point of having been using Emacs for about half my life – doubly neat!)

Anyway. I think it would be great to have some kind of knowledge-swapping thing. Since I’m not particularly keen on travelling, not everyone can make it out to Canada, and it’s hard to make awesome in-person conference recordings anyway, maybe a virtual conference would be a great bet. I’m willing to spend what I would have paid for airfare on things like organization, speaker honoraria, and other good things.

I enjoyed the Emacs Conference in 2013, and I think we should figure out how to have these kinds of get-togethers more often. Emacs Chats and Emacs Hangouts are tiny steps in that direction, and I’d appreciate help in making this and many other community-ish things even better. =)

2015-02-02 Imagining an Emacs conference -- index card #emacs #conference #plans #organizing-people

2015-02-02 Imagining an Emacs conference – index card #emacs #conference #plans #organizing-people

So here’s what I imagine a virtual Emacs conference might be like. People volunteer, and somehow we organize a schedule of fascinating talks. This could be a full day, or maybe we’d spread it out over a couple of half-days (maybe even scheduled for different timezones so that everyone has something they can interact with life). We use Google Hangout on Air or a similar platform that can stream and automatically record. There’s the speaker with slides and screensharing, and there’s a moderator who can pick up questions from IRC and Google Hangout in order to ask them out loud. We might even be able to pull off panel discussions. Afterwards, there’s a playlist and a webpage with all the videos/MP3s/OGGs, and people can share their notes/discussions/follow-ups.

All this is immensely doable with the technology we have today. For free, even. Anyway, the technology should be okay.

What about topics? Here’s what I’m particularly curious about:

  • New features in Emacs 25 (and beyond)
  • Demos, workflows, and setup tips for popular toolsets/needs (ex: awesome setups for Clojure/CL, Rails, Javascript, C++, Java, writing, research)
  • Fascinating uses of Emacs
  • Good practices for Emacs Lisp: automated testing, performance, reliability, coding style/idioms (maybe even workshops along these lines)
  • Demystifying cool stuff: how core modules work, how to contribute to Emacs
  • A hackathon: get package.el headers on everything! fix bugs! make improvements! document!
  • Emacs microhabits, learning
  • Workshops: intermediate/advanced use of Org Mode, Calc, ESS, and other powerful packages
  • Emacs community-building and sharing

And people can suggest other topics, too. =) Maybe we can even figure out some kind of unconference setup: people suggesting topics they can share, quickly voting on what they’re interested in, and breaking up into separate “rooms” to share/discuss.

2015-02-02 Making a virtual Emacs conference happen -- index card #emacs #organizing-people #conference #planning #questions

2015-02-02 Making a virtual Emacs conference happen – index card #emacs #organizing-people #conference #planning #questions

An Emacs conference would be awesome. Here are my (pitiful) excuses for why I haven’t figured out how to organize one yet, and things I want to figure out (especially with people’s help):

  • Who might be interested in speaking? How does one go about organizing speakers, schedules, topics, tech, etc? I’m still slowly getting the hang of reaching out to people and inviting them to Emacs Chats.
  • Will people show up and ask questions? Part of me is worried that I’ll pick entirely the wrong date/time/topics and there’ll be awkward silence.
  • How can we handle questions? IRC, probably, so that people can chat about stuff too. I think I’m pretty comfortable at keeping an eye on stuff and repeating people’s questions. Or maybe people can join the Emacs Hangout if we can get the flow to be smooth?
  • Will the experience be pleasant and worthwhile? Maybe not as goosebump-inducingly awesome as being in a room with 80+ other Emacs geeks, but I think it will be worthwhile.
  • How can we harvest and share resources? Hangouts on Air will put videos on Youtube automatically, so that’ll be taken care of.
  • What would we need to do leading up to it? Something about a mailing list, and a webpage, and lots and lots of coordination.
  • Do I need to gain experience/confidence with smaller steps? Or maybe find some accomplices?

Of course, if someone wants to organize an in-person one, that’s cool too. Especially in Toronto. That would be awesome. =) (Although I might be able to get to New York or similar places too…)

My evil plans for a conference like this include:

  • Getting cool stuff out of people’s heads/fingers/configs and into a form that other people can look at, learn from, and link to
  • Ditto for good practices that can help us develop better code (performance)
  • Discovering resources and tips we might not have found out about otherwise
  • Sparking more conversations and follow-ups
  • Spurring people to create and share more resources

What could help the Emacs community learn even faster?

2015-02-01 Accelerating the Emacs community -- index card #accelerating #emacs

2015-02-01 Accelerating the Emacs community – index card #accelerating #emacs

How can we get more people sharing their configs, or learning from other people’s configs? How can we make it easier for people to share through blog posts, videos, animated GIFs, and presentations? How can we create spaces for people to connect, either with virtual meetups or in person? How can we swap interesting ideas, workflows, and mental habits? How can we improve our skills? How can we keep the conversation going?

Mm. Figuring out how to do virtual conferences might be a good start. Also, I’ve got this idea noodling around in my head on having some kind of an intermediate/advanced Org Mode workshop: something that covers clocking workflows, table calculations, literate programming, data analysis, publishing. Figuring out how to do virtual workshops would be awesome too.

Okay. First things first. Some kind of date and some kind of time, and some kind of help sorting out a schedule. August 8 and/or August 15, maybe? If librarians can hold an online conference through Google Hangouts, we should be able to figure this out too. (Librarians are super-cool!) If you have lots of experience in organizing virtual conferences or you have ideas for how to make this less intimidating for a non-organizer-y introvert, I’d love to hear from you in the comments or at [email protected]. Let’s make this happen!

Digital index piles with Emacs: Rapid categorization of Org Mode items

Somewhat daunted by the prospect of categorizing more than a hundred sketches and blog posts for my monthly review, I spent some time figuring out how to create the digital equivalent of sorting index cards into various piles.

2015-02-01 Digital piles of index card -- index card #indexing #organization #pkm

2015-02-01 Digital piles of index cards – index card #indexing #organization #pkm

In fact, wouldn’t it be super-cool if the items could automatically guess which category they should probably go in, prompting me only if it wasn’t clear?

I wanted to write a function that could take a list structured like this:

  • Keyword A
    • Previous links
  • Keyword B
    • Previous links
  • Link 1 with Keyword A
  • Link 2 with Keyword B
  • Link 3 with Keyword A
  • Link 4

It should file Link 1 and 3 under Keyword A, Link 2 under Keyword B, and prompt me for the category for Link 4. At that prompt, I should be able to select Keyword A or Keyword B, or specify a new category.

Inspired by John Kitchin’s recent post on defining a Helm source, I wanted to get it to work with Helm.

First step: I needed to figure out the structure of the list, maybe including a sample from the category to make it clearer what’s included. org-list.el seemed to have useful functions for this. org-list-struct gave me the structure of the current list. Let’s say that a category is anything whose text does not match org-bracket-link-regexp.

(defun sacha/org-get-list-categories ()
  "Return a list of (category indent matching-regexp sample).
List categories are items that don't contain links."
  (let ((list (org-list-struct)) last-category results)
    (save-excursion
      (mapc
       (lambda (x)
         (goto-char (car x))
         (let ((current-item
                (buffer-substring-no-properties
                 (+ (point)
                    (elt x 1)
                    (length (elt x 2)))
                 (line-end-position))))
           (if (string-match
                org-bracket-link-regexp
                (buffer-substring-no-properties
                 (point)
                 (line-end-position)))
               ;; Link - update the last category
               (when last-category
                 (if (< (elt x 1) (elt last-category 1))
                     (setq results
                           (cons (append last-category
                                         (list
                                          (match-string-no-properties
                                           3
                                           (buffer-substring-no-properties
                                            (point)
                                            (line-end-position)))))
                                 (cdr results))))
                 (setq last-category nil))
             ;; Category
             (setq results
                     (cons
                      (setq last-category
                            (list
                             current-item
                             (elt x 1)
                             (concat "^"
                                     (make-string (elt x 1) ?\ )
                                     (regexp-quote
                                      (concat (elt x 2)
                                              current-item))
                                     "$")))
                      results)))))
       list))
    results))

The next step was to write a function that guessed the list category based on the item text, and moved the item there.

(defvar sacha/helm-org-list-candidates nil)
(defun sacha/helm-org-list-categories-init-candidates ()
  "Return a list of categories from this list in a form ready for Helm."
  (setq sacha/helm-org-list-candidates
        (mapcar (lambda (x)
                  (cons (if (elt x 3)
                            (format "%s - %s" (car x) (elt x 3))
                          (car x))
                        x))
                (sacha/org-get-list-categories))))

(defun sacha/org-move-current-item-to-category (category)
  (when category
    (let* ((beg (line-beginning-position))
           (end (line-end-position))
           (string (buffer-substring-no-properties beg end)))
      (save-excursion
        (when (re-search-backward (elt category 2) nil t)
          (delete-region beg (min (1+ end) (point-max)))
          (forward-line 1)
          (insert (make-string (+ 2 (elt category 1)) ?\ )
                  string "\n")))) t))

(defun sacha/org-guess-list-category (&optional categories)
  (interactive)
  (require 'cl-lib)
  (unless categories
    (setq categories
          (sacha/helm-org-list-categories-init-candidates)))
  (let* ((beg (line-beginning-position))
         (end (line-end-position))
         (string (buffer-substring-no-properties beg end))
         (found
          (cl-member string
                     categories
                     :test
                     (lambda (string cat-entry)
                       (string-match (regexp-quote (downcase (car cat-entry)))
                                     string)))))
    (when (car found)
      (sacha/org-move-current-item-to-category
       (cdr (car found)))
      t)))

After that, I wrote a function that used Helm to prompt me for a category in case it couldn’t guess the category. It took me a while to figure out that I needed to use :init instead of :candidates because I wanted to read information from the buffer before Helm kicked in.

(setq sacha/helm-org-list-category-source
      (helm-build-sync-source
          "Non-link categories in the current list"
        :init 'sacha/helm-org-list-categories-init-candidates
        :candidates 'sacha/helm-org-list-candidates
        :action 'sacha/org-move-current-item-to-category
        :fuzzy-match t))

(defun sacha/org-guess-uncategorized ()
  (interactive)
  (sacha/helm-org-list-categories-init-candidates)
  (let (done)
    (while (not done)
      (save-excursion
        (unless (sacha/org-guess-list-category sacha/helm-org-list-candidates)
          (unless
              (helm :sources
                    '(sacha/helm-org-list-category-source
                      sacha/helm-org-list-category-create-source))
            (setq done t))))
      (unless done
        (setq done (not (looking-at "^[-+] \\[")))))))

The :action above refers to this function, which creates a category if it doesn’t exist yet.

(setq sacha/helm-org-list-category-create-source
      (helm-build-dummy-source
          "Create category"
        :action (helm-make-actions
                 "Create category"
                 (lambda (candidate)
                   (save-excursion
                     (let* ((beg (line-beginning-position))
                            (end (line-end-position))
                            (string (buffer-substring beg end)))
                       (delete-region beg (min (1+ end) (point-max)))
                       (org-beginning-of-item-list)
                       (insert "- " candidate "\n  " string "\n")))
                   (sacha/helm-org-list-categories-init-candidates)))))

I’m new to fiddling with Helm, so this implementation is not the best it could be. But it’s nifty and it works the way I want it to, hooray! Now I can generate a list of blog posts and unblogged sketches, categorize them quickly, and then tweak the categorizations afterwards.

2015-02-01 Index card sketches and monthly reviews -- index card #organization #pkm #indexing

2015-02-01 Index card sketches and monthly reviews – index card #organization #pkm #indexing

You can see the results in my January 2015 review, or check my config to see if the code has changed.

My next step for learning more about Helm sources is probably to write a Helm command that creates a montage of selected images. John Kitchin has a post about handling multiple selection in Helm, so I just need to combine that with my code for using Imagemagick to create a montage of images. Whee!

Getting data from Org Mode tables

Org Mode is an amazingly powerful package for Emacs. I’ve been learning a lot about how to use its support for plain-text tables and spreadsheet calculations.

Using table data in Emacs Lisp with the :var argument

For example, I wanted to be able to define my abbreviations in an Org Mode table in my config. I remembered coming across this technique a few weeks ago, but I couldn’t find the webpage with the code. It turned out to be simple to write from scratch. Here’s the plain text I added to my config.

#+NAME: abbrev
| Base  | Expansion                             |
|-------+---------------------------------------|
| bc    | because                               |
| wo    | without                               |
| wi    | with                                  |
| ex    | For example,                          |
| qm    | [email protected]                   |
| qe    | http://sachachua.com/dotemacs         |
| qw    | http://sachachua.com/                 |
| qb    | http://sachachua.com/blog/            |
| qc    | http://sachachua.com/blog/emacs-chat/ |

#+begin_src emacs-lisp :exports code :var data=abbrev
(mapc (lambda (x) (define-global-abbrev (car x) (cadr x))) data)
#+end_src

The :var data=abbrev argument to the Emacs Lisp source block is where all the magic happens. Here, it takes the data from the table named “abbrev” (which I set using #+NAME: before the table) and makes it available to the code. Emacs evaluates that data when the code is tangled (or exported) to my configuration. The code that’s in my Sacha.el looks like this:

(let ((data (quote (("bc" "because")
                    ("wo" "without")
                    ("wi" "with")
                    ("ex" "For example,")
                    ("email" "[email protected]")
                    ("dote" "http://sachachua.com/dotemacs")
                    ("web" "http://sachachua.com/")
                    ("blog" "http://sachachua.com/blog/")
                    ("ec" "http://sachachua.com/blog/emacs-chat/")))))
  (mapc (lambda (x) (define-global-abbrev (car x) (cadr x))) data) )

Looking up data with org-lookup-first, org-lookup-last, and org-lookup-all

You can do more complex things with Org tables, too. Inspired by Eric Boyd’s talk on his Epic Quest of Awesome (which he based on Steve Kamb‘s), I started putting together my own. I made a list of little achievements, guessed at the years, and assigned arbitrary experience points.

The achievements table had rows like this:

Approximate date Category XP Description ID
2014 Life 50 Became a Canadian citizen – link L_CAN
2014 Programming 20 Used NodeJS and AngularJS for a client project – link P_NOD
2014 Programming 5 Pulled information out of Evernote

I wanted to summarize the points by year: points gained, total points, level (according to a lookup table based on D&D experience points), and description. The lookup table was structured like this:

#+TBLNAME: levels
| Total XP | Level | Adjective             |
|----------+-------+-----------------------|
|        0 |     1 | trained-initiate      |
|     1000 |     2 | experienced           |
|     2250 |     3 | savvy                 |
|     3750 |     4 | veteran               |
|     5500 |     5 | unusually experienced |

Now for the summary table. I created rows for different years, and then I used Org Mode to fill in the rest. (Org Mode! Wow.)

| Year | Points gained | Cumulative points | Level | Adjective        |
|------+---------------+-------------------+-------+------------------|
| 1997 |             0 |                 0 |     1 | trained-initiate |
| 1998 |            10 |                10 |     1 | trained-initiate |
| 1999 |            50 |                60 |     1 | trained-initiate |
| 2000 |            50 |               110 |     1 | trained-initiate |
| 2001 |           100 |               210 |     1 | trained-initiate |
| 2002 |            60 |               270 |     1 | trained-initiate |
| 2003 |           245 |               515 |     1 | trained-initiate |
| 2004 |           115 |               630 |     1 | trained-initiate |
| 2005 |           140 |               770 |     1 | trained-initiate |
| 2006 |            60 |               830 |     1 | trained-initiate |
| 2007 |           270 |              1100 |     2 | experienced      |
| 2008 |           290 |              1390 |     2 | experienced      |
| 2009 |           205 |              1595 |     2 | experienced      |
| 2010 |           215 |              1810 |     2 | experienced      |
| 2011 |           115 |              1925 |     2 | experienced      |
| 2012 |           355 |              2280 |     3 | savvy            |
| 2013 |           290 |              2570 |     3 | savvy            |
| 2014 |           350 |              2920 |     3 | savvy            |
| 2015 |            45 |              2965 |     3 | savvy            |
#+TBLFM: $2='(calc-eval (format "vsum(%s)" (vconcat (org-lookup-all $1 '(remote(accomplishments,@2$1..@>$1)) '(remote(accomplishments,@2$3..@>$3))))))::$3=vsum(@2$2..@+0$2)::$4='(org-lookup-last $3 '(remote(levels,@2$1..@>$1)) '(remote(levels,@2$2..@>$2)) '>=);N::$5='(org-lookup-last $3 '(remote(levels,@2$1..@>$1)) '(remote(levels,@2$3..@>$3)) '>=);L

The TBLFM (table formula) line is very long, so let me break it down.

Points gained:

(calc-eval
 (format "vsum(%s)"
         (vconcat
          (org-lookup-all
           $1
           '(remote(accomplishments,@2$1..@>$1))
           '(remote(accomplishments,@2$3..@>$3))))))

This uses org-lookup-all to look up the value of the first column ($1) in the accomplishments table, from the second row to the last row @2..@>, looking in the first column ($1). It returns the values from the third column of the matching rows ($3). This is then passed through calc’s vsum function to calculate the sum.

Cumulative points: vsum(@2$2..@+0$2) is the sum of the second column $2 from the second row @2 to the current row @+0.

Level: This uses org-lookup-last to find the last value where the operator function returns true. In this case, testing the level from column $3 against each of the values in the levels table’s column $1 while the given level is greater than or equal to the value from levels. When it finds the last matching row, it returns the $2 second column from it. ;N means treat everything as a number.

org-lookup-first is like org-lookup-last, but it returns the first matching row.

Adjective: This one works like Level does, but it returns the value from column $3 instead. I found that it converted the return values to 0 if I used ;N, so I used ;L instead.

Passing data to R or other external programs

Of course, you’re not limited to things that Emacs can do. I wanted to summarize the data in graphs, so here’s what I did.

#+RESULTS: category_analysis

#+name: category_analysis
#+begin_src R :var data=accomplishments :exports both :results graphics :file quest_category.png :height 300
library(plyr)
library(ggplot2)
categories <- ddply(data, c("Category"), summarize, Points=sum(XP))
cat_sorted <- transform(categories, Category=reorder(Category, Points))
plot <- ggplot(data = cat_sorted, aes(x = Category, y = Points))
plot <- plot + geom_bar(stat="identity")
plot <- plot + geom_text(aes(label = Points, x = Category, y = Points + 10, hjust=0))
plot <- plot + scale_y_continuous(expand=c(0,70))
plot <- plot + coord_flip()
print(plot)
#+end_src

I like including source code in published pages for fellow geeks, but having the results come first gives people more context for the source block. So I named the source block using the #+name: directive and defined a #+RESULTS: directive before it. The source block used the :var argument to bring the data in from the accomplishments table. With R blocks, the data becomes available as a data frame that you can then do interesting things with. I used the :file argument to save the output to quest_category.png.

Those are a few ways that you can get data out of Org Mode tables and into Emacs Lisp, other Org Mode tables, or external programs. As I learn more about Org Mode, I find myself using it for more of the things that I used to use Microsoft Excel for – tracking, analyzing, and even graphing. I found it a little difficult to piece together what I needed to do from the manuals and examples on the Web, so I hope this explanation will help you (and that it’ll help me when I forget the syntax, as I’m sure I will). If you come up with something really neat that uses Org Mode tables, tell me what you’ve figured out!

Emacs microhabit: Switching windows with windmove, ace-window, and ace-jump

When I work with a large monitor, I often divide my Emacs frame (what most people call a window) into two or more windows (divisions within a frame). I like this more than dealing with multiple Emacs frames, even if I could spread those frames across multiple monitors. I find it easier to manage the windows using keyboard shortcuts than to manage the tiling and display of frames.

One of the Emacs micro-habits I’m working on is getting better at switching between windows. When there are only two windows, C-x o (other-window) works just fine. However, when there are three or more, it can take a few repetitions of C-x o to get to where I want. I could get around that by binding other-window to M-o instead, replacing the default keymap for that. Or I could try to get the hang of other ways to move around.

Here’s an 8-minute video showing windmove, ace-window, and ace-jump:

https://www.youtube.com/watch?v=nKCKuRuvAOw&list=UUlT2UAbC6j7TqOWurVhkuHQ

Windmove lets you move around with cursor keys, if you set up the appropriate keyboard shortcuts. Ace-window works like ace-jump. In addition, you can use C-u to swap windows and C-u C-u to delete windows. Ace-jump works across windows, so that’s handy too.

Here’s my relevant code snippet for Windmove. I changed this to use define-key instead of bind-key.

(defvar sacha/windmove-map (make-sparse-keymap))
(define-key sacha/windmove-map "h" 'windmove-left)
(define-key sacha/windmove-map "t" 'windmove-up)
(define-key sacha/windmove-map "n" 'windmove-down)
(define-key sacha/windmove-map "s" 'windmove-right)
(define-key sacha/windmove-map "[left]" 'windmove-left)
(define-key sacha/windmove-map "[up]" 'windmove-up)
(define-key sacha/windmove-map "[down]" 'windmove-down)
(define-key sacha/windmove-map "[right]" 'windmove-right)
(key-chord-define-global "yy"     sacha/windmove-map)

Here’s the cheat sheet I made for myself:

2015-01-12 Emacs microhabit - window management -- index card #emacs

2015-01-12 Emacs microhabit – window management – index card #emacs

And here’s a simpler reference that you can personalize with your own shortcuts:

2015-01-18 Emacs microhabit - Switching windows -- index card #emacs #microhabit

2015-01-18 Emacs microhabit – Switching windows – index card #emacs #microhabit

Naturally, after recording the video, I thought of a better way to manage my windows. I took advantage of the def-repeat-command that abo-abo posted on (or emacs so that I could repeat keybindings easily. I modified the function to accept nil as the first value if you don’t want the keymap to run a command by default, and to use kbd for the keybinding definitions.

  (defun sacha/def-rep-command (alist)
    "Return a lambda that calls the first function of ALIST.
It sets the transient map to all functions of ALIST,
allowing you to repeat those functions as needed."
    (lexical-let ((keymap (make-sparse-keymap))
                  (func (cdar alist)))
      (mapc (lambda (x)
              (when x
                (define-key keymap (kbd (car x)) (cdr x))))
            alist)
      (lambda (arg)
        (interactive "p")
        (when func
          (funcall func arg))
        (set-transient-map keymap t))))

Here’s my new binding for yy. It lets me bounce on y to use other-window as normal, use the arrow keys to move between windows thanks to windmove, and use ace-window as well: h is the regular ace-window, s swaps, and d deletes.

(key-chord-define-global "yy"   
      (sacha/def-rep-command
       '(nil
         ("<left>" . windmove-left)
         ("<right>" . windmove-right)
         ("<down>" . windmove-down)
         ("<up>" . windmove-up)
         ("y" . other-window)
         ("h" . ace-window)
         ("s" . (lambda () (interactive) (ace-window 4)))
         ("d" . (lambda () (interactive) (ace-window 16)))
         )))

Neat, eh?

Emacs kaizen: helm-swoop and editing

This entry is part 2 of 4 in the series Emacs Kaizen

Continuing on this quest to focus on one tiny little workflow change at a time, so that I can get even better at using Emacs…

One of those packages I installed but never got around to trying out was all, which lets you interactively edit all lines matching a given regular expression. It’s like an editable occur, sorta.

It turns out that helm-swoop lets you use C-c C-e to edit matching lines interactively (so you can use keyboard macros or replace-regexp or whatever). You can type C-x C-s to save it back to the buffer.

On a related note, I’m still tickled pink every time I use dired-toggle-read-only (C-x C-q) to make a Dired buffer editable so that I can batch-rename filenames.

Developing Emacs micro-habits: Abbreviations and templates

When it comes to improving how you use Emacs, picking one small change and paying close attention seems to work well. Little things make a lot of difference, especially when frequently repeated over a long period of time. It reminded me of this quote I came across on Irreal:

I’ve gotten the hang of basic multiple-cursors-mode and I’m making gradual progress towards internalizing smart-parens by the simple approach of focusing on one tiny habit at a time. For example, I spent a week reminding myself to use mc/mark-all-like-this-dwim or mc/mark-lines instead of using keyboard macros.

Inspired by the Emacs Advent Calendar, I wanted to start a 52-week series on micro-habits for more effective Emacs use. I brain-dumped an outline of four sets (basic Emacs, Org, programming, meta-habits) of thirteen small tips each. Looking at my list, I realized there were many ideas there that I hadn’t quite gotten the hang of myself. I figured that this might be more of a project for 2016; in the meantime, I could learn by doing.

The first micro-habit I wanted to dig into was that of automating text: abbreviations, templates, and other ways to expand or transform text. I’d used Yasnippet before. I sometimes accidentally expanded keywords instead of indenting lines if my cursor happened to be at the wrong spot. But I hadn’t yet drilled the instinct of automation or the familiarity with templates into my fingers.

This blog post isn’t the easy-to-understand guide to automating text. I’ll write that later, when I’ve figured more things out. In the meantime, I’ll share what I’ve been learning and thinking so far, and maybe you can help me make sense of it.

Emacs has a separate manual for autotyping, which I had never read before. The short manual covers abbrev, skeleton, auto-insert, copyright messages, timestamps, and tempo. Did you know that define-skeleton lets you create a template that accepts multiple interregions if you call your skeleton function with a negative argument? (Interregions? What are those?) It took me an embarrassing amount of time to figure out how to mark interregions and use them. It turns out they have to be contiguous. It might be easier to think of marking the beginning of the region, marking some points in the middle, and then calling the command when your point is at the end – which is probably how most people would interpret the diagrams, but I was trying to mark discontinuous regions because that would be super-cool, and that totally didn’t work. And then I forgot that using helm-M-x means you need to specify numeric arguments after typing M-x instead of before. (I wrote about that very point in one of my blog posts, but it slipped my mind.) Once I got past that, I was delighted to find that it worked as advertised. I still haven’t imagined a situation where I would use it, but it seems like a good sort of thing to know.

What are the practical situations where text automation can help people work more effectively? I looked around to see how other people were using it. Coding, of course – especially if you use Emacs Lisp to transform the text. Debugging, too. Marking up text. Remembering parameters. Wrapping regions. Writing e-mails. Adding blog post metadata. Citing references. Lifehacker has a long list, too.

I came up with several categories I’m going to focus on so that I can more easily recognize opportunities to work better:

2015-01-05 Seeing opportunities for abbreviations and text automation -- index card

2015.01.05 Seeing opportunities for abbreviations and text automation – index card

  • Abbreviations are about typing long words with fewer keystrokes. For example, you might shorten “description” to desc.
  • Phrases are like word abbrevations, but longer. You might want to be able to expand btw to “by the way.”
  • Code benefits from expansion in multiple ways:
    • Automatically inserting characters that are harder to reach on a keyboard, like { and }
    • Being consistent about coding style, like the way many people like adding a comment after the closing brace of an if
    • Transforming text that shows up in multiple places, such as variable names that need getters and setters
    • Filling in the blanks: parameters, comments, etc.
    • Reducing the cognitive load of switching between languages by establishingq a common vocabulary. For example, I sometimes need to look up the syntax of for or the proper way to display a debugging statement when I switch to a language I haven’t used in a while
  • Templates are also useful for consistency in writing, planning, and other areas
  • Text transformation can save time and minimize error.

2015-01-04 Automating text - index card

2015.01.04 Automating text – index card

Translating the examples I’d seen to my personal interests, I could probably find plenty of opportunities to automate text while coding, debugging, writing, planning, or publishing. To dig deeper, I looked at each of the categories in detail.

Abbreviations

2015-01-06 Abbreviations -- index card

2015.01.06 Abbreviations – index card

When I was curious about typing faster, I read forum posts from people who had increased their speed by developing their own form of digital shorthand. The trick works on paper, too. When I need to write quickly or in limited space, I use abbreviations like bc for “because” and w/o for “without.” Why not on the computer as well?

I often take advantage of dynamic abbreviations when I know I’ve recently typed the word I want. To trigger those, I just have to type the beginning of the word and then use dabbrev-expand. I haven’t set up my own static abbreviations, though. Main obstacles:

  • I want to write shorter words instead of longer ones
  • In the beginning, it’s faster to type the word instead of thinking of the abbreviation and expanding it
  • If I have to undo or backspace, that makes me slower
  • If I burn this into my muscle memory, I might be more frustrated on other computers or in other apps (then again, I already customize Emacs extensively, so I guess I’m okay with the tradeoff)

Anyway, here’s a short list I’m trying out with define-global-abbrev and hippie-expand:

hw however
bc because
wo without
prob probably
st sometimes

Hmm. Let’s say that it takes me two keystrokes to trigger the expansion, whether it’s the xx keychord I’ve just set up or the M-/ I’ve replaced with hippie-expand. (Hmm, maybe a double-space keychord is a good candidate for expansion too.) Is the retraining worth a ~50% possible reduction in keystrokes? Probably not.

How about text with punctuation, so I can minimize reaching for symbols?

blog http://sachachua.com/blog/
mail [email protected]

Maybe it’s better to look at the words I frequently misspell, or that I tend to slow down then typing. I’ll keep an eye out for those.

Phrases

2015-01-06 Phrases -- index card

2015.01.06 Phrases – index card

Phrases are an easier sell. Still, I’m trying not to settle into the rut of set phrases. I should cut those mercilessly or avoid writing them from the beginning.

co check out
iti I think I
otoh on the other hand,
mean in the meantime,
fe for example
fi for instance,
oc of course
ip in particular

Code insertion

This is, fortunately, well-trodden ground. The yasnippet package comes with a large collection of snippets for many programming languages. You can start by familiarizing yourself with the pre-defined snippets for the modes that you use. For example, in my installation, they’re under ~/.emacs.d/elpa/yasnippet-20141117.327/snippets. You can use the filename (or keywords defined with key, if specified) as the abbreviation, and you can expand them with yas-expand (which should be bound to TAB if you have yas-global-mode on).

I mostly work with HTML, CSS, Javascript, Ruby on Rails, and Emacs Lisp, so this is the cheat sheet I’ve made for myself:

2015-01-07 Code insertion -- index card

2015.01.07 Code insertion – index card

For HTML, I need to remember that the tags are generally expandable, and that there are a few Lorem Ipsum abbreviations triggered by lorem.1 through .5. CSS has a v abbreviation that sets up a bunch of rules with vendor prefixes. For Javascript, I’ll probably start with f to define a function and log to output something to console.log. Rails has a bunch of iterators like eai that look interesting. As for Emacs Lisp, the pre-defined templates generally add parentheses around common functions so you don’t have to type them, and there are a few shortcuts like bs for buffer-string and cc for condition-case. I think I’ll modify the default snippets to make better use of Yasnippet’s field support, though, so that I don’t have to delete and replace text.

Templates

In addition to using text expansion for code, you can use it for planning and writing other text. I saw Karl Voit use it to great effect in my Emacs Chat with him (around the 44:00 mark), and I’ve been gradually refining some templates of my own.

2015-01-07 Templates -- index card

2015.01.07 Templates – index card

For example, here’s the template I’ve been using for sketched books. Note: If you use Yasnippet for Org Mode properties, you may want to set yas-indent-line to fixed or the fields will get confused.

Gist: sbook

# key: sbook
# name: Sketched Book
# --

**** TOSKETCH ${1:short title}
      :PROPERTIES:
      :TITLE: ${2:long title}
      :SHORT_TITLE: $1
      :AUTHOR: ${3:authors}
      :YEAR: ${4:year}
      :BUY_LINK: ${5:Amazon link}
      :BASENAME: ${6:`(org-read-date nil nil ".")`} Sketched Book - ${2:$(sacha/convert-sketch-title-to-filename yas-text)} - ${3:$(sacha/convert-sketch-title-to-filename yas-text)}
      :ISBN: ${7:ISBN}
      :BLOG_POST: http://sachachua.com/blog
      :END:

$0

***** TODO Sketchnote $1
:PROPERTIES:
:Effort: 2:00
:QUANTIFIED: Drawing
:END:

[[elisp:sacha/prepare-sketchnote-file][Prepare the file]]

***** TODO Write personal reflection for $1
:PROPERTIES:
:Effort: 1:00
:QUANTIFIED: Writing
:END:

[[http://sachachua.com/blog/wp-admin/edit.php?page=cal][View in calendar]]

****** Sketched Book - $2 - $3

$3's /$2/ ($4) ...

I’ve sketched the key points of the book below to make it easier to remember and share. Click on the image for a larger version that you can print if you want.

Haven't read the book yet? You can [[$5][buy it from Amazon]] (affiliate link) or get it from your favourite book sources.

Like this sketch? Check out [[http://sketchedbooks.com][sketchedbooks.com]] for more. Feel free to share – it’s under the Creative Commons Attribution License, like the rest of my blog.

***** TODO Post $1 to blog
:PROPERTIES:
:Effort: 1:00
:QUANTIFIED: Packaging
:END:


***** TODO Update sketched books collection
:PROPERTIES:
:Effort: 1:00
:QUANTIFIED: Packaging
:END:

1. [[elisp:sacha/index-sketched-book][Index sketched book]]
   - [[file:~/Dropbox/Packaging/sketched-books/index.org][Edit index]]
   - [[file:~/Dropbox/Packaging/sketched-books/ebook.org][Edit ebook]]
2. [[elisp:sacha/package-sketched-book][Compile]]
3. Update [[https://gumroad.com/products/pBtS/edit]]

***** TODO Tweet sneak peek of $1 with attached picture

[[elisp:(progn (kill-new (format "Sneak peek: Sketched Book: %s - %s %s" (org-entry-get-with-inheritance "SHORT_TITLE") (org-entry-get-with-inheritance "AUTHOR") (org-entry-get-with-inheritance "BLOG_POST"))) (browse-url "http://twitter.com"))][Copy text and launch Twitter]]

It’s a lot of code and I keep tweaking it as I come across rough corners, but it’s handy to have that all captured in a template that I can easily expand. =)

Text transformation

One of the advantages of tweaking text expansion inside Emacs instead of using a general-purpose text expansion program is that you can mix in some Emacs Lisp to transform the text along the way. I’m still thinking about how to make the most of this, as you can see from this half-filled note-card:

2015-01-07 Text transformation as part of expansion -- index card

2015.01.07 Text transformation as part of expansion – index card

For example, this snippet makes it easier to share source code on my blog while also linking to a Gist copy of the code, in case I revise it or people want to comment on the code snippet itself. It doesn’t use any of the built-in text expansion capabilities, but I think of it as a text expander and transformer because it replaces work I used to do manually. You’ll need the gist package for this one.

Gist: Sacha (updated to clean up region code)

(defun sacha/copy-code-as-org-block-and-gist (beg end)
  (interactive "r")
  (let ((filename (file-name-base))
        (mode (symbol-name major-mode))
        (contents
         (if (use-region-p) (buffer-substring beg end) (buffer-string)))
        (gist (if (use-region-p) (gist-region beg end) (gist-buffer))))
    (kill-new
     (format "\n[[%s][Gist: %s]]\n#+begin_src %s\n%s\n#+end_src\n"
             (oref (oref gist :data) :html-url) filename
             (replace-regexp-in-string "-mode$" "" mode)
             contents))))

Both Yasnippet and Skeleton allow you to use Lisp expressions in your template. If you don’t have all the data yet, you might consider writing another Lisp function that you can call later when you do. For example, in the sketched books code above, I have an Emacs Lisp link that composes a tweet with a link, puts it in the clipboard, and then opens up a web browser. (I do this instead of posting directly because I also want to attach an image to that tweet, and I haven’t figured out how to modify any of the Emacs Twitter clients to do that.)

So that’s what I’ve learned so far about automating text in Emacs. It’ll take me more than a week to get the hang of the abbreviations I’ve just set up, and I’ll probably need to add even more before adding and using abbreviations become true habits. But hey, maybe this will help you pay closer attention to repetitive text and editing actions in Emacs so that you can automate them too, and we can swap notes on useful abbreviations. What kind of text do you expand?

For more information, see: