Werden wir Helden für einen Tag

Home | About | Archive

Advent of emacs #9: How I type formulaic things in emacs

Posted on Dec 9, 2022 by Chung-hong Chan

I admit it now: at least 50% of the things I write are formulaic.

Programming is actually a pretty formulaic thing. Using R as an example, visualization using ggplot is actually pretty repetitive. Here are some ggplot snippets I obtained from the book ggplot2: Elegant Graphics for Data Analysis by Hadley Wickham.

ggplot(mpg, aes(x = displ, y = after_stat(count))) + geom_histogram()

ggplot(mpg, aes(displ, hwy)) + 
  geom_point() +
    facet_wrap(vars(year))

ggplot(mpg, aes(drv, hwy)) + 
  geom_hline(yintercept = 28, colour = "red") + 
    geom_boxplot()

ggplot(faithfuld, aes(waiting, eruptions)) + 
  geom_raster(aes(fill = density)) + 
  theme(legend.position = "none") + 
  labs(x = NULL, y = NULL)

If you know the package, you might know that these snippets generate different plots. But in usual cases, one can see that an execution of the function ggplot must have an aes in it. Also, in more than 99% of the situations, there will be at least one geom_* following it.

SQL is also like that. Here are some select statements I extracted from here:

SELECT DISTINCT Country FROM Customers;
SELECT COUNT(DISTINCT Country) FROM Customers;
SELECT * FROM Customers WHERE Country='Mexico';
SELECT * FROM Customers ORDER BY Country DESC;
SELECT Orders.OrderID, Customers.CustomerName FROM Orders INNER JOIN Customers ON Orders.CustomerID = Customers.CustomerID; 

The keyword select must be followed by something (even *) and then the keyword from; it also must be followed by something, with an optional where. These things are what we called boilerplates. Even for writing prose, there are many formulaic expressions: “future studies are still warranted”, “mit freundlichen Grüßen”, “we’ve decided to move forward with another candidate.” The least mentally stimulating part of any content creating (including programming) is probably filling those boilerplates; or when worse, writing those boilerplates. I always think about my tasks inside emacs in terms of identifying boilerplates. It is because emacs is extremely good at handling this kind of boilerplate filling tasks and one can greatly improve the efficiency if one can identify these boilerplates and generalize them as snippets.

yasnippet

I created some snippets using yasnippet by João Távora et al. The idea is to type a small “key” (a short keyboard sequence) and then press the tab key to expand the key to a boilerplate. This is a demo in Org mode to insert an emacs lisp code block.

There have been many, many, many tutorials on how to use yasnippet.

I want to show you how I use yasnippet:

Keep my own repository of snippets

I maintain my own snippets. For a while, I have used the snippets created by someone else, e.g. yasnippet-snippets by Andreas Crotti et al. But I switch to maintain my own repository. The collection is smaller, but there are two advantages.

  1. The snippets are created by myself, it must suit my own need.
  2. I can put the snippet repo alongside my dotfiles in my Github.

Even if you really like yasnippet-snippets, I recommend copying snippets from there to your own (but friendly reminder: those snippets are licensed under GPL3).

helm-c-yasnippet

helm-c-yasnippet by Emacs JP integrates yasnippet with Helm. If you have a lot of snippets, it is a great way to discover them. But I mostly use it for the command helm-yas-create-snippet-on-region. Before I dive into my way of creating a snippet, I have this in my configuration file.

(use-package yasnippet
  :init
  (yas-global-mode 1)
  (setq yas-snippet-dirs  '("~/dev/dotfiles/my-snippets"))
  )
(use-package helm-c-yasnippet
  :init
  (setq helm-yas-space-match-any-greedy t)
  (global-set-key (kbd "C-c y") 'helm-yas-complete)
  :after yasnippet
  )

The above code does two things: 1) point the variable yas-snippet-dirs to my repository of snippets, 2) bind C-c y to the command helm-yas-complete. Punkt.

How do I create and use a new snippet

I create a new snippet by first selecting a region I want to create a snippets. For example, this C++ code

And then I press M-x helm-yas-create-snippet-on-region, and give this snippet a name. In this case: “cout”.

Accoding to the snippet syntax, I modify the changing part to a “tab stop field” ($1) and where the cursor point should be in the end ($0). Then save it.

And try to it: typing cout, press tab, 3.14, press tab.

My R snippets

I particularly like 3 of my snippets. I have a snippet called rio:

rio::import("$1") %>% tibble::as_tibble() $0

And to be honest, I only use the package to do this 99% of the time. I also have another snippet called hehe

here::here("$1") $0

I also modify a testthat snippet from yasnippet-snippets to fit my programming style

test_that("$1", {
    expect_${2:equal}($0)
})

Moving on

Tomorrow (day 10) I will talk about themes. I use that as a Pause (a break).


Powered by Jekyll and profdr theme