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