06.25.06

Details on the GTDAlt bundle

Posted in TextMate, ruby/rails, GTD at 12:17 am by Haris

IMPORTANT UPDATE: The GTDAlt bundle now lives in the Subversion Repository of Bundles for TextMate. You can find details on how to access bundles this way here. The version on my website linked to below will NOT be updated any more.

I apologize for the inconvenience to those of you not using subversion, but I can assure you that following the above instructions is pretty painless, and the benefits to my sanity and general state of mind are too numerous to list here.

Update: You can find a more recent description of the workflow while working with the GTDAlt bundle here. It’s a work in progress.

This is a general help file for the GTDAlt bundle, one of the two bundles for implementing GTD in TextMate. You can also access this file from within the TextMate bundle. This file consists of three parts. The first part is an introduction to the bundle, describing how to use it. The second describes its internals, describing how to extend the bundle. The third is an example of creating a new command.

You can also find some pretty lame screencasts here, as well as a converter to convert your Kinkless GTD project to this format here.

Using the GTDAlt bundle

First of all, let’s describe the GTD format. Here is a typical file:


project World domination
  #completed:[2006-06-20]  @office Assemble email address of world leaders
  @errand Create giant laser beam
  @email Threaten to destroy Barbados
  @work Take over world
end
@email Hello there
project testing project
  project Some subproject
    @homework first action  due:[2006-07-20]
    @email another action
    @email a third action
  end
  @home Hurray [1] due:[2006-07-04]
  @home second action [2] due:[2006-06-04]
  @errand a new one [3]
@email a third action
end
[1] A note here
[2] Another note
[3] A third note

As you can see it has a very simple minimalistic style. Projects start with a project line, and end with an end line, and they can be nested. Actions start with an at-sign (@), which is then followed by the context. Contexts can contain any non-space character. Then comes the name of the action, which is followed by two optional parts. The first one is a bracketed number, and signifies the presence of a note for the action. Notes are all at the end, and can contain links in brackets. The second optional part is a due date, in the format due:[yyyy-mm-dd].

Now let’s describe the workflow for working with a gtd file:

  1. You create a new project via a snippet with shortcut the exclamation point ! (shift-1).
  2. You create a new action by typing shift-2 (@). This sets a default context (email) which can then be changed as described in the next steps. At this stage you only enter the name of the action.
  3. There are three ways to change the context. You could of course just select it and type in. You can also press ctrl-C anywhere in the line, which changes the context to the next context in alphabetical order. Also ctrl-opt-C offers a pop-up of all contexts to pick from. These contexts are assembled by scanning all gtd files in the given directory, or by simply looking at the environment variable TM_GTD_CONTEXT, space-separated.
  4. Pressing ctrl-{ (ctrl-shift=[) adds a note. If a note is present, it takes you to the note for editing. The same keypress takes you back to the action.
  5. Pressing ctrl-? (ctrl-shift-/) shows the note of the action in the current line as a tooltip.
  6. Pressing # (shift-3) adds a due date to the current line’s action. A popup shows up, asking you to enter a date. You can either enter a date in a standard format, or you can type something like “today”, “in 2 months“ or “next tuesday”.
  7. Once a due date has been set, you can either completely change the date by the exact same keypress, which will allow you to enter a new date, or you can use one of a series of commands for adjusting the date:
    • shift-, (<) reduces the date by one day.
    • shift-. (>) increases the date by one day.
    • ctrl-, reduces the date by one week.
    • ctrl-. increases the date by one week.
    • ctrl-shift-, (ctrl-<) reduces the date by one month.
    • ctrl-shift-. (ctrl->) increases the date by one month.
  8. You can mark a line as completed by pressing cmd-/. This marks it as the second line in the example above. Pressing it again unmarks it. These two also work with a whole selection of lines, even a selection crossing among various projects.
  9. opt-cmd-/ cleans up the current project, moving information to the file GTD.gtdlog in the current directory. This takes care of all completed actions, as well as completed tasks.
  10. ctrl-L opens all links in the current line to the current browser.
  11. ctrl-shift-W surrounds the current selection in a project.
  12. Finally, pressing % (shift-5) asks you for a context, and then creates an HTML page with a list of all actions for that context, along with their due dates if any, and the projects they belong to. Each action and project is a link to its file location. Design improvement suggestions for this page are strongly encouraged.
  13. ctrl-opt-P allows you to move to another project, by showing you a pop-up of all projects from all gtd files. Of course you could use the “Go To Symbol” pop-up (cmd-shift-T), but this only shows the projects in the current file.

Finally, the commands in the bundle work by default with the current directory. You can ask them to work with another special directory by setting the variable TM_GTD_DIRECTORY.

The internals of the GTDAlt bundle

Each command should start with the three lines:

#!/usr/bin/env ruby
require ENV['TM_BUNDLE_PATH']+"/bin/GTD.rb"
include GTD

This sets up the ground-work for working with the GTD module. There are a number of classes in the GTD module, let’s look at them one at a time:

GTDFile

This is the basic class. It has a number of useful class methods, as well as instance methods. A new instance method is created with a command like:

obj = GTDFile.new(filename)

where filename is a path to a gtd file. For instance to load the current file you would use:

obj = GTDFile.new(ENV['TM_FILEPATH'])

This processes the current file and returns an object (instance of the GTDFile class). You can query this object for data on the gtd file. For instance:

  • obj.projects returns an array of projects (Project objects).
  • obj.actions returns an array of actions (Action objects).
  • obj.notes returns an array of notes (Note objects).
  • obj.completed_actions returns an array of completed actions (Action objects marked as completed).

You can recover the text corresponding to file by using obj.dump_object. This can for instance be used to reproduce the text after the various completed actions have been removed. The call obj.cleanup_projects removes completed actions and projects (a project is completed when all its actions and subprojects are completed). It adds logging information to the class MyLogger, which you can recover using MyLogger.dump. Check the “Clean-up current file” command to see how this is used. Finally, obj.actions_for_context(context) returns all actions with a given context. All this does is:

return self.actions.find_all {|a| a.context == context}

There are a couple of class methods that allow work with an entire directory.

  • self.process_instructions processes all files in the current directory (or the one pointed to by TM_GTD_DIRECTORY or the one specified as an extra argument). It returns an array of objects, one for each gtd file in the directory.
  • self.get_contexts returns an array of all contexts discovered so far.
  • self.add_contexts(contexts) adds the contexts passed to it to the context list.
  • self.actions_for_context(context) returns all actions with given context from all files.
  • self.get_files returns all files from the same directory as in process_instructions.

The Linkable mix-in

Linkable is a mix-in module that all of the various item classes, like Project and Action, have. It expects that class that includes it to respond to the instance methods file, line and optionally name, and it adds three methods:

  • uri returns the file uri scheme of the string returned by file. It expects the full path to the file.
  • txmt returns a txmt uri scheme of the file described by the string file and the line number line.
  • link returns html code for an anchor link with the link described by txmt and link name described by name.

The Project, Action etc classes

These are the classes representing the various items in the file. They have some common instance methods, namely the file, line, name methods, which return the expected things.

Project has a couple of extra obvious instance methods, namely: subitems returning the array of subitems, parent returning the parent project, or nil if there isn’t one, and end_line returning the line number where the project’s “end” was located. Finally, completed? returns whether the Project is completed or not.

Action has some more interesting methods, namely:

  • due returns the string of the due date.
  • project returns the project the action belongs to, or nil if there isn’t one.
  • note returns the note text, or an empty string if there isn’t one.
  • completed and completed? return whether the action is completed.
  • context returns the string of the context of the current string.

There are two more item classes, Comment and EndMarker, doing some obvious things.

The Printer class

There is one final class of interest, the Printer class. It is used to generate tabular output out of data provide. It works similarly to the amazing Builder class, it’s like a very-poor-man’s Builder. First create an instance:

pr = Printer.new

Then you need to add data to it. Here’s an example, straight from the “Actions for context” command:

pr.table do
pr.title("Actions for context: #{context}")
pr.headers(["Action name","Project","Due_by"])
actions.each do |a|
proj = if a.project != nil then a.project.link else "none" end
due = case a.due
when "",nil
""
when DateLate
"#{a.due}"
else
a.due
end
pr.row([a.link,proj,due])
end
return pr.to_html

Note the class DateLate. It simply responds to: DateLate === date, where date is either a Date object or a date expressed as a string. It returns true if the date is earlier than Date.today. The last command, pr.to_html, is the one producing html output. Printer instances also respond to the method raw, which allows you to just add raw html code (e.g. style) directly.

An Example

Let’s create a command that generates an html file with all actions, sorted by their due date breaking ties alphabetically, along with links to the corresponding files, and tooltips showing the notes for those actions that have notes. First, we need to process the directory:

#!/usr/bin/env ruby
require ENV['TM_BUNDLE_PATH']+"/bin/GTD.rb"
include GTD
objects = GTDFile.process_directory

Then we extract all actions:

acts = objects.map{|o| o.actions}.flatten

Then we sort them:

acts.sort! do |a,b|
  da, db = *[a.due, b.due].map {|i| i || ""}  # convert nils to empty strings
  if da == db then
    a.name <=> b.name
  else
    da <=> db
  end
end

Now we create the printer object:


pr = Printer.new
pr.table do
  pr.title "List of all Actions"
  pr.headers(["Action", "Context", "Due by"])
  acts.each do |a|
    due = case a.due
          when "",nil
            ""
          when DateLate
            "#{a.due}"
          else
            a.due
          end
    note_part = (a.note != "") ? " title=\"#{a.note}\"" : ""
    text = "#{a.name}”
    pr.row([text, a.context, due])
  end
end

Finally, we print the resulting object:

puts pr.to_html

This it it, we just created a new command! You’ll find it in the bundle, under the key-equivalent ctrl-% (ctrl-shift-5).

That’s it for now. Enjoy!

Later

27 Comments »

  1. After thought » Yet another GTD bundle said,

    June 25, 2006 at 12:51 am

    […] Update5: Totally revamped. A lot of documentation added here. […]

  2. Mark Eli Kalderon said,

    June 25, 2006 at 1:57 pm

    Dear Harris, just downloaded and begun to play with the bundle. Looks promising. Couple of queries though.

    First when I called the help command I got the following error message:

    Can’t open /Users/markkalderon/Library/Application Support/TextMate/Pristine Copy/Bundles/GTDAlt.tmbundle/README.txt: No such file or directory at /usr/local/bin/Markdown.pl line 218. Use of uninitialized value in substitution (s///) at /usr/local/bin/Markdown.pl line 245. Use of uninitialized value in substitution (s///) at /usr/local/bin/Markdown.pl line 246.

    Checking inside the bundle I see that README.txt is at /Users/markkalderon/Library/Application Support/TextMate/Pristine Copy/Bundles/Support/GTDAlt.tmbundle/README.txt

    Two more things.

    First, the Note For Action command works from the gear menu but the key command fails for me.

    Second, does the nesting of projects allow the tracking of dependencies among tasks without assigning them dates?

    Thanks a lot. Mark

  3. Haris said,

    June 25, 2006 at 3:20 pm

    @Mark,

    first of all, it’s Haris. Not a big deal really, just thought I’d correct it before it escalates. :)

    I just updated to a new version, so if you download and install the new version, the help command problem should be fixed.

    As for the Note for Action command, where are you on the line when you try it? What is the scope then?

    I am not sure what you mean about tracking dependencies, can you give me an example? Right now the actions are stored in the project in an array of subitems, each action only directly belonging to the subitems of the innermost project that it is in.

  4. Mark Eli Kalderon said,

    June 25, 2006 at 5:34 pm

    Sorry, Haris, it was a typo, honest.

    Reinstalled the bundle and and the help command works fine now, thanks.

    The key binding for the Note for Action command is still inoperative (though, as I said, it does work from the gear menu). I am calling it from the end of line describing an action in a project.

    As for dependencies, what I had in mind is this. Very often I have a number of related tasks where some must be completed before others. Something like:

    • Get John’s number
    • Call John to arrange meeting
    • Get John’s agreement on the proposal

    It would be nice to generate a list of next actions where only the first of these tasks are listed since the others cannot be done until it is completed.

    If the tasks have due dates, no problem, there is a temporal ordering on them. But suppose they don’t have due dates (I have a lot of ongoing projects with no determinate timetable), how might the dependencies be represented then?

    As I said, I have just started playing with the bundle. Perhaps something can be done with the nested projects.

    All the best, Mark

  5. Haris said,

    June 25, 2006 at 6:04 pm

    The key binding for the Note for Action command is still inoperative (though, as I said, it does work from the gear menu). I am calling it from the end of line describing an action in a project.

    Ah yes, this is a bug with the regexp for matching the line with the action. I can’t seem to get it to match the newline character properly, so at the end of the line you have the wrong scope. The command will work from anywhere else on the line, which is very unfortunate since one usually is at the end of the line. One way to fix it is to change its scope to text.gtdalt - meta.note. But then you must be careful not to use it when not in a non-action non-note line. Probably a small price to pay.

    As for dependencies, what I had in mind is this. Very often I have a number of related tasks where some must be completed before others. Something like:

    * Get John’s number
    * Call John to arrange meeting
    * Get John’s agreement on the proposal
    

    It would be nice to generate a list of next actions where only the first of these tasks are listed since the others cannot be done until it is completed.

    As I said, I have just started playing with the bundle. Perhaps something can be done with the nested projects.

    Well, it would be easy to generate a list of all the first actions for each project. So for instance if you have a project whose items are a subproject and then an action (outside the subproject), then it would offer you as options the action as well as the first action of the subproject. Alan Schussman just created such a command and I have added it to the latest version of the bundle. His command also focuses on a particular context, but I think one can easily modify it to not care about context. I’ll try and do that.

    In other words, when working with the bundle, you have information about the entire tree of projects and actions, and you can act analogously. You could for instance even just have those actions be colored differently in the bigger list.

  6. Haris said,

    June 25, 2006 at 8:42 pm

    @Mark

    I just implemented a Next Actions command. Youl’ll find it in the updated bundle.

  7. Ron Rosson said,

    June 26, 2006 at 5:39 am

    Haris,
    With your rapid movent on this bundle. (Good Job) is there any mechanism in place for us to be notified of any new updates.

    Keep up the good work

  8. Ron Rosson said,

    June 26, 2006 at 5:56 am

    Haris,
    Is it possible to set a due date for a project and not the tasks in the project. Here is an exaple:

    project Annual Door maintenance (place due date here)
    @todo Check and repair/replace weather stripping on exterior doors as necessary.
    @todo Check and tighten door hardware and lubricate as necessary.
    @todo Tighten all bolts on garage door.
    @todo Recommended that counter tops be re-sealed annually with a penetrating sealer.
    end

    Also Just downloaded the bundle from this link and noticed I had to add tmbundle extension. Keep up the good work.

  9. Haris said,

    June 26, 2006 at 7:49 am

    @Ron

    re: keeping up to date. I don’t have my own subversion repository, but I might add this bundle to the bundles repository, if enough people express interest. It would definitely be easier for me too, but I don’t want to bloat the repository with too many bundles.

    Let’s make a deal: If 20 people express interest in the bundle, either through comments here or through email to me or the list, I will see about adding it to the repository. So far there are 4 people.

    re: the link. Hm, that’s really interesting, you shouldn’t have to add the extention. What was the name of the folder that was created when you do double-clicked the bundle?

    re: dates for projects. I’ve considered adding dates for projects, and I might add such support in the future, but the main reason I’ve not done that is that it goes (I think) against the GTD principles. When you make decisions about which action to perform, you really are only supposed to see the actions you can perform at your given context, and then decide accordingly which one has higher priority for you. So the due date of the project would not even be visible, unless it is inherited by each action of the project. And really in your example, these actions, or the whole project for that matter, should not have a due date, they should be done as soon as it is possible. That’s the whole next actions philosophy as I understand it. You do things as soon as it is possible, with only exception things that you have to do on a particular day. Perhaps a starting date would make more sense in such a case.

    However, I can see some benefits in adding both notes and due dates to projects. Though it might have to way a bit I’m afraid.

  10. Ron Rosson said,

    June 26, 2006 at 10:09 am

    Haris,
    regarding updates: Makes sense. Hmm.. a sttic page might work.. But how to tie your posts back to tfhe place were all the commenting is happening.. Currently as I count it I think you have threee different permalinks concerning GTDAlt. Maybe an e-mail alias defined for the people who are interested so you can spam us with the Changelog when you hake updates. ;-)

    regarding the link. I ended up iwth a folder called GTDAlt. There was no tmbundle extension when it uncompressed. No harm done just wanted you to be aware.

    regarding dates for projects. That makes sens on all counts that you brought up.

    Ok. while reading this have you thought ab out adding the ruby gem icalendar to this. Reason why I am asking is currently there are dates set in the projects but at this time there is no mechanism to have them warn you of upcoming/overdue events. With the addition of icalendar it would be possible to interact with Apple’s ical program.

    Using the GTDAlt bundle how do you manage a tickler file that can set to notify you in some automated way of things that need to be done.

    I look forward to the new things you bring to this bundle.

  11. gavin hurley said,

    June 26, 2006 at 10:55 am

    When describing actions you said “Actions start with an ampersand”. But it appears that they actually start with an at-sign.

  12. Haris said,

    June 26, 2006 at 11:12 am

    Quite right, thank you Gavin.

  13. Maurice Calhoun said,

    June 30, 2006 at 6:32 am

    Haris,

    I have a problem, whenever I use any of the command under ‘Context’ or ‘Show Actions’ I get this error


    /Users/mauricecalhoun/Library/Application Support/TextMate/Bundles/GTDAlt.tmbundle/Support/bin/GTD.rb:40:in parse': Parse error: PROJECT|p|PROJECT ${1:new project}|^PROJECT[\s,:]+(\w.*)$|990099 (RuntimeError)
    from /Users/mauricecalhoun/Library/Application Support/TextMate/Bundles/GTDAlt.tmbundle/Support/bin/GTD.rb:115:in
    initialize’
    from /Users/mauricecalhoun/Library/Application Support/TextMate/Bundles/GTDAlt.tmbundle/Support/bin/GTD.rb:73:in new'
    from /Users/mauricecalhoun/Library/Application Support/TextMate/Bundles/GTDAlt.tmbundle/Support/bin/GTD.rb:73:in
    processdirectory’
    from /Users/mauricecalhoun/Library/Application Support/TextMate/Bundles/GTDAlt.tmbundle/Support/bin/GTD.rb:71:in each'
    from /Users/mauricecalhoun/Library/Application Support/TextMate/Bundles/GTDAlt.tmbundle/Support/bin/GTD.rb:71:in
    process
    directory’
    from /tmp/temp_textmate.jTi7IJ:5

    “PROJECT|p|PROJECT ${1:new project}|^PROJECT[\s,:]+(\w.*)$|990099″

    But I’m only getting this error when the gtd file is save outside of Bundle’s ‘Package Content’, why? Please help.

  14. Haris said,

    June 30, 2006 at 11:11 am

    @Maurice

    The program processes all files with extension .gtd in the directory where your current file lives, or in the directory specified by TM_GTD_DIRECTORY. Some file in that directory does not follow the conventions of the GTDAlt format. If you use a dedicated directory for all gtd files following the GTDAlt, and no other gtd files, it should work. Let me know if it doesnt/

  15. Maurice Calhoun said,

    July 1, 2006 at 11:47 pm

    Haris,

    By setting the “TMGTDDIRECTORY”, it worked but, ‘Context’ or ‘Show Actions’ only shows or change to ‘@email’… any thoughts.

  16. Maurice Calhoun said,

    July 2, 2006 at 12:02 am

    Sorry Haris, typo… I had fat finger it.

  17. illtemper.org said,

    July 2, 2006 at 5:18 am

    […] After thought » Details on the GTDAlt bundle (tags: lifehacks productivity textmate gtd bundle plugin mac osx) […]

  18. After thought » GTDAlt and Quicksilver, finally together! said,

    July 3, 2006 at 12:09 am

    […] The GTDALT bundle now contains a script that processes an “Inbox file” for actions, and distributes them to appropriate places. The purpose of this script is to be used in conjunction with a program like Quicksilver. You use Quicksilver to append such lines to the inbox as described here. Then when you are ready to review things, you run the script. The script has not been tested extensively, so be careful the first couple of times you use it. It keeps backups, so theoretically you would not lose data. […]

  19. marios said,

    March 13, 2007 at 2:07 am

    Haris,

    Excellent work. I had a look at Mike Mellors Bundle as well. As a means, to organize my work a little better, I thought that Mike’s approach would suit my needs a little better, since I wanted something more simplified and stripped down, without spending to much time on these files.
    However, I haven’t been able to set this Bundle up with latest cutting Edge, so I reverted to yours ( which is probably also the most comprehensive one, with with the benefit of a solid documentation )

    As I am starting to try this out, allow me to ask a question:

    While working on a Plan to reconfigure my MAC, I am looking also for a kind of means, that would allow me to administer a set of Site Projects, which both exist, as physical Folders ( on a Share through smb) and as projects in the Home Folder.

    In other words, could I use this Bundle in a way, that tasks in gtd files would take me to these Site Projects, to complete the relevant tasks ?

    Sorry, if this question is irrelevant.

    regards, marios

  20. Haris said,

    March 13, 2007 at 4:14 pm

    Geia sou Mario,

    first of all, have a look at the GTDAlt template, it is really a tour of GTDAlt.
    As per your question, you can have links in the gtd file that when you execute the “Open Links in Line” command, they will open in whatever the default application to handle them is. So you could have a link like:

    Then pressing ctrl+L on that line would open that file with the default application for .txt files, which is textmate. So you should be able to link to these projects. Perhaps you would need to create TextMate Project files for them, and then link those project files, if you want whole projects to open up instead of individual files.

  21. marios said,

    March 16, 2007 at 2:47 am

    Hi, Haris.

    Yes indeed, it was all there and it works for projects, files, whatever. I haden’t really finished to examine the Bundle enough, before posting.

    It does in fact do all what I need it to do, however it will fail if triggering the command on network allocated shares through smb.
    I am having similar problems with my own bundle, I wished, that there was a way to work around this, however this hasn’t much todo with your Bundle.

    Thanks again for all this great work, I believe that it is the most sound GTD approach that I have seen so far and the documentation is really a great help.

    regards, marios

  22. Michael said,

    March 21, 2007 at 3:48 pm

    I’m a new and utterly thrilled user of GTDalt. Thank you!.

    An error that cropped up for me with the latest Textmate build (1372). The context command (shift-@) gives this error after I select from the popup list: /Users/mcg/Applications/Text/Textmate/TextMate.app/Contents/SharedSupport/Support/lib/dialog.rb:138: warning: Insecure world writable dir /Users/mcg/Documents, mode 040756.

    Any advice. Did I do something clueless when I grabbed the latest build?

  23. Graham English said,

    May 14, 2007 at 6:08 pm

    This bundle rules! Seriously, it suits me perfectly. Thanks so much for creating it.

  24. Theo said,

    July 15, 2007 at 12:26 pm

    Sorry to be a bit of a newbie idiot but can someone show me how to add contexts via the TMGTDCONTEXT variable. In particular the coding required.

    Thanks.

  25. Haris said,

    July 17, 2007 at 7:43 pm

    Just create a TM_GTD_CONTEXT variable through TextMate’s preferences, whose value will contain the contexts you want, separated by spaces. That’s all you should have to do really.

  26. Mark PS said,

    October 11, 2007 at 4:08 pm

    Haris,

    Thanks for making this great bundle! I’m just starting to scratch the surface with this one and I was wondering if there was any way to list items by context without using the ‘Review Current Actions’ option. In other words, is there a way to alphabetize non-project actions such that they are grouped by context?

    Thaks again!

  27. Big Bucket - Archive - Things said,

    March 17, 2008 at 5:43 am

    […] I can’t speak for how Things compares to other GTD apps as it is the only GTD app I’ve ever seriously tried to use, unless you count the TextMate bundle, GTDalt. I suppose GTDalt was my first attempt at GTD. My biggest problem using GTDalt was that I basically treated it as write-only medium; I would jot down things that I thought I might forget and maybe mark them off when I actually did them. I rarely consulted the list before getting down to work and I didn’t use it to record everything that I needed doing. […]

Leave a Comment