Saturday, January 22, 2011

Todo Lists

It seems the thing to do these days is to write "a better todo list". Probably there is at least one (maybe dozens) implemented in each programming language in existence. Factor even has its own todo web application.

When it comes to development, most developers keep lists of changes that need to be made or features that need to be implemented. Factor has its own todo list on the wiki. I know you are thinking what I'm thinking: wouldn't it be great if we could keep the todo list alongside the code? In any event, it would make a nice demonstration of the vocabulary and help browser system.


First, some background. Every vocabulary supports various metadata associated with the code, including:

summary.txta single line description of the vocabulary
authors.txta list of vocabulary authors
resources.txta list of files to include when deploying
tags.txta list of vocabulary tags used for organization
platforms.txta list of supported platforms if not cross-platform


We are going to add to this a todo.txt file containing a todo list of improvements or additions that could be made to the vocabulary. The format of the todo.txt file will be a list of text, each on its own line.

USING: arrays assocs formatting io io.pathnames kernel
sequences vocabs vocabs.loader vocabs.metadata ;

IN: todos

The path to the todo.txt file is relative to the directory containing the specified vocabulary:

: vocab-todo-path ( vocab -- string )
    vocab-dir "todo.txt" append-path ;

We can get and set the list of todo items using vocab-file-contents and set-vocab-file-contents, respectively.

: vocab-todo ( vocab -- todos )
    dup vocab-todo-path vocab-file-contents ;

: set-vocab-todo ( todos vocab -- )
    dup vocab-todo-path set-vocab-file-contents ;

We could add new todo items at runtime:

: add-vocab-todo ( todo vocab -- )
    [ vocab-todo swap suffix ] keep set-vocab-todo ;

Printing out the todo list for a specified vocabulary is pretty easy:

: todos. ( vocab -- )
    vocab-todo [ print ] each ;

Using the child-vocabs word, we can look through a vocabulary hierarchy for all todo files, returning a map of vocabulary to non-empty list of todo items.

: all-todos ( vocab -- assoc )
    child-vocabs [ dup vocab-todo 2array ] map
    [ second empty? not ] filter ;

And then print them out from the Listener:

: all-todos. ( vocab -- )
    all-todos [
        [ "%s:\n" printf ] [ [ "- %s\n" printf ] each ] bi*
    ] assoc-each ;

Try it

Although we could make the todo.txt files by hand, why not try using Factor?

( scratchpad ) USE: tools.scaffold

( scratchpad ) "foo" scaffold-work
Creating scaffolding for P" resource:work/foo/foo.factor"

( scratchpad ) "" scaffold-work
Creating scaffolding for P" resource:work/foo/bar/bar.factor"
Loading resource:work/foo/bar/bar.factor

( scratchpad ) "The first thing" "foo" add-vocab-todo

( scratchpad ) "The second thing" "foo" add-vocab-todo

( scratchpad ) "Another thing" "" add-vocab-todo

( scratchpad ) "foo" todos.
The first thing
The second thing

( scratchpad ) "foo" all-todos.
- The first thing
- The second thing
- Another thing

If you look in $FACTOR/work, you will now find the foo/todo.txt and foo/bar/todo.txt files that we just created.


We can use these words to make a dynamic help article containing all of the todo entries for loaded vocabularies:

USING: assocs help.markup help.syntax kernel todos ;

: $all-todos ( element -- )
    drop "" all-todos [
        [ $heading ] [ $list ] bi*
    ] assoc-each ;

ARTICLE: "vocab-todos" "Vocabulary todos"
{ $all-todos } ;

Once loaded, just run this to see the help article created by the previous example:

( scratchpad ) "vocab-todos" help

The code for this is on my Github.

No comments: