May 19, 2018 at 11:37 AM by Dr. Drang
As I’ve written before, I use the TaskPaper plain-text format to keep track of my to-do lists. In particular, I have a main to-do list that I adjust every day, adding new projects and tasks as they come in and (I hope) marking older tasks as completed. At the beginning of every month, I make a new TaskPaper document, filling it with the uncompleted tasks from the previous month. In this way, the documents from the previous months are a record of what I was planning to do and what I finished.
On the Mac, I use Hog Bay Software’s TaskPaper app. Jesse Grosjean of Hog Bay created the TaskPaper format, and his app is still the best for creating and organizing TaskPaper documents. Unfortunately, Jesse stopped supporting his iOS TaskPaper app, and I haven’t thought much of replacements like Taskmator. As a result, I’ve been doing almost all of my TaskPaper tracking on the Mac, which isn’t the best way to keep on top of things, especially when I’m traveling.
But with a little bit of work, Drafts 5 can become a great TaskPaper app. Out of the box, it understands the project and task syntax and formats them nicely, including a strikeout for tasks marked with the
I created an action group that lets me do common manipulations of TaskPaper lists in Drafts. Be warned: by “common” I mean “common to me.” TaskPaper is a very flexible format, and I know a lot of people do things with it that I don’t. The actions in the group I made are tuned to my use. If you’re a TaskPaper user, some of them will be useful to you and some of them won’t. But I hope that even the actions that aren’t immediately useful to you will help you develop your own.
Let’s start with actions that upload and download my current monthly task list. I need this because I still edit this list on my Mac, so having it exclusively in Drafts isn’t an option. The current month’s task list is saved in a file named
yyyy-mm.taskpaper in the
Elements/tasks subfolder of my Dropbox folder,1 where
mm represent the current year and month.
The Upload Dated action (which appears as just Upload in the keyboard row) has just a single Dropbox action, reflecting the file name and folder choices:
The file name is set using template tags for the current date, and the action overwrites whatever is in that file with the contents of the current draft.
I think this is mostly self-explanatory. To me, the most interesting part is handling the month number in Lines 9 and 10. The
padStart function to add a leading zero to single-digit months. I’m not sure when
With the upload/download actions out of the way, let’s move on to the actions that manipulate the lists themselves.
We’ll start with New Item. When you’re adding a series of tasks to a project, Drafts helpfully inserts the tab-hyphen-space at the beginning of the line. but it doesn’t do that for the first task in a newly added project. The New Item action is a simple text insertion action that adds those three characters at the current insertion point. Nothing special, but it does help, especially when working with the software keyboard, which doesn’t have a Tab key.
Next is the Mark Done action, which adds a
@done tag and the current date to the end of the current line or the end of all the selected lines if text is selected. Mark Done is smart enough to ignore project lines.
Lines 4–10 get today’s date and use it to format the marker to appear at the ends of the lines. It looks like this:
The TaskPaper app for Mac has a setting for adding the date to
@done tags, and I use it because I like to be able to look back to confirm when I finished something.
Lines 12–13 get the selected lines (which is just the current line if there is no selection) and put the text from those lines into the variable
sel. The trailing newline is omitted.
Lines 17–23 split
sel into an array of lines and loop through that array, adding the done marker to each task line. Task lines are distinguished from others by the tab-hyphen they start with.
Lines 26–29 clean up by replacing the originally selected lines with the altered text from the previous step. The cursor is then placed at the end of the last selected line.
The Marked Waiting action is basically the same as Mark Done except it puts a
@waiting tag at the end of each selected task line.
Archive Done mimics another feature of the Mac TaskPaper app. It goes through the list, finds all the tasks marked
@done and adds them to an Archive section at the bottom of the document. The archived tasks are marked with the name of the project they came from (in a
@project tag at the end of the line), and they are placed at the top of the Archive section. If there isn’t already an Archive section, one is created.
Here’s how it works. Let’s go back to the task list I showed at the top of the post:
As you can see, there are a couple of done tasks in the Administration project. After running the Archive Done action, the draft looks like this:
Here’s the script step of Archive Done:
Lines 4–10 set up a bunch of variables for later use. Lines 13–20 find the Archive section and save all of its lines to the variable
archive (which was initialized to an empty string). They also set the
archStart variable to the character index of the beginning of the Archive section. This will be used later to determine the end of the active (unarchived) tasks. If there is no Archive section,
archive remains blank and
archStart is the end of the document.
Lines 24–42 are the heart of the script. They put the text of active section of the document into the
active variable, turn it into an array of lines, and loop through the lines, looking for done tasks. Whenever a project header line is encountered (Lines 29–32), the
project variable is updated to keep track of the current project. Done tasks (found in Line 34) are extracted and marked with the current project name and added to the front of the
archive variable (Line 37)
When the looping is done, the active and archive sections are concatenated and the draft is replaced with the result. The cursor is put at the top of the draft.
One last action. As you can see in the screenshots, it’s my habit to have no space between the projects. I think the formatting is sufficient to make the list readable without extra whitespace.2 This does, however, make rearranging projects difficult in Drafts. Drafts has a rearranging mode, but it won’t work on a project-by-project basis unless there’s a blank line between the projects.
The Un/Block action addresses this problem by adding or deleting blank lines between the projects. It toggles between the tightly spaced format I like and the blank-line-separated format needed to use Drafts’s block rearrangement. When I want to move a project to the top of the list to make it more prominent, I run Un/Block to add blank lines between the projects, use Drafts block rearranging to drag the projects into the order I want, and then run Un/Block again to get rid of the extra blank lines. Un/Block is smart enough to maintain the blank line above the Archive section.
Here’s the script step for Un/Block:
Lines 4–6 define a set of regular expressions used to find project headers under different conditions. Line 13 replaces all “tight” project headers with “loose” ones. If that replacement didn’t do anything (Line 16), the spacing must already be loose, so Lines 17–18 replace the loose spacing with tight (except for the extra line above the Archive section).
I’ve been using these actions for over a month, and I’m pretty happy with the way they work. The functionality has remained the same for the past few weeks, with most of the effort going into simplifying the actions to make them easier to understand and maintain. As I say in the Drafts Action Directory, these actions are idiosyncratic, but I hope they can be used by others to make actions that fit their TaskPapering needs.
This subfolder choice is historical rather than rational, and reflects my long-ago use of the iOS plain text editor, Elements, which required an
Elementsfolder. I suppose I should reorganize things, but I’ve never bothered. ↩
The Archive section is set off from the rest by a blank line. This is something the Mac TaskPaper app does, and I mimic that behavior in Drafts. ↩
May 6, 2018 at 2:06 PM by Dr. Drang
Drafts has long had an easy way to save a draft to Dropbox: You use the first line of your draft as the file name and make a action with the Dropbox step set up something like this:
Drafts knows, through its template tag system, that
safe_title is the first line of the draft with troublesome characters removed (you can also use the
title tag, which takes the first line as-is). The Dropbox path is the slash-separated list of subfolders to your main Dropbox folder. Here, I’ve shown it as a fixed, literal path, but you can also use tags to choose a subfolder by date, another line in the draft, or even the expansion of a TextExpander snippet.
But what if you don’t want the first line of your file to be the title? For example, when I write a blog post, the system of scripts that do the publishing expect the header lines of the Markdown source code to look like this,
Title: A line-numbering action for Drafts Keywords: drafts Summary: A script/action for numbering lines of code in a style that works on this blog. Date: 2018-05-05 18:31:02 Slug: a-line-numbering-action-for-drafts
and they expect the source to be a file with the name
<slug>.md in the
yyyy/mm subfolder of my blog’s source directory, where the slug is taken from the Slug line and the year and month are taken from the Date line. To publish from my iPad, I need to save the draft to that spot with that file name.
I do this with a two-step action. The first step is a script that gets the required information from the draft:
Line 4 pulls in the entire contents of the current draft. Lines 7 and 8 establish regular expressions for extracting the year, month, and slug from the header lines. Note the capturing parentheses in Line 7 collect the year and the month, and the capturing parentheses in Line 8 collect the slug. The
m flag at the end of each regex stands for “multiline,” which means the
$ represent the end of a line, not the end of the entire text string being searched.
Lines 11–14 run the date regex on the text, pull out the year and month, and construct the path to the subdirectory where the file will be saved. Line 17 extracts the slug.
Finally, Lines 20 and 21 create the
blog_slug template tags, which we’ll use in the next step of the action. The
The second step in the action is similar to the simple Dropbox step shown above. This time, though, we’re using the tags defined in the script.
Now I can write my blog posts with the same format I’ve used for years and can upload them to Dropbox in a single step when I’m ready to publish.
This action is very handy, but it’s specific to one use case. I wanted a more general solution for saving other types of files—LaTeX files, Python scripts, whatever—to Dropbox. In particular, I wanted flexibility in where the path and file name could be located within the file, and I wanted them to be easy to write and easy to identify when reading.
I decided to steal the idea of modelines from Vim and adapt it to file names and paths. Somewhere in the draft, I put a line—which I call a fileline—that contains
and that provides both the path and the file name for where the draft should be saved. Typically, these will be inside comments, so the draft of a Python file will start with
#!/usr/bin/env python # dbox:/path/to/subfolder/filename.py
and a LaTeX file will have
somewhere in the header.
Like my blog post saving action, the action that uses these “filelines” to determine where to save the draft consists of two steps. First, the script step that creates the template tags,
and then the Dropbox step that uses those tags for saving,
The script follows the same outline as the blog post saver, but the regex in Line 7 is worth an extra look. It takes advantage of the fact that the
+ quantifier is “greedy.” So the
dbox:(.+)\/ part of the regex captures every character after “dbox:” and before the last slash on the fileline. So no matter how many subdirectories deep I want to save this draft, they’re all captured in
fileline. Only the file name is captured in
This action is available to download and install from the Drafts Action Directory.
There are ways to do this without using scripting. You could, for example, make a header with the file name in the top line, the directory in the second line, and a blank line separating the header from the rest of the contents. With the proper template tags, you could set up a single-step action that saves only the remainder of the draft, not its entire contents.
I’ve put this in the Drafts Action Directory, too, calling it Header Save. Although it’s simple and easy to understand, because the header isn’t saved to Dropbox, there’s a difference between the contents of the draft and the contents of the saved file. I use the fileline solution because I prefer a system in which the draft and the saved file are the same.
May 5, 2018 at 6:31 PM by Dr. Drang
Among my many deficiencies as a human is my failure to write a review of Drafts 5. It will come eventually, but in the meantime, I’ll put out a few posts on how I’m scripting and configuring Drafts to be my primary text editor on iOS for both short snippets (which is how I’ve used it for years) and longer pieces like reports and blog posts. If you want to see a real review of Drafts 5, I’ll suggest
Because so many of my blog posts include source code with line numbers, I need my text editor to have a line-numbering command. Drafts 5 has built-in a way of displaying line numbers,1 but I need the numbers to part of the actual text. The action I made for doing this is called, with the great imagination I’m known for, Number Lines, and you can get it from the Drafts Action Directory.
The action consists of a single step, which is this script:
sprintf. So the
rjust function in Lines 6–14 is there just to right justify the line numbers.
Update May 5, 2018 9:56 PM
padStart method that will handle the right justification of strings. So the
rjust method wasn’t necessary, and line in which it’s used can be changed to
I’ve left the original code here in the blog post, but the Drafts Action Directory has been updated.
This tells me a couple of things:
- Code in other actions I’ve written that zero-pads numbers to ensure two digits for month and day numbers can be simplified with
padStart(it has an optional parameter for the padding character).
Lines 17–21 get either the selected text or—if no text is selected—the entire draft. This is the text that will have line numbers added to it.
Line 26 splits the text into an array of lines so we can loop through and add line numbers to each one. Line 27 gets the length of the array,
numLines, which we’ll use as a limit in the
for loop that starts on Line 31.
Before we start the loop, though, we need to know how wide (in characters) the line numbers will be. I could do this by turning
numLines into a string and getting its length, but there’s no mathematical fun in that. Instead, in Line 28, I took the integer portion of the base-10 logarithm of
numLines and convert it to base 10 by multiplying it by the natural log of 10. Who says junior high math doesn’t come in handy?
Inside the loop, we prefix each line with the right-justified line number, a colon, and two spaces (we’ll get to the colon and the spaces in a minute), and
push the resulting string onto the end of the
numbered array, which we initialized as empty in Line 29.
With the loop finished, we join up the elements of
numbered with a newline character and replace the selection in the draft with the numbered lines.
I’ve been using Drafts 5 for almost two months now (I came in late to the beta), and I’ve been slowing building up and refining a set of actions to help me use it for nearly all of my iOS writing. Some of them are so customized for my way of working that they won’t be immediately useful to anyone else. But I’ll still post them, as they give a sense of what Drafts 5 is capable of, and they can be the starting point for your own scripts.
May 1, 2018 at 8:53 PM by Dr. Drang
It’s been a while since I’ve operated this here blog, so let’s get back into the swing of things with a post that nearly writes itself.
Apple announced its quarterly sales today. You can get loads of charts on all the data from the usual sources. Revenue was up significantly, but I’ve always been more interested in unit sales because it’s the number of Macs and iOS devices out there that provide the market for third-party developers who make the software I need to use these machines.
Here are the unit sales for the three products Apple reports separately. As always, the dots show the actual quarterly sales and the line is the four-quarter moving average. Also, I plot real dates along the horizontal axis, not Apple’s goofy fiscal quarters.
This doesn’t show the old iPhone sales growth, but it’s steady. Certainly not the precipitous dropoff that was being predicted some weeks ago from “supply channel sources.”
Because the iPhone dominates unit sales, the chart above isn’t particularly good at showing what’s going on with the iPad and the Mac. Here’s the iPad by itself:
The iPad has managed to pull off an entire year of growth after bottoming out this time last year. As with the iPhone, it’s not stellar growth, but it’s welcome after the previous three years. It would be nice to see it always over the 10 million threshold again.
(You’ll note that since becoming an iPad user, I’ve stopped being snarky about its no-longer-world-beating figures and have started rooting for it. That’s what happens when you have a dog in the fight.)
Here’s the Mac:
Bleh. While iMacs seem to be in good shape, the MacBook line, which has usually driven sales, has little to recommend it. The MacBook Air has wheezy old specs that make many prospective buyers fear they’d be getting instant obsolescence. The MacBook Pro has a keyboard with serious reliability problems and a feature, the TouchBar, that neither Apple nor third-party developers have done much with. The unsuffixed MacBook is, I think, a nice machine, but I’m not sure many users are willing to pay a premium for an extra-light notebook (that’s somewhat underpowered) when they can get an iPad.
The real news of Apple’s growth is coming from services and the elusive “other” category of products, and that’s been the case for a while. These categories don’t have unit sales figures, so I’ve kept away from them. Maybe I should rethink that.