/todo

To get this branch, use:
bzr branch http://bzr.ed.am/todo
1 by edam
init
1
#!/usr/bin/perl
2
6 by edam
rewrite file parsing to cache the last line and only display it when we know the current line is not a section heading "underline" line.
3
use feature "state";
4
1 by edam
init
5
use strict;
6
use warnings;
7
use Getopt::Long;
8
use File::Basename;
9
10
# get app name
11
my $app_name = basename( $0 );
12
13
# option defaults
14
my $todo_dir = "~/.todo";
15
my $display_all = 0;
2 by edam
added default todo file content and changed main todo section name to "TODO"
16
my $display_section = "TODO";
1 by edam
init
17
my $display_headers = 0;
18
my $mode_edit = 0;
8 by edam
added --list
19
my $mode_less = 0;
1 by edam
init
20
my $mode_help = 0;
21
my $mode_version = 0;
22
23
# parse command line opts
24
Getopt::Long::Configure( 'gnu_getopt' );
25
if( !GetOptions(
26
    'a|all' => \$display_all,
27
    'e|edit' => \$mode_edit,
28
    'h|headers' => \$display_headers,
8 by edam
added --list
29
	'l|list' => \$mode_less,
7 by edam
made section selection case insensitive and added short option (-s) for it
30
    's|section=s' => \$display_section,
1 by edam
init
31
    'help' => \$mode_help,
32
    'version' => \$mode_version,
33
) ) {
34
    print "Try `$app_name --help' for more information.\n";
35
    exit( 1 );
36
}
37
38
# help mode
39
if( $mode_help ) {
40
    print "todo - display your todo file\n\n".
4 by edam
fixed spaces
41
    "Usage: $app_name [OPTIONS]\n\n".
42
    "Options:\n".
43
    "  -a, --all              display all sections\n".
44
    "  -e, --edit             edit your todo file\n".
45
    "  -h, --headers          show setion headers\n".
8 by edam
added --list
46
	"  -l, --list             show the list in your pager (see notes)\n".
7 by edam
made section selection case insensitive and added short option (-s) for it
47
    "  -s, --section=SECTION  display this named section\n".
4 by edam
fixed spaces
48
    "      --help     display this help and exit\n".
8 by edam
added --list
49
    "      --version  output version information and exit\n".
50
	"\n".
51
	"The advantage of using '--list' is that the pager is run as if the\n".
52
	"todo list were being edited. This means that if you decide to spawn an\n".
53
	"editor from you pager and edit the list, these changes get noticed.\n".
54
	"\n".
55
	"The environment variables EDITOR and PAGER are used.\n".
56
	"\n".
57
	"Please report bugs to Tim Marston <tim\@ed.am>.\n";
1 by edam
init
58
    exit( 0 );
59
}
60
61
# version mode
62
if( $mode_version ) {
3 by edam
fixed version info and error about failing to start the editor
63
    print "todo 1.0\n".
8 by edam
added --list
64
        "Copyright (C) 2011, 2012 Tim Marston.\n".
4 by edam
fixed spaces
65
        "http://ed.am/software/todo\n";
1 by edam
init
66
    exit( 0 );
67
}
68
69
#_______________________________________________________________________________
70
71
72
# check we have bazaar installed
73
my $output = `which bzr`;
74
chomp $output;
75
$output eq "" and die "Bazaar is not installed";
76
77
# glob todo directory
78
$todo_dir = glob( $todo_dir );
79
( -f $todo_dir ) and die "$todo_dir exists and is a file";
80
8 by edam
added --list
81
# less mode
82
if( $mode_less )
83
{
84
    ( ! -f "$todo_dir/todo" ) && die "no todo file";
85
	$mode_edit = 1;
86
}
87
1 by edam
init
88
# edit mode
89
if( $mode_edit )
90
{
91
    # create the todo directory, as necessary
4 by edam
fixed spaces
92
    if( ! -d $todo_dir ) {
93
        mkdir $todo_dir or die "couldn't create todo directory";
94
        `bzr init --no-aliases -q "$todo_dir"`;
95
        $? == 0 or die "couldn't init bzr repo";
96
    }
97
    
98
    # create a default todo file, as necessary
99
    if( ! -f "$todo_dir/todo" ) {
100
        open FILE, ">$todo_dir/todo" or die "couldn't create default todo file";
101
        my $content = <<"EOT";
2 by edam
added default todo file content and changed main todo section name to "TODO"
102
<!-- This file uses Markdown syntax. For more info about Markdown
103
     syntax, see http://daringfireball.net/projects/markdown/syntax.
104
105
     There should be a main H1 header called "TODO" for the main todo
106
     list section (one has been added for you below). You can also add
107
     as many more sections as you like for other lists. And feel free
108
     to delete this comment! -->
109
110
TODO
111
====
112
113
* make a list of things to do
114
115
EOT
116
        print FILE $content or die "couldn't write default todo file";
4 by edam
fixed spaces
117
        close FILE;
118
        `bzr add --no-aliases -q "$todo_dir/todo"`;
119
        $? == 0 or die "couldn't add todo file to bzr repo";
120
    }
1 by edam
init
121
122
    # determine editor from environment, default to "emacs -nw"
8 by edam
added --list
123
	my $editor;
124
	if( $mode_less ) {
125
		$editor = $ENV{ 'PAGER' };
126
		defined $editor or $editor = 'less';
127
	}
128
	else {
129
		$editor = $ENV{ 'EDITOR' };
130
		defined $editor or $editor = 'emacs -nw';
131
	}
1 by edam
init
132
    my @exec_array = split( / +/, $editor );
133
    push( @exec_array, "$todo_dir/todo" );
134
4 by edam
fixed spaces
135
    # detect emacs and try to use markdown-mode
136
    $exec_array[ 0 ] eq "emacs" and
137
        push( @exec_array, '--funcall=markdown-mode' );
1 by edam
init
138
139
    # edit todo file
140
    system( @exec_array );
4 by edam
fixed spaces
141
    $? == 0 or
142
        die "can't start editor, check EDITOR envionment variable";
143
144
    # check for changes and commit it
145
    $output = `bzr status --no-aliases "$todo_dir/todo"`;
146
    $? == 0 or die "couldn't check bzr rerpo status";
147
    chomp $output;
148
    if( $output ne "" ) {
149
        `bzr commit --no-aliases -q -m - "$todo_dir/todo"`;
150
        $? == 0 or die "couldn't commit to bzr repo";
151
    }
152
153
    # after editing, exit
154
    exit
1 by edam
init
155
}
156
6 by edam
rewrite file parsing to cache the last line and only display it when we know the current line is not a section heading "underline" line.
157
# function to display a line
158
sub display_line
159
{
160
	my ( $line, $section ) = @_;
161
	state $old_section = '';
162
163
	# detect section change
164
	if( $section ne $old_section ) {
165
		$old_section = $section;
166
		
167
		# display section heading
168
		if( $display_headers || $display_all ) {
169
			print "$section\n".
170
				( "=" x length( $section ) )."\n";
171
		}
172
	}
173
174
	# replace tabs with 4 spaces
175
	$line =~ s/\t/    /g;
176
	
177
	# display the line
178
	print $line;
179
}
180
181
1 by edam
init
182
# scan through file
6 by edam
rewrite file parsing to cache the last line and only display it when we know the current line is not a section heading "underline" line.
183
my $candidate_section = '';
1 by edam
init
184
my $section = '';
6 by edam
rewrite file parsing to cache the last line and only display it when we know the current line is not a section heading "underline" line.
185
my $last_line = '';
1 by edam
init
186
open FILE, "<$todo_dir/todo" or die "can't open todo file";
6 by edam
rewrite file parsing to cache the last line and only display it when we know the current line is not a section heading "underline" line.
187
while( <FILE> )
188
{
1 by edam
init
189
    # detect the line after section headings, and thus sections
6 by edam
rewrite file parsing to cache the last line and only display it when we know the current line is not a section heading "underline" line.
190
    if( /^[-=]{2,}/ && $candidate_section ne '' ) {
191
        $section = $candidate_section;
192
        $candidate_section = '';
193
		$last_line = '';
194
		next;
1 by edam
init
195
    }
6 by edam
rewrite file parsing to cache the last line and only display it when we know the current line is not a section heading "underline" line.
196
1 by edam
init
197
    # detect section headings
6 by edam
rewrite file parsing to cache the last line and only display it when we know the current line is not a section heading "underline" line.
198
    if( /^[-_\.A-Za-z0-9 ]+$/ ) {
199
        $candidate_section = $_;
200
		chomp $candidate_section;
201
	}
1 by edam
init
202
    else {
6 by edam
rewrite file parsing to cache the last line and only display it when we know the current line is not a section heading "underline" line.
203
        $candidate_section = '';
204
	}
205
206
	# display last line
207
	display_line( $last_line, $section ) if( $last_line ne '' );
208
209
    # display line
7 by edam
made section selection case insensitive and added short option (-s) for it
210
	if( ( lc( $section ) eq lc( $display_section ) ) ||
6 by edam
rewrite file parsing to cache the last line and only display it when we know the current line is not a section heading "underline" line.
211
		( $section && $display_all ) )
212
	{
213
		$last_line = $_;
1 by edam
init
214
    }
6 by edam
rewrite file parsing to cache the last line and only display it when we know the current line is not a section heading "underline" line.
215
	else {
216
		$last_line = '';
217
	}
1 by edam
init
218
}
6 by edam
rewrite file parsing to cache the last line and only display it when we know the current line is not a section heading "underline" line.
219
display_line( $last_line, $section ) if( $last_line ne '' );