Merge branch 'master' into dependency-types
authorJoey Hess <joey@gnu.kitenet.net>
Mon, 5 Oct 2009 21:11:08 +0000 (17:11 -0400)
committerJoey Hess <joey@gnu.kitenet.net>
Mon, 5 Oct 2009 21:11:08 +0000 (17:11 -0400)
30 files changed:
IkiWiki.pm
IkiWiki/Plugin/brokenlinks.pm
IkiWiki/Plugin/calendar.pm
IkiWiki/Plugin/edittemplate.pm
IkiWiki/Plugin/inline.pm
IkiWiki/Plugin/linkmap.pm
IkiWiki/Plugin/listdirectives.pm
IkiWiki/Plugin/map.pm
IkiWiki/Plugin/meta.pm
IkiWiki/Plugin/orphans.pm
IkiWiki/Plugin/pagecount.pm
IkiWiki/Plugin/pagestats.pm
IkiWiki/Plugin/postsparkline.pm
IkiWiki/Plugin/progress.pm
IkiWiki/Render.pm
debian/NEWS
debian/changelog
debian/postinst
doc/bugs/bestlink_change_update_issue.mdwn
doc/bugs/transitive_dependencies.mdwn
doc/ikiwiki/directive/inline.mdwn
doc/ikiwiki/directive/pagestats.mdwn
doc/plugins.mdwn
doc/plugins/contrib.mdwn
doc/plugins/orphans.mdwn
doc/plugins/sidebar.mdwn
doc/plugins/write.mdwn
doc/todo/dependency_types.mdwn
ikiwiki-transition
t/add_depends.t [new file with mode: 0644]

index 97d84c9deb175b694bd303b10ff75fba6b95bb08..b895e12fc0a924fac01a6ef1b10d58e02dbdb003 100644 (file)
@@ -28,6 +28,11 @@ our $VERSION = 3.00; # plugin interface version, next is ikiwiki version
 our $version='unknown'; # VERSION_AUTOREPLACE done by Makefile, DNE
 our $installdir='/usr'; # INSTALLDIR_AUTOREPLACE done by Makefile, DNE
 
+# Page dependency types.
+our $DEPEND_CONTENT=1;
+our $DEPEND_PRESENCE=2;
+our $DEPEND_LINKS=4;
+
 # Optimisation.
 use Memoize;
 memoize("abs2rel");
@@ -1524,18 +1529,28 @@ sub loadindex () {
                                $links{$page}=$d->{links};
                                $oldlinks{$page}=[@{$d->{links}}];
                        }
-                       if (exists $d->{depends_simple}) {
+                       if (ref $d->{depends_simple} eq 'ARRAY') {
+                               # old format
                                $depends_simple{$page}={
                                        map { $_ => 1 } @{$d->{depends_simple}}
                                };
                        }
+                       elsif (exists $d->{depends_simple}) {
+                               $depends{$page}=$d->{depends_simple};
+                       }
                        if (exists $d->{dependslist}) {
+                               # old format
                                $depends{$page}={
-                                       map { $_ => 1 } @{$d->{dependslist}}
+                                       map { $_ => $DEPEND_CONTENT }
+                                               @{$d->{dependslist}}
                                };
                        }
+                       elsif (exists $d->{depends} && ! ref $d->{depends}) {
+                               # old format
+                               $depends{$page}={$d->{depends} => $DEPEND_CONTENT };
+                       }
                        elsif (exists $d->{depends}) {
-                               $depends{$page}={$d->{depends} => 1};
+                               $depends{$page}=$d->{depends};
                        }
                        if (exists $d->{state}) {
                                $pagestate{$page}=$d->{state};
@@ -1581,11 +1596,11 @@ sub saveindex () {
                };
 
                if (exists $depends{$page}) {
-                       $index{page}{$src}{dependslist} = [ keys %{$depends{$page}} ];
+                       $index{page}{$src}{depends} = $depends{$page};
                }
 
                if (exists $depends_simple{$page}) {
-                       $index{page}{$src}{depends_simple} = [ keys %{$depends_simple{$page}} ];
+                       $index{page}{$src}{depends_simple} = $depends_simple{$page};
                }
 
                if (exists $pagestate{$page}) {
@@ -1753,20 +1768,39 @@ sub rcs_receive () {
        $hooks{rcs}{rcs_receive}{call}->();
 }
 
-sub add_depends ($$) {
+sub add_depends ($$;@) {
        my $page=shift;
        my $pagespec=shift;
 
-       if ($pagespec =~ /$config{wiki_file_regexp}/ &&
-               $pagespec !~ /[\s*?()!]/) {
-               # a simple dependency, which can be matched by string eq
-               $depends_simple{$page}{lc $pagespec} = 1;
+       # Is the pagespec a simple page name?
+       my $simple=$pagespec =~ /$config{wiki_file_regexp}/ &&
+               $pagespec !~ /[\s*?()!]/;
+
+       my $deptype=$DEPEND_CONTENT;
+       if (@_) {
+               my %params=@_;
+               
+               # Is the pagespec limited to terms that will continue
+               # to match pages as long as those pages exist?
+               my $limited=1;
+               while ($limited && $pagespec=~m/(\w+)\([^\)]*\)/g) {
+                       $limited = $1 =~ /^(glob|internal|creation_month|creation_day|creation_year|created_before|created_after)$/;
+               }
+
+               $deptype=$deptype & ~$DEPEND_CONTENT | $DEPEND_PRESENCE
+                       if $params{presence} && $limited;
+               $deptype=$deptype & ~$DEPEND_CONTENT | $DEPEND_LINKS
+                       if $params{links} && $limited;
+       }
+
+       if ($simple) {
+               $depends_simple{$page}{lc $pagespec} |= $deptype;
                return 1;
        }
 
        return unless pagespec_valid($pagespec);
 
-       $depends{$page}{$pagespec} = 1;
+       $depends{$page}{$pagespec} |= $deptype;
        return 1;
 }
 
index eb698b0bef9f7b2c9df90b342dcfac56c79a1795..9e65f52c656f55dd201c6b2abba6fd5f9419e3a8 100644 (file)
@@ -23,9 +23,8 @@ sub preprocess (@) {
        my %params=@_;
        $params{pages}="*" unless defined $params{pages};
        
-       # Needs to update whenever a page is added or removed, so
-       # register a dependency.
-       add_depends($params{page}, $params{pages});
+       # Needs to update whenever the links on a page change.
+       add_depends($params{page}, $params{pages}, links => 1);
        
        my @broken;
        foreach my $link (keys %IkiWiki::brokenlinks) {
index 5d16dff75ba7a98a68c3fc8c94d9d9378cb11732..a1117992a865ed3a5cd5a7a8ded409d3b8ace33e 100644 (file)
@@ -104,19 +104,22 @@ sub format_month (@) {
                        "$archivebase/$year/".sprintf("%02d", $month),
                        linktext => " $monthname ");
        }
-       add_depends($params{page}, "$archivebase/$year/".sprintf("%02d", $month));
+       add_depends($params{page}, "$archivebase/$year/".sprintf("%02d", $month),
+               presence => 1);
        if (exists $cache{$pagespec}{"$pyear/$pmonth"}) {
                $purl = htmllink($params{page}, $params{destpage}, 
                        "$archivebase/$pyear/" . sprintf("%02d", $pmonth),
                        linktext => " $pmonthname ");
        }
-       add_depends($params{page}, "$archivebase/$pyear/".sprintf("%02d", $pmonth));
+       add_depends($params{page}, "$archivebase/$pyear/".sprintf("%02d", $pmonth),
+               presence => 1);
        if (exists $cache{$pagespec}{"$nyear/$nmonth"}) {
                $nurl = htmllink($params{page}, $params{destpage}, 
                        "$archivebase/$nyear/" . sprintf("%02d", $nmonth),
                        linktext => " $nmonthname ");
        }
-       add_depends($params{page}, "$archivebase/$nyear/".sprintf("%02d", $nmonth));
+       add_depends($params{page}, "$archivebase/$nyear/".sprintf("%02d", $nmonth),
+               presence => 1);
 
        # Start producing the month calendar
        $calendar=<<EOF;
@@ -209,11 +212,11 @@ EOF
 
        # Add dependencies to update the calendar whenever pages
        # matching the pagespec are added or removed.
-       add_depends($params{page}, $params{pages});
+       add_depends($params{page}, $params{pages}, presence => 1);
        # Explicitly add all currently linked pages as dependencies, so
        # that if they are removed, the calendar will be sure to be updated.
        foreach my $p (@list) {
-               add_depends($params{page}, $p);
+               add_depends($params{page}, $p, presence => 1);
        }
 
        return $calendar;
@@ -246,19 +249,19 @@ sub format_year (@) {
                        "$archivebase/$year",
                        linktext => "$year");
        }
-       add_depends($params{page}, "$archivebase/$year");
+       add_depends($params{page}, "$archivebase/$year", presence => 1);
        if (exists $cache{$pagespec}{"$pyear"}) {
                $purl = htmllink($params{page}, $params{destpage}, 
                        "$archivebase/$pyear",
                        linktext => "\&larr;");
        }
-       add_depends($params{page}, "$archivebase/$pyear");
+       add_depends($params{page}, "$archivebase/$pyear", presence => 1);
        if (exists $cache{$pagespec}{"$nyear"}) {
                $nurl = htmllink($params{page}, $params{destpage}, 
                        "$archivebase/$nyear",
                        linktext => "\&rarr;");
        }
-       add_depends($params{page}, "$archivebase/$nyear");
+       add_depends($params{page}, "$archivebase/$nyear", presence => 1);
 
        # Start producing the year calendar
        $calendar=<<EOF;
@@ -310,7 +313,7 @@ EOF
                else {
                        $calendar.=qq{\t<td class="$tag">$monthabbr</td>\n};
                }
-               add_depends($params{page}, "$archivebase/$year/$mtag");
+               add_depends($params{page}, "$archivebase/$year/$mtag", presence => 1);
 
                $calendar.=qq{\t</tr>\n} if ($month % $params{months_per_row} == 0);
        }
index 0bafc95d06d854b860e566a8c14134119d51db17..2dd1dbe682c2567e4bf879e0708e0ea9e2fcb8c0 100644 (file)
@@ -58,7 +58,7 @@ sub preprocess (@) {
        $pagestate{$params{page}}{edittemplate}{$params{match}}=$link;
 
        return "" if ($params{silent} && IkiWiki::yesno($params{silent}));
-       add_depends($params{page}, $link);
+       add_depends($params{page}, $link, presence => 1);
        return sprintf(gettext("edittemplate %s registered for %s"),
                htmllink($params{page}, $params{destpage}, $link),
                $params{match});
index ccfadfd699929a8be1764546118d89ee9f958f56..cebd9037cc65c6095bca009c99663d2894223dd4 100644 (file)
@@ -197,7 +197,7 @@ sub preprocess_inline (@) {
                        split ' ', $params{pagenames};
        }
        else {
-               add_depends($params{page}, $params{pages});
+               add_depends($params{page}, $params{pages}, presence => $quick);
 
                @list = pagespec_match_list(
                        [ grep { $_ ne $params{page} } keys %pagesources ],
@@ -248,10 +248,9 @@ sub preprocess_inline (@) {
        }
 
        # Explicitly add all currently displayed pages as dependencies, so
-       # that if they are removed or otherwise changed, the inline will be
-       # sure to be updated.
+       # that if they are removed, the inline will be sure to be updated.
        foreach my $p ($#list >= $#feedlist ? @list : @feedlist) {
-               add_depends($params{page}, $p);
+               add_depends($params{page}, $p, presence => $quick);
        }
        
        if ($feeds && exists $params{feedpages}) {
index 941ed5f3672145bb6111c5a6650fb14431641297..d0671ae0e93b7877aef75b314158145ce61770db 100644 (file)
@@ -28,8 +28,8 @@ sub preprocess (@) {
 
        $params{pages}="*" unless defined $params{pages};
        
-       # Needs to update whenever a page is added or removed, so
-       # register a dependency.
+       # Needs to update whenever a page is added, removed, or
+       # its links change, so register a dependency.
        add_depends($params{page}, $params{pages});
        
        # Can't just return the linkmap here, since the htmlscrubber
index bd73f1a04e728638c19994c0d64a4f288a120150..4023ed7d75fd4b2582b7fe00265a5ac6a449619c 100644 (file)
@@ -84,7 +84,7 @@ sub preprocess (@) {
        foreach my $plugin (@pluginlist) {
                $result .= '<li class="listdirectives">';
                my $link=linkpage($config{directive_description_dir}."/".$plugin);
-               add_depends($params{page}, $link);
+               add_depends($params{page}, $link, presence => 1);
                $result .= htmllink($params{page}, $params{destpage}, $link);
                $result .= '</li>';
        }
index 54146dc467bdcb513e426257a6fd0879005cd35a..625cfdfcaaa723a80f7c15e155d70d0583d2b9b7 100644 (file)
@@ -68,13 +68,13 @@ sub preprocess (@) {
        }
 
        # Needs to update whenever a page is added or removed (or in some
-       # cases, when its content changes, if show=title), so register a
-       # dependency.
-       add_depends($params{page}, $params{pages});
+       # cases, when its content changes, if show= is specified), so
+       # register a dependency.
+       add_depends($params{page}, $params{pages}, presence => ! exists $params{show});
        # Explicitly add all currently shown pages, to detect when pages
        # are removed.
        foreach my $item (keys %mapitems) {
-               add_depends($params{page}, $item);
+               add_depends($params{page}, $item, presence => ! exists $params{show});
        }
 
        # Create the map.
index 514b0936907ce58edabcbafcb44d5559a154eec9..9b041a748c35590820f89f17a65b6e183dbcf59c 100644 (file)
@@ -195,7 +195,7 @@ sub preprocess (@) {
                        if (! length $link) {
                                error gettext("redir page not found")
                        }
-                       add_depends($page, $link);
+                       add_depends($page, $link, presence => 1);
 
                        $value=urlto($link, $page);
                        $value.='#'.$redir_anchor if defined $redir_anchor;
index 71122677273275d3e2f160bde3c8b44a8f333e49..ae330b23b81e78abf227cbcb0a5cb757088e485a 100644 (file)
@@ -23,9 +23,13 @@ sub preprocess (@) {
        my %params=@_;
        $params{pages}="*" unless defined $params{pages};
        
-       # Needs to update whenever a page is added or removed, so
-       # register a dependency.
-       add_depends($params{page}, $params{pages});
+       # Needs to update whenever a link changes, on any page
+       # since any page could link to one of the pages we're
+       # considering as orphans.
+       add_depends($params{page}, "*", links => 1);
+       # Also needs to update whenever potential orphans are added or
+       # removed.
+       add_depends($params{page}, $params{pages}, presence => 1);
        
        my @orphans;
        foreach my $page (pagespec_match_list(
index 5a2301af49232943d4f0889c0f3b9ba28f05396f..80561350b5fe44d7d282fb5fac6b5c9ac8e295ea 100644 (file)
@@ -23,8 +23,8 @@ sub preprocess (@) {
        $params{pages}="*" unless defined $params{pages};
        
        # Needs to update count whenever a page is added or removed, so
-       # register a dependency.
-       add_depends($params{page}, $params{pages});
+       # register a presence dependency.
+       add_depends($params{page}, $params{pages}, presence => 1);
        
        my @pages;
        if ($params{pages} eq "*") {
index 874ead7e6de669c1d33c66b74984ef90504f9b8e..0765c1cfa1ac566343923bd8e1491fa822a29324 100644 (file)
@@ -35,10 +35,13 @@ sub preprocess (@) {
        $params{pages}="*" unless defined $params{pages};
        my $style = ($params{style} or 'cloud');
        
-       # Needs to update whenever a page is added or removed, so
-       # register a dependency.
-       add_depends($params{page}, $params{pages});
-       add_depends($params{page}, $params{among}) if exists $params{among};
+       # Needs to update whenever a page is added or removed.
+       add_depends($params{page}, $params{pages}, exists => 1);
+       # Also needs to update when any page with links changes, 
+       # in case the links point to our displayed pages.
+       # (Among limits this further.)
+       add_depends($params{page}, exists $params{among} ? $params{among} : "*",
+               links => 1); 
        
        my %counts;
        my $max = 0;
index d2e5c23788678efb7819e10c59964bd852663e06..3205958d40dcb5f8b0a510b58d14fba95423c1ff 100644 (file)
@@ -48,7 +48,7 @@ sub preprocess (@) {
                error gettext("unknown formula");
        }
 
-       add_depends($params{page}, $params{pages});
+       add_depends($params{page}, $params{pages}, presence => 1);
 
        my @list=sort { $params{timehash}->{$b} <=> $params{timehash}->{$a} } 
                pagespec_match_list(
index 76d994acc7ad0f9d49c63c32d3d5d8dd54db5554..26c537a84022ae51001beb9be86db90b1da25e0a 100644 (file)
@@ -36,8 +36,8 @@ sub preprocess (@) {
                $fill.="%";
        }
        elsif (defined $params{totalpages} and defined $params{donepages}) {
-               add_depends($params{page}, $params{totalpages});
-               add_depends($params{page}, $params{donepages});
+               add_depends($params{page}, $params{totalpages}, presence => 1);
+               add_depends($params{page}, $params{donepages}, presence => 1);
 
                my @pages=keys %pagesources;
                my $totalcount=0;
index 246c2260d7748e9fe2b3522d73035564e0fb6754..599bb26e2b382a7b139f3fc415db9442a11c195a 100644 (file)
@@ -58,6 +58,52 @@ sub backlinks ($) {
        return @links;
 }
 
+sub find_changed_links (@_) {
+       my %linkchanged;
+       my %linkchangers;
+       foreach my $file (@_) {
+               my $page=pagename($file);
+       
+               if (exists $links{$page}) {
+                       foreach my $l (@{$links{$page}}) {
+                               my $link=bestlink($page, $l);
+                               if (length $link) {
+                                       if (! exists $oldlinks{$page} ||
+                                           ! grep { bestlink($page, $_) eq $link } @{$oldlinks{$page}}) {
+                                               $linkchanged{$link}=1;
+                                               $linkchangers{lc($page)}=1;
+                                       }
+                               }
+                               else {
+                                       if (! grep { lc $_ eq lc $l } @{$oldlinks{$page}}) {
+                                               $linkchangers{lc($page)}=1
+                                       }
+                               }
+                               
+                       }
+               }
+               if (exists $oldlinks{$page}) {
+                       foreach my $l (@{$oldlinks{$page}}) {
+                               my $link=bestlink($page, $l);
+                               if (length $link) {
+                                       if (! exists $links{$page} || 
+                                           ! grep { bestlink($page, $_) eq $link } @{$links{$page}}) {
+                                               $linkchanged{$link}=1;
+                                               $linkchangers{lc($page)}=1;
+                                       }
+                               }
+                               else {
+                                       if (! grep { lc $_ eq lc $l } @{$links{$page}}) {
+                                               $linkchangers{lc($page)}=1
+                                       }
+                               }
+                       }
+               }
+       }
+
+       return \%linkchanged, \%linkchangers;
+}
+
 sub genpage ($$) {
        my $page=shift;
        my $content=shift;
@@ -342,7 +388,7 @@ sub refresh () {
        run_hooks(refresh => sub { shift->() });
        my ($files, $exists)=find_src_files();
 
-       my (%rendered, @add, @del, @internal);
+       my (%rendered, @add, @del, @internal, @internal_change);
        # check for added or removed pages
        foreach my $file (@$files) {
                my $page=pagename($file);
@@ -407,7 +453,7 @@ sub refresh () {
                    $forcerebuild{$page}) {
                        $pagemtime{$page}=$stat[9];
                        if (isinternal($page)) {
-                               push @internal, $file;
+                               push @internal_change, $file;
                                # Preprocess internal page in scan-only mode.
                                preprocess($page, $page, readfile($srcfile), 1);
                        }
@@ -429,7 +475,7 @@ sub refresh () {
                render($file);
                $rendered{$file}=1;
        }
-       foreach my $file (@internal) {
+       foreach my $file (@internal, @internal_change) {
                # internal pages are not rendered
                my $page=pagename($file);
                delete $depends{$page};
@@ -454,79 +500,90 @@ sub refresh () {
                }
        }
 
-       if (%rendered || @del || @internal) {
+       if (%rendered || @del || @internal || @internal_change) {
                my @changed=(keys %rendered, @del);
+               my ($linkchanged, $linkchangers)=find_changed_links(@changed);
 
-               my %lcchanged = map { lc(pagename($_)) => 1 } @changed;
-               # rebuild dependant pages
-               foreach my $f (@$files) {
-                       next if $rendered{$f};
-                       my $p=pagename($f);
-                       my $reason = undef;
-
-                       if (exists $depends_simple{$p}) {
-                               foreach my $d (keys %{$depends_simple{$p}}) {
-                                       if (exists $lcchanged{$d}) {
-                                               $reason = $d;
-                                               last;
-                                       }
-                               }
-                       }
-
-                       if (exists $depends{$p} && ! defined $reason) {
-                               D: foreach my $d (keys %{$depends{$p}}) {
-                                       my $sub=pagespec_translate($d);
-                                       next if $@ || ! defined $sub;
-
-                                       # only consider internal files
-                                       # if the page explicitly depends
-                                       # on such files
-                                       foreach my $file (@changed, $d =~ /internal\(/ ? @internal : ()) {
-                                               next if $file eq $f;
-                                               my $page=pagename($file);
-                                               if ($sub->($page, location => $p)) {
-                                                       $reason = $page;
-                                                       last D;
+               my $unsettled;
+               do {
+                       $unsettled=0;
+                       @changed=(keys %rendered, @del);
+                       my @exists_changed=(@add, @del);
+       
+                       my %lc_changed = map { lc(pagename($_)) => 1 } @changed;
+                       my %lc_exists_changed = map { lc(pagename($_)) => 1 } @exists_changed;
+        
+                       # rebuild dependant pages
+                       foreach my $f (@$files) {
+                               next if $rendered{$f};
+                               my $p=pagename($f);
+                               my $reason = undef;
+       
+                               if (exists $depends_simple{$p}) {
+                                       foreach my $d (keys %{$depends_simple{$p}}) {
+                                               if (($depends_simple{$p}{$d} & $IkiWiki::DEPEND_CONTENT &&
+                                                    $lc_changed{$d})
+                                                   ||
+                                                   ($depends_simple{$p}{$d} & $IkiWiki::DEPEND_PRESENCE &&
+                                                    $lc_exists_changed{$d})
+                                                   ||
+                                                   ($depends_simple{$p}{$d} & $IkiWiki::DEPEND_LINKS &&
+                                                    $linkchangers->{$d})
+                                               ) {
+                                                       $reason = $d;
+                                                       last;
                                                }
                                        }
                                }
-                       }
+       
+                               if (exists $depends{$p} && ! defined $reason) {
+                                       D: foreach my $d (keys %{$depends{$p}}) {
+                                               my $sub=pagespec_translate($d);
+                                               next if $@ || ! defined $sub;
+       
+                                               # only consider internal files
+                                               # if the page explicitly depends
+                                               # on such files
+                                               my $internal_dep=$d =~ /internal\(/;
+
+                                               my @candidates;
+                                               if ($depends{$p}{$d} & $IkiWiki::DEPEND_PRESENCE) {
+                                                       @candidates=@exists_changed;
+                                                       push @candidates, @internal
+                                                               if $internal_dep;
+                                               }
+                                               if (($depends{$p}{$d} & ($IkiWiki::DEPEND_CONTENT | $IkiWiki::DEPEND_LINKS))) {
+                                                       @candidates=@changed;
+                                                       push @candidates, @internal, @internal_change
+                                                               if $internal_dep;
+                                               }
 
-                       if (defined $reason) {
-                               debug(sprintf(gettext("building %s, which depends on %s"), $f, $reason));
-                               render($f);
-                               $rendered{$f}=1;
-                       }
-               }
-               
-               # handle backlinks; if a page has added/removed links,
-               # update the pages it links to
-               my %linkchanged;
-               foreach my $file (@changed) {
-                       my $page=pagename($file);
-                       
-                       if (exists $links{$page}) {
-                               foreach my $link (map { bestlink($page, $_) } @{$links{$page}}) {
-                                       if (length $link &&
-                                           (! exists $oldlinks{$page} ||
-                                            ! grep { bestlink($page, $_) eq $link } @{$oldlinks{$page}})) {
-                                               $linkchanged{$link}=1;
+                                               foreach my $file (@candidates) {
+                                                       next if $file eq $f;
+                                                       my $page=pagename($file);
+                                                       if ($sub->($page, location => $p)) {
+                                                               if ($depends{$p}{$d} & $IkiWiki::DEPEND_LINKS) {
+                                                                       next unless $linkchangers->{lc($page)};
+                                                               }
+                                                               $reason = $page;
+                                                               last D;
+                                                       }
+                                               }
                                        }
                                }
-                       }
-                       if (exists $oldlinks{$page}) {
-                               foreach my $link (map { bestlink($page, $_) } @{$oldlinks{$page}}) {
-                                       if (length $link &&
-                                           (! exists $links{$page} || 
-                                            ! grep { bestlink($page, $_) eq $link } @{$links{$page}})) {
-                                               $linkchanged{$link}=1;
-                                       }
+       
+                               if (defined $reason) {
+                                       debug(sprintf(gettext("building %s, which depends on %s"), $f, $reason));
+                                       render($f);
+                                       $rendered{$f}=1;
+                                       $unsettled=1;
+                                       last;
                                }
                        }
-               }
-
-               foreach my $link (keys %linkchanged) {
+               } while $unsettled;
+               
+               # update backlinks at end
+               foreach my $link (keys %{$linkchanged}) {
                        my $linkfile=$pagesources{$link};
                        if (defined $linkfile) {
                                next if $rendered{$linkfile};
index 808105fd5b1e7cbc49306bd81bb5b15be2d79490..520f3881535efb4afc1a5619ac7c86ab200bad0c 100644 (file)
@@ -1,3 +1,13 @@
+ikiwiki (3.14159266) UNRELEASED; urgency=low
+
+  To take advantage of significant performance improvements, all
+  wikis need to be rebuilt on upgrade to this version. If you
+  listed your wiki in /etc/ikiwiki/wikilist this will be done
+  automatically when the Debian package is upgraded. Or use
+  ikiwiki-mass-rebuild to force a rebuild.
+
+ -- Joey Hess <joeyh@debian.org>  Mon, 05 Oct 2009 16:48:59 -0400
+
 ikiwiki (3.1415926) unstable; urgency=low
 
   In order to fix a performance bug, all wikis need to be rebuilt on
index ca5409af7949f7a7e6eb900ab697892c57134e25..89d377769aadbd298152bd33909b3ad4aebdad4f 100644 (file)
@@ -10,6 +10,25 @@ ikiwiki (3.14159266) UNRELEASED; urgency=low
   * mirrorlist: Display nothing if list is empty.
   * Fix a bug that could lead to duplicate links being recorded
     for tags.
+  * Added support framework for multiple types of dependencies.
+  * Allow declaring that a dependency is only affected by page presence
+    or changes to its links.
+    (By passing presence => 1 or links => 1 to add_depends.)
+  * pagecount, calendar, postsparkline, progress: Use a presence dependency,
+    which makes these directives much less expensive to use, since page
+    edits will no longer trigger an unnecessary update.
+  * map: Use a presence dependency unless show= is specified.
+    This makes maps efficient enough that they can be used on sidebars!
+  * inline: Use a presence dependency in quick mode.
+  * brokenlinks: Use a link dependency.
+    This makes it much more efficient, only updating when really necessary.
+  * orphans, pagestats: Use a combination of presence and link dependencies.
+    This makes them more efficient. It also fixes a longstanding bug,
+    where if only a small set of pages were considered by orphans/pagestats,
+    changes to links on other pages failed to cause an update.
+  * Transitive dependencies are now correctly supported.
+  * Rebuild wikis on upgrade to this version to get improved dependency
+    info.
 
  -- Joey Hess <joeyh@debian.org>  Sun, 27 Sep 2009 17:40:03 -0400
 
index 2ba26e5b65010cee1649390240228a3d939b7afb..dd4be6e0e7948d408551a38ca0e886828189b102 100755 (executable)
@@ -4,7 +4,7 @@ set -e
 
 # Change this when some incompatible change is made that requires
 # rebuilding all wikis.
-firstcompat=3.1415926
+firstcompat=3.14159266
 
 if [ "$1" = configure ] && \
    dpkg --compare-versions "$2" lt "$firstcompat"; then
index 5ce4a93d22707e69a0f546c9e850bf54dba444e1..fee65c0deaa67cc60269eaef7e31414d727618fc 100644 (file)
@@ -11,3 +11,6 @@ calculation in refresh(), which doesn't detect that the link has changed in
 this case.
 
 Still true in 1.43 although the code is much different now..
+
+> Still true as of 031d1bf5046ab77c796477a19967e7c0c512c417, 
+> and now this same problem also affects link dependencies.
index 9586bc9b02767a29ceaaa5050a034a6b0f0fff45..0a2e9ec2896176658a2a94f7c44b5921d54b03b8 100644 (file)
@@ -65,4 +65,4 @@ Downsides here:
   modification to plugins/brokenlinks causes an unnecessary update of
   plugins, and could be solved by adding more dependency types.)
 
---[[Joey]] 
+[[done]] --[[Joey]] 
index 99f795972dcf8f4d8010a7807763834be2d01880..4e087ab6c5e1272b69b416aa0ccbcffb3cdcaa44 100644 (file)
@@ -98,7 +98,7 @@ Here are some less often needed parameters:
 * `feedonly` - Only generate the feed, do not display the pages inline on
   the page.
 * `quick` - Build archives in quick mode, without reading page contents for
-  metadata. By default, this also turns off generation of any feeds.
+  metadata. This also turns off generation of any feeds.
 * `timeformat` - Use this to specify how to display the time or date for pages
   in the blog. The format string is passed to the strftime(3) function.
 * `feedpages` - A [[PageSpec]] of inlined pages to include in the rss/atom
index 66f851dbd9204250389b9fdb888d9fb12a485658..f14c80b0798d85b6e6279622b1fe6d774b5fdad0 100644 (file)
@@ -12,13 +12,13 @@ And here's how to create a table of all the pages on the wiki:
 
        \[[!pagestats style="table"]]
 
-The optional `among` parameter limits display to pages that match a
-[[ikiwiki/PageSpec]]. For instance, to display a cloud of tags used on blog
-entries, you could use:
+The optional `among` parameter limits the pages whose outgoing links are
+considered. For instance, to display a cloud of tags used on blog
+entries, while ignoring other pages that use those tags, you could use:
 
        \[[!pagestats pages="tags/*" among="blog/posts/*"]]
 
-or to display a cloud of tags related to Linux, you could use:
+Or to display a cloud of tags related to Linux, you could use:
 
        \[[!pagestats pages="tags/* and not tags/linux" among="tagged(linux)"]]
 
index 527568208913e1c379c1ae31b79e2202173dab58..697b4a21964c884703bf616c6626fb82ba4b1f1d 100644 (file)
@@ -1,7 +1,7 @@
 Most of ikiwiki's [[features]] are implemented as plugins. Many of these 
 plugins are included with ikiwiki.
 
-[[!pagestats pages="plugins/type/* and !plugins/type/slow"]]
+[[!pagestats pages="plugins/type/* and !plugins/type/slow" among="plugins/*"]]
 
 There's documentation if you want to [[write]] your own plugins, or you can
 [[install]] plugins [[contributed|contrib]] by others.
@@ -13,7 +13,5 @@ will fit most uses of ikiwiki.
 
 ## Plugin directory
 
-[[!inline pages="plugins/* and !plugins/type/* and !plugins/write and 
-!plugins/write/* and !plugins/contrib and !plugins/install and !*/Discussion"
-feedpages="created_after(plugins/graphviz)" archive="yes" sort=title
-rootpage="plugins/contrib" postformtext="Add a new plugin named:" show=0]]
+[[!map pages="plugins/* and !plugins/type/* and !plugins/write and 
+!plugins/write/* and !plugins/contrib and !plugins/install and !*/Discussion"]]
index a03e6a95d72993abf28aeb5b68a40ed0cbbf4dc2..ac6c1b75134d65c035c22642a82e19d72c0ff3eb 100644 (file)
@@ -1,6 +1,4 @@
 These plugins are provided by third parties and are not currently
 included in ikiwiki. See [[install]] for installation help.
 
-[[!inline pages="plugins/contrib/* and !*/Discussion" 
-feedpages="created_after(plugins/contrib/navbar)" archive="yes"
-rootpage="plugins/contrib" postformtext="Add a new plugin named:" show=0]]
+[[!map pages="plugins/contrib/* and !*/Discussion"]]
index ea7c4df13f42aba0a83a3b957cd3fcc621d89e69..e403c2d189249f8bd6069ab06d594cab3a177253 100644 (file)
@@ -10,5 +10,6 @@ Here's a list of orphaned pages on this wiki:
 
 [[!orphans pages="* and !news/* and !todo/* and !bugs/* and !users/* and
 !recentchanges and !examples/* and !tips/* and !sandbox/* and !templates/* and
+!forum/* and !*.js and
 !wikiicons/* and !plugins/*"]]
 """]]
index 36982eff386a31e91eaa3fade012795bb9fd61c1..4e356d65a8984885a256788f82f1f2173ffeaa67 100644 (file)
@@ -16,6 +16,10 @@ will turn off the sidebar altogether.
 
 Warning: Any change to the sidebar will cause a rebuild of the whole wiki,
 since every page includes a copy that has to be updated. This can
-especially be a problem if the sidebar includes [[inline]] or [[map]]
-directives, since any changes to pages inlined or mapped onto the sidebar
+especially be a problem if the sidebar includes an [[ikiwiki/directive/inline]]
+directive, since any changes to pages inlined into the sidebar
 will change the sidebar and cause a full wiki rebuild.
+
+Instead, if you include a [[ikiwiki/directive/map]] directive on the sidebar,
+and it does not use the `show` parameter, only adding or removing pages
+included in the map will cause a full rebuild. Modifying pages will not.
index 668f8d8b69dff65acc0727fa5fa3233e516b132a..133030f08fc5bef55dc7dcf2fbca03add189a2db 100644 (file)
@@ -609,10 +609,22 @@ page created from it. (Ie, it appends ".html".)
 Use this when constructing the filename of a html file. Use `urlto` when
 generating a link to a page.
 
-#### `add_depends($$)`
+#### `add_depends($$;@)`
 
 Makes the specified page depend on the specified [[ikiwiki/PageSpec]].
 
+By default, dependencies are full content dependencies, meaning that the
+page will be updated whenever anything matching the PageSpec is modified.
+This default can be overridden by additional named parameters, which can be
+used to indicate weaker types of dependencies:
+
+* `presence` if set to true, only the presence of a matching page triggers
+  the dependency.
+* `links` if set to true, any change to links on a matching page
+  triggers the dependency. This includes when a link is added, removed,
+  or changes what it points to due to other changes. It does not include
+  the addition or removal of a duplicate link.
+
 #### `pagespec_match($$;@)`
 
 Passed a page name, and [[ikiwiki/PageSpec]], returns true if the
index f13f1448eeded4732c356317b37471e90dd707db..f46a6a7c62ff6d7b940db1b956c4d2170df8f3d8 100644 (file)
@@ -162,14 +162,13 @@ Link dependencies:
 
 * `add_depends($page, $spec, links => 1, presence => 1)`
   adds a links + presence dependency.
-* `refresh` only rebuilds a page with a links dependency if
-  pages matched by the pagespec gain or lose links. (What the link
-  actually points to may change independent of this, due to changes
-  elsewhere, without it firing.)
+* Use backlinks change code to detect changes to link dependencies too.
 * So, brokenlinks can fire whenever any links in any of the
   pages it's tracking change, or when pages are added or
   removed.
 
 TODO: How to determine if a pagespec is valid to be used with a links
 dependency? Use the same simple pagespecs that are valid for presence
-dependencies?
+dependencies? Seems ok.
+
+[[done]]
index 8a20cf655f021b328eef5afe509a75908f202d2a..1bebb1176d99569341a6735e83c4cb51c14e1ffc 100755 (executable)
@@ -299,7 +299,7 @@ sub oldloadindex {
                        $pagemtime{$page}=$items{mtime}[0];
                        $oldlinks{$page}=[@{$items{link}}];
                        $links{$page}=[@{$items{link}}];
-                       $depends{$page}={ $items{depends}[0] => 1 } if exists $items{depends};
+                       $depends{$page}={ $items{depends}[0] => $IkiWiki::DEPEND_CONTENT } if exists $items{depends};
                        $destsources{$_}=$page foreach @{$items{dest}};
                        $renderedfiles{$page}=[@{$items{dest}}];
                        $pagecase{lc $page}=$page;
diff --git a/t/add_depends.t b/t/add_depends.t
new file mode 100644 (file)
index 0000000..935a579
--- /dev/null
@@ -0,0 +1,68 @@
+#!/usr/bin/perl
+use warnings;
+use strict;
+use Test::More tests => 50;
+
+BEGIN { use_ok("IkiWiki"); }
+%config=IkiWiki::defaultconfig();
+$config{srcdir}=$config{destdir}="/dev/null";
+IkiWiki::checkconfig();
+
+# avoids adding an unparseable pagespec
+ok(! add_depends("foo", "foo and (bar"));
+ok(! add_depends("foo", "foo another"));
+
+# simple and not-so-simple dependencies split
+ok(add_depends("foo", "*"));
+ok(add_depends("foo", "bar"));
+ok(add_depends("foo", "BAZ"));
+ok(exists $IkiWiki::depends_simple{foo}{"bar"});
+ok(exists $IkiWiki::depends_simple{foo}{"baz"}); # lowercase
+ok(! exists $IkiWiki::depends_simple{foo}{"*"});
+ok(! exists $IkiWiki::depends{foo}{"bar"});
+ok(! exists $IkiWiki::depends{foo}{"baz"});
+
+# default dependencies are content dependencies
+ok($IkiWiki::depends{foo}{"*"} & $IkiWiki::DEPEND_CONTENT);
+ok(! ($IkiWiki::depends{foo}{"*"} & ($IkiWiki::DEPEND_PRESENCE | $IkiWiki::DEPEND_LINKS)));
+ok($IkiWiki::depends_simple{foo}{"bar"} & $IkiWiki::DEPEND_CONTENT);
+ok(! ($IkiWiki::depends_simple{foo}{"bar"} & ($IkiWiki::DEPEND_PRESENCE | $IkiWiki::DEPEND_LINKS)));
+
+# adding other dep types standalone
+ok(add_depends("foo2", "*", presence => 1));
+ok(add_depends("foo2", "bar", links => 1));
+ok($IkiWiki::depends{foo2}{"*"} & $IkiWiki::DEPEND_PRESENCE);
+ok(! ($IkiWiki::depends{foo2}{"*"} & ($IkiWiki::DEPEND_CONTENT | $IkiWiki::DEPEND_LINKS)));
+ok($IkiWiki::depends_simple{foo2}{"bar"} & $IkiWiki::DEPEND_LINKS);
+ok(! ($IkiWiki::depends_simple{foo2}{"bar"} & ($IkiWiki::DEPEND_PRESENCE | $IkiWiki::DEPEND_CONTENT)));
+
+# adding combined dep types
+ok(add_depends("foo2", "baz", links => 1, presence => 1));
+ok($IkiWiki::depends_simple{foo2}{"baz"} & $IkiWiki::DEPEND_LINKS);
+ok($IkiWiki::depends_simple{foo2}{"baz"} & $IkiWiki::DEPEND_PRESENCE);
+ok(! ($IkiWiki::depends_simple{foo2}{"baz"} & $IkiWiki::DEPEND_CONTENT));
+
+# adding a pagespec that requires page metadata should cause a fallback to
+# a content dependency
+foreach my $spec ("* and ! link(bar)", "* or link(bar)", "unknownspec()",
+       "title(hi)",
+       "* or backlink(yo)", # this one could actually be acceptably be
+                            # detected to not need a content dep .. in
+                            # theory!
+       ) {
+       ok(add_depends("foo3", $spec, presence => 1));
+       ok($IkiWiki::depends{foo3}{$spec} & $IkiWiki::DEPEND_CONTENT);
+       ok(! ($IkiWiki::depends{foo3}{$spec} & ($IkiWiki::DEPEND_PRESENCE | $IkiWiki::DEPEND_LINKS)));
+}
+
+# adding dep types to existing dependencies should merge the flags
+ok(add_depends("foo2", "baz"));
+ok($IkiWiki::depends_simple{foo2}{"baz"} & $IkiWiki::DEPEND_LINKS);
+ok($IkiWiki::depends_simple{foo2}{"baz"} & $IkiWiki::DEPEND_PRESENCE);
+ok(($IkiWiki::depends_simple{foo2}{"baz"} & $IkiWiki::DEPEND_CONTENT));
+ok(add_depends("foo2", "bar", presence => 1)); # had only links before
+ok($IkiWiki::depends_simple{foo2}{"bar"} & ($IkiWiki::DEPEND_LINKS | $IkiWiki::DEPEND_PRESENCE));
+ok(! ($IkiWiki::depends_simple{foo2}{"bar"} & $IkiWiki::DEPEND_CONTENT));
+ok(add_depends("foo", "bar", links => 1)); # had only content before
+ok($IkiWiki::depends{foo}{"*"} & ($IkiWiki::DEPEND_CONTENT | $IkiWiki::DEPEND_LINKS));
+ok(! ($IkiWiki::depends{foo}{"*"} & $IkiWiki::DEPEND_PRESENCE));