[Vimoutliner] VimOutliner as a task manager? - thescript

Fredrik fredrik at jumans.net
Sat Nov 3 05:52:27 EST 2007


I might as well attach my script here... First I add some shortcutkeys
to it in my .vimrc as this :

" GTD stuff
map gp :%!/<path>/groupby.pl project<CR>
map gc :%!/<path>/groupby.pl context<CR>

Below follows the whole script. Bare in mind that this is a betascript
so I have added something that creates a copy of the file that is edited
before regrouping so you need to change paths etc :

#!/usr/bin/perl -W

use strict;
use Data::Dumper;

my %in;
my %projects;
my %contexts;

# Help sort to sort pos
sub possort { # {{{1
	my @a = split (/\./, $a); my @b = split (/\./, $b);
	my $min = $#a;
	if ($#b < $#a) {$min = $#b}
	my $result=0;
	for (my $c=0; $c <= $min; $c++) {
		if ($a[$c] > $b[$c]) {$result = 1; last}
		elsif ($a[$c] < $b[$c]) {$result = -1; last}
	}
	if ($result == 0) {
		if ($#a < $#b) {$result = -1}
		elsif ($#a > $#b) {$result = 1}
	}
	return $result;
} # }}}1

# Count tabs at beginning of line
sub ctabs { # {{{1
	my ($t) = @_;
	my @t = split (//, $t);
	my $c=0;
	while ($t[$c] eq "\t") {$c++}
	return $c;
} #}}}1

# Create full projectname based on position
sub fullprojectname { # {{{1
	my ($pos) = @_;
	my $name = $in{$pos}{"text"};
	while ($pos =~ /\./) {
		$pos =~ s/\.\d*?$//;
		$name = $in{$pos}{"text"}.":$name";
	}
	return $name;
} # }}}1

# Take a projectstring and break it apart
sub addfullproject { # {{{1
	my ($project) = @_;
	if (!defined $projects{$project}) {$projects{$project} = ""}
	while ($project =~ /\:/) {
		my @p = split (/\:/, $project);
		pop @p; $project = join (":", @p);
		if (!defined $projects{$project}) {$projects{$project} =
""}
	}
} # }}}1

# Parse projectline to datastructure
sub parseproject { # {{{1
	my ($pos, $lc, $line) = @_;
	my %data;
	$line =~ s/.*\% //;
	$in{$pos}{"type"} = "P";
	$in{$pos}{"text"} = $line;
	$projects{&fullprojectname($pos)} = $pos;
} # }}}1

# Parse contextline to datastructure
sub parsecontext { # {{{1
	my ($pos, $lc, $line) = @_;
	if ($pos =~ /\./) {
		print "Context at line $lc must be a root object\n";
		exit;
	}
	$line =~ s/\@//;
	$in{$pos}{"type"} = "C";
	$in{$pos}{"text"} = $line;
	$contexts{$line} = $pos;
} # }}}1

# Parse miscline to datastructure
sub parsemisc { # {{{1
	my ($pos, $lc, $line) = @_;
	if ($pos !~ /\./) {
		print "Misc at line $lc can not be a root object\n";
		exit;
	}
	$in{$pos}{"type"} = "M";
	$in{$pos}{"text"} = $line
} # }}}1

# Parse taskline to datastructure
sub parsetask { # {{{1
	my ($pos, $lc, $line) = @_;
	$in{$pos}{"type"} = "T";
	$in{$pos}{"status"} = "";
	if ($line =~ /^\[.\] /) {
		$in{$pos}{"status"} = $line;
		$in{$pos}{"status"}  =~ s/.*\[//;
		$in{$pos}{"status"}  =~ s/\].*//;
		$line =~ s/.*\] //;
	}
	if ($line =~ / \{.*\}$/) {
		$in{$pos}{"project"} = $line;
		$in{$pos}{"project"}  =~ s/.*\{//;
		$in{$pos}{"project"}  =~ s/\}.*//;
		&addfullproject ($in{$pos}{"project"});
		$line =~ s/ \{.*//;
	} else {
		my $tmppos = $pos; $tmppos =~ s/\.\d*?$//;
		$in{$pos}{"project"} = &fullprojectname($tmppos);
	}
	if ($line =~ / \@/) {
		$in{$pos}{"context"} = $line;
		$in{$pos}{"context"} =~ s/.* \@//;
		if (!defined $contexts{$in{$pos}{"context"}}) {
			$contexts{$in{$pos}{"context"}} = "";
		}
		$line =~ s/ \@.*$//;
	} else {
		my $tmppos = $pos; $tmppos =~ s/\.\d*?$//;
		$in{$pos}{"context"} = $in{$tmppos}{"text"};
	}
	$in{$pos}{"text"} = $line;
} # }}}1

# Convert lines to hash by type
sub parseline { # {{{1
	my ($pos, $lc, $line) = @_;
	$line =~ s/[\n\r]//g;
	$line =~ s/^\s*//; $line =~ s/\s*$//;
	if ($line =~ / \@.*$/) {&parsetask($pos,$lc,$line)}
	elsif ($line =~ / \{.*\}/) {&parsetask($pos,$lc,$line)}
	elsif ($line =~ /^\[.\] \d*% /) {&parseproject($pos,$lc,$line)}
	elsif ($line =~ /^\@/) {&parsecontext($pos,$lc,$line)}
	else {&parsemisc($pos,$lc,$line)}
} # }}}1

# Read stdin and parse lines to data
sub readstdin { # {{{1
	my @pos;
	my $lc=0;
	my $date = `date +"%Y%m%d%H%M%S"`; $date =~ s/[\n\r]//;
	open (FILE, ">/<path>/.backup-".$date) or die "No backup!";
	while (<STDIN>) {
		$lc++;
		print FILE $_;
		if (/^\s*$/) {next} # Skip blank lines
		# Create current position
		my $ctabs = &ctabs($_);
		if ($ctabs > $#pos) {push (@pos, 0)}
		elsif ($ctabs < $#pos) {
			while ($ctabs < $#pos) {pop @pos}
			$pos[$#pos]++;
		} else {$pos[$#pos]++}
		# Clean up line a bit
		$_ =~ s/[\n\r]//g;
		$_ =~ s/^\s*//; $_ =~ s/\s*$//;
		&parseline(join(".", @pos), $lc, $_);
	}
	close (FILE);
} #}}}1

# Go trough all %in and find tasks
sub findtask { # {{{{1
	my ($context, $project, $status) = @_;
	my @found;
	foreach my $pos (sort possort keys %in) {
		if ($in{$pos}{"type"} eq "T") {
			if ($in{$pos}{"status"} eq $status) {
				if ($context ne "" ) {
					if ($in{$pos}{"context"} eq
$context) {
						push (@found, $pos);
					}
				} else {
					if ($in{$pos}{"project"} eq
$project) {
						push (@found, $pos);
					}
				}
			}
		}
	}
	return (@found);
} # }}}1

# Go through all of %in and find misc lines
sub findmisc { # {{{1
	my ($parent) = @_;
	my @found;
	foreach my $pos (sort possort keys %in) {
		if ($pos =~ /^$parent\.\d*$/) {
			if ($in{$pos}{"type"} eq "M") {
				push (@found, $pos);
			}
		}
	}
	return (@found);
} # }}}1

# Print context view
sub printcontextview { # {{{1
	foreach my $context (sort keys %contexts) {
		my @activetasks = &findtask ($context,"","_");
		my @passivetasks = &findtask ($context,"","");
		if (($#activetasks >= 0) || ($#passivetasks >= 0)) {
			print "\@$context\n";
		}
		# Print active tasks
		foreach my $taskpos (@activetasks) {
			print "\t[_] ".$in{$taskpos}{"text"};
			print " {".$in{$taskpos}{"project"}."}\n";
			foreach my $miscpos (&findmisc ($taskpos)) {
				print "\t\t".$in{$miscpos}{"text"}."\n";
			}
		}
		# Print passive tasks
		foreach my $taskpos (@passivetasks) {
			print "\t".$in{$taskpos}{"text"};
			print " {".$in{$taskpos}{"project"}."}\n";
			foreach my $miscpos (&findmisc ($taskpos)) {
				print "\t\t".$in{$miscpos}{"text"}."\n";
			}
		}
		if (($#activetasks >= 0) || ($#passivetasks >= 0)) {
			print "\n";
		}
	}
	# Print projects
	foreach my $proj (sort keys %projects) {
		my $tabs = $proj;
		$tabs =~ s/[^\:]//g;
		$tabs =~ s/\:/\t/g;
		my $short = $proj;
		$short =~ s/.*\://;
		print $tabs."[_] % ".$short."\n";
		if ($projects{$proj} ne "") {
			# Print project comments
			foreach my $miscpos (&findmisc
($projects{$proj})) {
				print
$tabs."\t".$in{$miscpos}{"text"}."\n";
			}
			# Print finished tasks
			foreach my $taskpos (&findtask ("",$proj,"X")) {
				print $tabs."\t[X]
".$in{$taskpos}{"text"};
				print "
\@".$in{$taskpos}{"context"}."\n";
				foreach my $miscpos (&findmisc
($taskpos)) {
					print
$tabs."\t\t".$in{$miscpos}{"text"}."\n";
				}
			}
		}
	}
} # }}}1

# Print the projectview
sub printprojectview { # {{{1
	foreach my $proj (sort keys %projects) {
		my $tabs = $proj;
		$tabs =~ s/[^\:]//g;
		$tabs =~ s/\:/\t/g;
		my $short = $proj;
		$short =~ s/.*\://;
		print $tabs."[_] % ".$short."\n";
		# Print project comments
		foreach my $miscpos (&findmisc ($projects{$proj})) {
			print $tabs."\t".$in{$miscpos}{"text"}."\n";
		}
		# Print passive tasks
		foreach my $taskpos (&findtask ("",$proj,"")) {
			print $tabs."\t".$in{$taskpos}{"text"};
			print " \@".$in{$taskpos}{"context"}."\n";
			foreach my $miscpos (&findmisc ($taskpos)) {
				print
$tabs."\t\t".$in{$miscpos}{"text"}."\n";
			}
		}
		# Print active tasks
		foreach my $taskpos (&findtask ("",$proj,"_")) {
			print $tabs."\t[_] ".$in{$taskpos}{"text"};
			print " \@".$in{$taskpos}{"context"}."\n";
			foreach my $miscpos (&findmisc ($taskpos)) {
				print
$tabs."\t\t".$in{$miscpos}{"text"}."\n";
			}
		}
		# Print finished tasks
		foreach my $taskpos (&findtask ("",$proj,"X")) {
			print $tabs."\t[X] ".$in{$taskpos}{"text"};
			print " \@".$in{$taskpos}{"context"}."\n";
			foreach my $miscpos (&findmisc ($taskpos)) {
				print
$tabs."\t\t".$in{$miscpos}{"text"}."\n";
			}
		}
	}
} # }}}1


# ----- MAIN -----

if ($#ARGV != 0) {
	print "Not enough command line parameters!\n";
	exit;
}

&readstdin;

if ($ARGV[0] eq "project") {
	&printprojectview;
} else {
	&printcontextview;
}

#print Dumper(%in);

#foreach my $key (sort keys %contexts) {
#print $key."\n";
#}

# vim600: set foldlevel=0 foldmethod=marker:



More information about the VimOutliner mailing list