X-Git-Url: http://git.vanrenterghem.biz/git.ikiwiki.info.git/blobdiff_plain/7a8b492bcce45123cef45ebe6f97cd70b38b9db7..d5f014449511460d4da22591379b30bd5de86145:/IkiWiki.pm?ds=inline diff --git a/IkiWiki.pm b/IkiWiki.pm index 7b1d24c6a..c735b26c8 100644 --- a/IkiWiki.pm +++ b/IkiWiki.pm @@ -17,11 +17,12 @@ use vars qw{%config %links %oldlinks %pagemtime %pagectime %pagecase %forcerebuild %loaded_plugins}; use Exporter q{import}; -our @EXPORT = qw(hook debug error template htmlpage add_depends pagespec_match - pagespec_match_list bestlink htmllink readfile writefile - pagetype srcfile pagename displaytime will_render gettext urlto - targetpage add_underlay pagetitle titlepage linkpage - newpagefile inject add_link +our @EXPORT = qw(hook debug error template htmlpage deptype use_pagespec + add_depends pagespec_match pagespec_match_list bestlink + htmllink readfile writefile pagetype srcfile pagename + displaytime will_render gettext urlto targetpage + add_underlay pagetitle titlepage linkpage newpagefile + inject add_link %config %links %pagestate %wikistate %renderedfiles %pagesources %destsources); our $VERSION = 3.00; # plugin interface version, next is ikiwiki version @@ -1768,59 +1769,135 @@ sub rcs_receive () { $hooks{rcs}{rcs_receive}{call}->(); } -sub add_depends ($$;@) { +sub add_depends ($$;$) { my $page=shift; my $pagespec=shift; + my $deptype=shift || $DEPEND_CONTENT; # Is the pagespec a simple page name? - my $simple=$pagespec =~ /$config{wiki_file_regexp}/ && - $pagespec !~ /[\s*?()!]/; + if ($pagespec =~ /$config{wiki_file_regexp}/ && + $pagespec !~ /[\s*?()!]/) { + $depends_simple{$page}{lc $pagespec} |= $deptype; + return 1; + } - my $deptype=0; - if (@_) { - my %params=@_; - - if ($params{presence}) { - # Is the pagespec limited to terms that will continue - # to match pages as long as those pages exist? - my $presence_limited=1; - while ($presence_limited && $pagespec=~m/(\w+)\([^\)]*\)/g) { - $presence_limited = $1 =~ /^(glob|internal|creation_month|creation_day|creation_year|created_before|created_after)$/; - } - if ($presence_limited) { - $deptype=$deptype | $DEPEND_PRESENCE; - } - else { - $deptype=$deptype | $DEPEND_CONTENT; - } + # Analyse the pagespec, and match it against all pages + # to get a list of influences, and add explicit dependencies + # for those. + #my $sub=pagespec_translate($pagespec); + #return if $@; + #foreach my $p (keys %pagesources) { + # my $r=$sub->($p, location => $page ); + # my %i=$r->influences; + # foreach my $i (keys %i) { + # $depends_simple{$page}{lc $i} |= $i{$i}; + # } + #} + print STDERR "warning: use of add_depends; influences not tracked\n"; + + $depends{$page}{$pagespec} |= $deptype; + return 1; +} + +sub use_pagespec ($$;@) { + my $page=shift; + my $pagespec=shift; + my %params=@_; + + my $sub=pagespec_translate($pagespec); + error "syntax error in pagespec \"$pagespec\"" + if $@ || ! defined $sub; + + my @candidates; + if (exists $params{limit}) { + @candidates=grep { $params{limit}->($_) } keys %pagesources; + } + else { + @candidates=keys %pagesources; + } + + if (defined $params{sort}) { + my $f; + if ($params{sort} eq 'title') { + $f=sub { pagetitle(basename($a)) cmp pagetitle(basename($b)) }; } - if ($params{links}) { - # Is the pagespec limited to terms that will continue - # to match pages as long as those pages exist and - # link to the same places? - my $links_limited=1; - while ($links_limited && $pagespec=~m/(\w+)\([^\)]*\)/g) { - $links_limited = $1 =~ /^(glob|internal|creation_month|creation_day|creation_year|created_before|created_after|backlink)$/; - } - if ($links_limited) { - $deptype=$deptype | $DEPEND_LINKS; + elsif ($params{sort} eq 'title_natural') { + eval q{use Sort::Naturally}; + if ($@) { + error(gettext("Sort::Naturally needed for title_natural sort")); } - else { - $deptype=$deptype | $DEPEND_CONTENT; + $f=sub { Sort::Naturally::ncmp(pagetitle(basename($a)), pagetitle(basename($b))) }; + } + elsif ($params{sort} eq 'mtime') { + $f=sub { $pagemtime{$b} <=> $pagemtime{$a} }; + } + elsif ($params{sort} eq 'age') { + $f=sub { $pagectime{$b} <=> $pagectime{$a} }; + } + else { + error sprintf(gettext("unknown sort type %s"), $params{sort}); + } + @candidates = sort { &$f } @candidates; + } + + @candidates=reverse(@candidates) if $params{reverse}; + + my @matches; + my $firstfail; + my $count=0; + foreach my $p (@candidates) { + my $r=$sub->($p, location => $page); + if ($r) { + push @matches, [$p, $r]; + last if defined $params{num} && ++$count == $params{num}; + } + elsif (! defined $firstfail) { + $firstfail=$r; + } + } + + $depends{$page}{$pagespec} |= ($params{deptype} || $DEPEND_CONTENT); + + my @ret; + if (@matches) { + # Add all influences from successful matches. + foreach my $m (@matches) { + push @ret, $m->[0]; + my %i=$m->[1]->influences; + foreach my $i (keys %i) { + $depends_simple{$page}{lc $i} |= $i{$i}; } } } - $deptype=$DEPEND_CONTENT unless $deptype; + elsif (defined $firstfail) { + # Add influences from one failure. (Which one should not + # matter; all should have the same influences.) + my %i=$firstfail->influences; + foreach my $i (keys %i) { + $depends_simple{$page}{lc $i} |= $i{$i}; + } - if ($simple) { - $depends_simple{$page}{lc $pagespec} |= $deptype; - return 1; + error(sprintf(gettext("cannot match pages: %s"), $firstfail)) + if $firstfail->isa("IkiWiki::ErrorReason"); } - return unless pagespec_valid($pagespec); + return @ret; +} - $depends{$page}{$pagespec} |= $deptype; - return 1; +sub deptype (@) { + my $deptype=0; + foreach my $type (@_) { + if ($type eq 'presence') { + $deptype |= $DEPEND_PRESENCE; + } + elsif ($type eq 'links') { + $deptype |= $DEPEND_LINKS; + } + elsif ($type eq 'content') { + $deptype |= $DEPEND_CONTENT; + } + } + return $deptype; } sub file_pruned ($$) { @@ -1926,10 +2003,10 @@ sub pagespec_translate ($) { }gx) { my $word=$1; if (lc $word eq 'and') { - $code.=' &&'; + $code.=' &'; } elsif (lc $word eq 'or') { - $code.=' ||'; + $code.=' |'; } elsif ($word eq "(" || $word eq ")" || $word eq "!") { $code.=' '.$word; @@ -2015,36 +2092,54 @@ sub glob2re ($) { package IkiWiki::FailReason; use overload ( - '""' => sub { ${$_[0]} }, + '""' => sub { $_[0][0] }, '0+' => sub { 0 }, '!' => sub { bless $_[0], 'IkiWiki::SuccessReason'}, + '&' => sub { $_[0]->merge_influences($_[1]); $_[0] }, + '|' => sub { $_[1]->merge_influences($_[0]); $_[1] }, fallback => 1, ); -sub new { - my $class = shift; - my $value = shift; - return bless \$value, $class; -} - -package IkiWiki::ErrorReason; - -our @ISA = 'IkiWiki::FailReason'; +our @ISA = 'IkiWiki::SuccessReason'; package IkiWiki::SuccessReason; use overload ( - '""' => sub { ${$_[0]} }, + '""' => sub { $_[0][0] }, '0+' => sub { 1 }, '!' => sub { bless $_[0], 'IkiWiki::FailReason'}, + '&' => sub { $_[1]->merge_influences($_[0]); $_[1] }, + '|' => sub { $_[0]->merge_influences($_[1]); $_[0] }, fallback => 1, ); sub new { my $class = shift; my $value = shift; - return bless \$value, $class; -}; + return bless [$value, {@_}], $class; +} + +sub influences { + my $this=shift; + if (! @_) { + return %{$this->[1]}; + } + else { + $this->[1]={@_}; + } +} + +sub merge_influences { + my $this=shift; + my $other=shift; + foreach my $influence (keys %{$other->[1]}) { + $this->[1]{$influence} |= $other->[1]{$influence}; + } +} + +package IkiWiki::ErrorReason; + +our @ISA = 'IkiWiki::FailReason'; package IkiWiki::PageSpec; @@ -2095,19 +2190,20 @@ sub match_link ($$;@) { my $from=exists $params{location} ? $params{location} : ''; my $links = $IkiWiki::links{$page}; - return IkiWiki::FailReason->new("$page has no links") unless $links && @{$links}; + return IkiWiki::FailReason->new("$page has no links") + unless $links && @{$links}; my $bestlink = IkiWiki::bestlink($from, $link); foreach my $p (@{$links}) { if (length $bestlink) { - return IkiWiki::SuccessReason->new("$page links to $link") + return IkiWiki::SuccessReason->new("$page links to $link", $page => $IkiWiki::DEPEND_LINKS) if $bestlink eq IkiWiki::bestlink($page, $p); } else { - return IkiWiki::SuccessReason->new("$page links to page $p matching $link") + return IkiWiki::SuccessReason->new("$page links to page $p matching $link", $page => $IkiWiki::DEPEND_LINKS) if match_glob($p, $link, %params); my ($p_rel)=$p=~/^\/?(.*)/; $link=~s/^\///; - return IkiWiki::SuccessReason->new("$page links to page $p_rel matching $link") + return IkiWiki::SuccessReason->new("$page links to page $p_rel matching $link", $page => $IkiWiki::DEPEND_LINKS) if match_glob($p_rel, $link, %params); } } @@ -2115,7 +2211,9 @@ sub match_link ($$;@) { } sub match_backlink ($$;@) { - return match_link($_[1], $_[0], @_); + my $ret=match_link($_[1], $_[0], @_); + $ret->influences($_[1] => $IkiWiki::DEPEND_LINKS); + return $ret; } sub match_created_before ($$;@) { @@ -2127,14 +2225,14 @@ sub match_created_before ($$;@) { if (exists $IkiWiki::pagectime{$testpage}) { if ($IkiWiki::pagectime{$page} < $IkiWiki::pagectime{$testpage}) { - return IkiWiki::SuccessReason->new("$page created before $testpage"); + return IkiWiki::SuccessReason->new("$page created before $testpage", $testpage => $IkiWiki::DEPEND_PRESENCE); } else { - return IkiWiki::FailReason->new("$page not created before $testpage"); + return IkiWiki::FailReason->new("$page not created before $testpage", $testpage => $IkiWiki::DEPEND_PRESENCE); } } else { - return IkiWiki::ErrorReason->new("$testpage does not exist"); + return IkiWiki::ErrorReason->new("$testpage does not exist", $testpage => $IkiWiki::DEPEND_PRESENCE); } } @@ -2147,14 +2245,14 @@ sub match_created_after ($$;@) { if (exists $IkiWiki::pagectime{$testpage}) { if ($IkiWiki::pagectime{$page} > $IkiWiki::pagectime{$testpage}) { - return IkiWiki::SuccessReason->new("$page created after $testpage"); + return IkiWiki::SuccessReason->new("$page created after $testpage", $testpage => $IkiWiki::DEPEND_PRESENCE); } else { - return IkiWiki::FailReason->new("$page not created after $testpage"); + return IkiWiki::FailReason->new("$page not created after $testpage", $testpage => $IkiWiki::DEPEND_PRESENCE); } } else { - return IkiWiki::ErrorReason->new("$testpage does not exist"); + return IkiWiki::ErrorReason->new("$testpage does not exist", $testpage => $IkiWiki::DEPEND_PRESENCE); } }