Before I started writing the book I had a single text file that I collected ideas in. This worked okay in the beginning, but once things started moving along it became unwieldy to manage.
I switched to keeping my notes in zetteldeft which made life much easier. There
is a single note titled "Writing PHP with Emacs" which I use as an entry
point. All related notes are linked to this one, and I also tag them with
#php-with-emacs so I can quickly search for them.
Not all of these notes are directly related to the book, but they might contain information that I want to include in the future.
The entire book plan is stored in a single
TODO.org file in the project
directory. I use it to keep a list of the overall contents, as well as
planning out individual chapters.
The plan looks a little like this (with content removed for brevity):
#+TITLE: PHP with Emacs - TODO List #+SEQ_TODO: TODO STARTED TO-EDIT | COMPLETE POSTPONED CANCELLED * TODO Milestones [4/5] ** COMPLETE Version 0.2 CLOSED: [2020-06-09 Tue 19:55] DEADLINE: <2020-05-31 Sun> *** COMPLETE php-mode: Customization - Using TAGS files [3/3] CLOSED: [2020-05-30 Sat 10:26] SCHEDULED: <2020-05-28 Thu> :PROPERTIES: :EFFORT: 1:00 :END: Prefer to use php-lsp, but this is an option too. - [X] What TAGS files can be used for - [X] Generating - [X] Navigating using a TAGS file Shortcuts and everything go here. * TODO Backlog [1/22] ** TODO Configuring a project for WordPress ** TODO Configuring a project for Drupal
org-columns for quickly viewing the list of milestones. From this view I
can set estimates and view time worked on each milestone and sub-task.
Backlog heading keeps a list of all future content that I want to work
on. When it's time to move an item to the plan, I'll refile it to a milestone
using a custom function called
(defvar pn/org-last-refile-marker nil "Marker for last refile") (defun pn/org-refile-in-file (&optional prefix) "Refile to a target within the current file." (interactive) (let ((helm-org-headings-actions '(("Refile to this heading" . helm-org--refile-heading-to)))) (save-excursion (helm-org-in-buffer-headings) (org-end-of-subtree t) (setq pn/org-last-refile-marker (point-marker)))))
I use this instead of
org-refile as I have a lot of org files that I don't
want to be used as targets. Once a headline has been moved to a milestone I'll
add a time estimate and schedule it. I use org-projectile so any scheduled tasks
in the book TODO show up in my agenda.
The project directory contains the
manuscript directory, which is used by
Leanpub to generate the book. It also contains a lightweight Emacs user
directory which I use when generating screenshots.
I use binder to keep a list of manuscript files.
binder also supports adding
file-specific notes, although I prefer to keep things in my
For most navigation I'll use
binder is helpful when trying
to get an overall look at the book or when I'm jumping between chapters.
Leanpub has its own offshoot of Markdown called
Markua, so everything is
written and edited using
markdown-mode+. I would have preferred to write in
org-mode - and there is an
ox-leanpub exporter - but I didn't want to add an
extra step to the write -> publish process. Maybe next time.
One of the things that caught me out at the start was line breaks. Markdown will
normally ignore a single line break, so hard-wrapped lines will still be treated
as a single line.
Markua treats them as hard line breaks, so it altered the
look of the book and made pages too narrow. I switched to
which wraps lines visually but doesn't alter the text.
I use a
.dir-locals.el file to automatically enable specific modes and to
increase the font size.
((markdown-mode . ((eval . (progn (turn-off-auto-fill) (text-scale-set 1) (turn-on-olivetti-mode))) (fill-column . 80) (visual-fill-column-width . 80) (mode . flyspell) (mode . binder) (mode . whitespace-cleanup) (mode . visual-line) (mode . visual-fill-column))))
The modes I use are:
flyspell– Enables real time spell-checking in the buffer.
binder– Enables binder navigation for moving forwards and backwards between chapters.
whitespace-cleanup– Strips trailing whitespace when saving a buffer.
visual-line– Enables soft word-wrapping.
visual-fill-column– Adds word-wrapping at a specific column, instead of the window's edge.
The code in the
eval block turns off hard-wrapping, increases the font size,
olivetti-mode. olivetti centers the text in the current window
which I prefer when writing words.
For taking screenshots, I have a separate Emacs configuration that contains just PHP and web-specific packages. It loads only the packages I want, and sizes the window/fonts to a width that fits on the page. Startup looks like this:
emacs --no-init-file --load "emacs-env/init.el"
--no-init-file= option tells Emacs to ignore my usual
init.el file, and
then I manually load the screenshot initializer.
The "standard" Leanpub plan allows the use of git for managing book files. I use
git every day, so the workflow is something I'm very accustomed to. The
branch contains content that will be published, and WIP content is stored in a
named branch (usually something like
Once content is ready I'll push it to the remote repository and then manually publish it from within Leanpub. There is an API, but that's reserved for the "PRO" plan.
I have two Beeminder goals set up for the book:
- write-php-book – This goal is updated any time I make a commit to the book repository. This makes sure I'm writing often enough to get things done.
- publish-php-book – Leanpub can notify a webhook when a new version is published, so I use this to add a data point to Beeminder. This makes sure I'm publishing changes I write instead of waiting until things are "perfect".
I originally started with a word count goal. This worked well when I was trying to write all of the initial content, but once I started editing it made more sense to change to a goal that didn't focus on raw words.