Exploring my Emacs packages - yasnippet

Project homepage : http://joaotavora.github.io/yasnippet/

yasnippet is a template expansion library for Emacs. It's a great way to quickly insert code without doing a lot of typing. For example:

Simple expansion example

yasnippet also supports user-entered variables in snippets. They look a little bit like this:

Snippet expansion with variables

Previous versions of the extension came bundled with snippets, but they are no longer included with the main package. The yasnippet-snippets repository on GitHub contains templates for over 50 major modes.

Basic snippet development

Every snippet is contained in a file of its own. To create a new snippet, call M-x yas-new-snippet to open a new buffer that is set up for snippet development. C-c C-c will save the new snippet.

The snippet code for the second screenshot looks like this:

# -*- mode: snippet -*-
# name: Wrap a helper or model
# key: wrap_helper
# group: templates
# --
protected function $1() {
	if ( empty( $this->$1 ) ) {
		$this->$1 = new ${2:ClassName}();
	}

	return $this->$1;
}
$0

The $1 and ${2:ClassName} are replaced by user input when the snippet is expanded. ClassName is inserted as a placeholder until replaced by the user.

Snippet files start with a small amount of metadata, most of which is for organization. There is also an optional condition field that executes elisp to check if the snippet should be expanded or not. This can be helpful to prevent snippet insertion in certain places.

Complex snippets using elisp

Snippets can also execute elisp code contained between ` characters. For example, the current time can be inserted in a template by using `(current-time-string)`.

It takes a little while to get the hang of, but it can be used for a lot of different things:

  • Inserting the name of the current file.
  • Transforming user input. This can be used to generate ID's.
  • Prompting for input from a set list of choices via yas-choose-value.
  • Using when and unless to insert additional content depending on user input.

A very simple example of when snippet logic looks a little like this:

# key: how_am_i
Current emotion: $1

${1:$(when (string= ":D" yas-text) "You are VERY happy")}
${1:$(when (string= ":)" yas-text) "You are happy")}
${1:$(when (string= ":(" yas-text) "You are sad")}

and does this:

Snippet expansion with elisp logic

By using embedded elisp, the original snippet from screenshot 2 can be changed to a single input that is formatted automatically:

# -*- mode: snippet -*-
# name: Wrap a helper or model
# key: wrap_helper
# group: template
# --
protected function ${1:$(string-inflection-underscore-function yas-text)} {
	if ( empty( $this->${1:$(string-inflection-underscore-function yas-text)} ) ) {
		$this->${1:$(string-inflection-underscore-function yas-text)} = new ${1:ClassName}();
	}

	return $this->${1:$(string-inflection-underscore-function yas-text)};
}
$0

The ClassName parameter (which can be accessed in elisp via yas-text) is automatically converted to underscores using string-inflection-underscore-function.

Snippet expansion with embedded elisp

There are also functions for working with regions, fields and user input. The yasnippet homepage contains a in-depth guide to snippet development along with a complete function reference.


This post is part of the "Exploring my Emacs packages" series.


Groundhog Day Resolutions - October 2019

September was a tricky month, but I think it went quite well.

September's Primary Goals

1. Run a half marathon

Running a half marathon was the biggest goal I set myself for the year. I'm not an athletic person and I knew running would be a challenge for me. I set myself three times to beat, with 2:30 being the absolute quickest I felt I could do if I really pushed myself.

I finished in 2:23, better than I ever imagined I could do.

Preparation was a big part of this goal. Running was the main prep, but I also planned out meals and tested various combinations of running gear. By race day I knew exactly what to wear, what to eat before and after, and what to do for recovery. It helped make the day go much smoother and eliminated most worries during the actual run.

2. Complete two secondary goals

I released two new BlitzMax tools: blam and docgen. Blam is a build tool that I use for all my BlitzMax projects. docgen is a fairly small project that extracts documentation from source code so I don't have to type it out twice.

3. Write two chapters of my book

Two more chapters are now ready to be cleaned up and edited. The book is still just a pile of notes and rough pages at this point, but it's slowly starting to coalesce.

September's Secondary Goals

1. Write at least 3 blog posts

I didn't write as much as I wanted to, but I wrote enough to pass here.

Primary Goals for October

1. Complete the MVP of my book

This won't be the finished version, and it will still need editing, but I want the basic structure and chapters ready by November 11th.

2. Complete FOUR secondary goals

There are 13 secondary goals left for the year and 82 days to complete them in. I really need to start crossing off more goals if I want everything done by December 31st.


Exploring my Emacs packages - company-mode

What is company-mode?

Project homepage : https://github.com/company-mode/company-mode

company-mode adds auto-completion to the current buffer in Emacs. It's similar to ac-mode and supports some more modern features, such as lsp (language server protocol).

It works well out of the box, but with some additional packages and per-language configuration it can really shine.

Example packages for web development

company-web
Adds completion for web-mode, and can be used with HTML and HTM templates, although I prefer using emmet-mode for creating elements.
company-flow
JavaScript completion. Requires flow to be installed.
robe
Auto-completion for Ruby which uses a Ruby REPL to get code information. Requires the file (or project in Rails) to be reloaded when code changes.
ac-php
Adds auto-completion for common PHP extensions, as well as user defined classes and functions. It also supports annotations and type hints. It requires a special tags file to be created in order to get the most out of it, and this file should be regenerated when code changes.

Using lsp

company-lsp-mode.png

lsp is a common protocol for language servers. lsp-mode is a package that allows Emacs to communicate with lsp servers, and it supports a bunch of different languages.

Using lsp requires some extra setup as each language has its own server implementation which may need to be installed and configured. However, once it's up and running it's a fairly smooth experience.

company-lsp integrates with lsp-mode and allows company-mode to use information from running lsp servers.

Although it takes a while to configure, using lsp removes the need to regenerate tag files or reload projects when code changes.


This post is part of the "Exploring my Emacs packages" series.


Groundhog Day Resolutions - September 2019

August's GHD goals went much better than July. I'm still way behind on my 2019 goals, but I made a slight dent which has helped with motivation.

August's Primary Goals

1. Keep on running

I picked up a couple of injuries playing soccer (including a nice scar) so I decided to play it safe a few times. I still managed to run more miles than any previous month, and I tried a couple of trail runs that were way, way steeper than I expected.

2. Use a schedule every week

I've been planning my days since setting this goal. I haven't always stuck to it, but so far it's kept me fairly focused.

3. Complete at least one secondary goal

I migrated this site to a new server, set up automated builds and added functionality for scheduling posts. Everything is configured via ansible which is something I wanted to learn.

4. Write a chapter of my book

Although I didn't write as much as I wanted, I did manage to complete a chapter. I'll probably edit it again in the future, but for now I'm happy with some progress.

August's Secondary Goals

1. Read another book

I read "The Life-Changing Magic of Tidying Up" by Marie Kondo. It's quite a breezy read considering it's about tidying up, and a lot of it reminded me of the GTD process, such as performing an initial inbox sweep and making sure things have a specific place.

2. Plan a diet and recovery plan for the half marathon

The short version: Eat carbs for a few days before the race. Avoid fat and fiber the night before to reduce the risk of stomach ache.

Primary Goals for September

1. Run a half marathon

My half marathon is scheduled for the first week of October so it fits into September's goals.

To say I'm nervous is an understatement.

2. Complete two secondary goals

There are a couple of projects I've been working on that just need a little push to be complete.

3. Write two chapters of my book

At this point the only thing that is going to get this book done is work. I have my chapter plan and notes, now it's just time to make it readable.

Secondary Goals for September

1. Write at least 3 blog posts

I've slipped out of the habit of writing regularly as the year has gone on. I'd like to get back in a rhythm and publish at least 3 posts in September.

Thoughts on August

One lesson I really tried to take from "The Motivation Hacker" was the use of success spirals. The idea is to complete lots of very small actions to build motivation.

Rather than trying to get all of my goals done in the first week (or the last week like I normally do) I tried to break them into small steps that I could do every day. This strategy worked really well for writing (which is normally something I dread).

Scheduling tasks also helped enormously, although I wasn't as disciplined with times as I should have been.

I'm still way behind on my goals, but I'm hoping once The Run is out of the way I'll have more time (and energy) to focus on them.


Groundhog Day Resolutions - August 2019

July was probably the most disappointing month I've had doing GHD so far. Nothing really went as planned and I was nowhere near as productive as I wanted to be. Outside of running I didn't make a dent on any of my primary or secondary goals for the year. 2019 is nearly 2/3rds complete and I'm way, way behind where I wanted to be.

July's Primary Goals

1. Run a total of 35 miles

I fell a couple of miles short on this, but I'm still counting it as a success considering I ran more miles in July than any month ever. I also swam and did some light yoga once a week.

Running went ok, but the large increase in miles has left me tired and aching. Recovery has also been a challenge and it takes me nearly an hour to get from "run finished" to showered and ready to carry on with the day.

2. Finish the "Exploring my Emacs packages" series

I wrote three rough drafts but didn't edit or publish them.

3. Finish one other goal on my "Other Goals" list

I didn't release anything.

July's Secondary Goals

1. Read another book

I read "The Motivation Hacker" by Nick Winter.

2. Add a page for tracking my bodyweight workouts

I came up with a couple of designs for the page but didn't actually build it.

Primary Goals for August

1. Keep on running

Miles are going to increase for August as run day gets closer. My average pace is currently too slow for a 2:30 finish, but I'm hoping it will improve as things cool down.

2. Use a schedule every week

I've used weekly schedules before with mixed results. I'd like to see if using one for August improves how much I get done.

3. Complete at least one secondary goal

This didn't work out so well for July, but I need to try again if I want to make any kind of impact on my other goals list.

4. Write a chapter of my book

At this point I'm pretty comfortable with what I should be writing about. I need to actually get it done.

Secondary Goals for August

1. Read another book

I started reading "The Life-Changing Magic of Tidying Up" and I'd like to finish it this month.

2. Plan a diet and recovery plan for the half marathon

I'd like to have the following figured out before I run:

  1. What to eat in the days leading up to the event
  2. What to eat pre and post run
  3. What to drink before/during/after

I've tried a couple of sports drinks in the last few weeks, but some of them contain more sugar than a can of soda which seems a bit too much to me. A lot of people have recommended coconut water - I'm not keen on the taste, but I think it could be used as a base instead of water and sugar.

Thoughts on July

Half-marathon training has been challenging. Below is a chart of my miles-per-week for all of 2019.

runkeeper-2019-08-07.png

It's been quite a jump from my regular schedule. I have to keep reminding myself that a year ago I couldn't even run 1/4 of a mile without stopping and now I'm running 4 miles in one go. It's not a huge distance, but it's a start.

Outside of actual running I've been trying to find information on recovery, either via stretches, diet or supplements. It takes time to find decent information, and there are a lot of "is said to improve" articles that don't really help.

One thing that has helped is wearing compression socks post-run (after showering). I bought a pair of 20-30mmHg socks which has reduced post-run aches in my calf muscles. It feels a bit like a gentle massage when walking around.

I read "The Motivation Hacker" which has some interesting techniques for increasing motivation. Some ideas I already know and use, but there were a few new ones. I'd like to read it again and start applying some of the ideas.

My GTD setup has also been on a bit of a wobble the last few weeks. A lot of tasks I've been working on haven't been added to the system and my next actions list has been somewhat ignored.

Even worse, I've gone back to old habits of scribbling urgent and tasks on note cards. I'm hoping that implementing short daily reviews and scheduling tasks will improve this. I might have to set up a Beeminder goal for creating schedules until it's all working.

Because of this I've built up a bit of a backlog. Some of the tasks on my next actions list have been there for over a month. I think part of the problem is that they're too big and should be broken into smaller tasks as part of a project.