X-Git-Url: https://sipb.mit.edu/gitweb.cgi/ikiwiki.git/blobdiff_plain/a875ee8be702bd4575e009dc652015c1157c7c2e..50d08bc2aea1d8d52e07ae1750e73b722bd2db24:/IkiWiki.pm diff --git a/IkiWiki.pm b/IkiWiki.pm index 8f36f5818..1730e476a 100644 --- a/IkiWiki.pm +++ b/IkiWiki.pm @@ -14,7 +14,7 @@ use open qw{:utf8 :std}; use vars qw{%config %links %oldlinks %pagemtime %pagectime %pagecase %pagestate %wikistate %renderedfiles %oldrenderedfiles %pagesources %destsources %depends %depends_simple %hooks - %forcerebuild %loaded_plugins}; + %forcerebuild %loaded_plugins %typedlinks %oldtypedlinks}; use Exporter q{import}; our @EXPORT = qw(hook debug error template htmlpage deptype @@ -24,7 +24,7 @@ our @EXPORT = qw(hook debug error template htmlpage deptype add_underlay pagetitle titlepage linkpage newpagefile inject add_link %config %links %pagestate %wikistate %renderedfiles - %pagesources %destsources); + %pagesources %destsources %typedlinks); 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 @@ -37,7 +37,7 @@ our $DEPEND_LINKS=4; # Optimisation. use Memoize; memoize("abs2rel"); -memoize("cmpspec_translate"); +memoize("sortspec_translate"); memoize("pagespec_translate"); memoize("template_file"); @@ -1165,7 +1165,7 @@ sub htmlize ($$$$) { my $content=shift; my $oneline = $content !~ /\n/; - + if (exists $hooks{htmlize}{$type}) { $content=$hooks{htmlize}{$type}{call}->( page => $page, @@ -1186,10 +1186,9 @@ sub htmlize ($$$$) { if ($oneline) { # hack to get rid of enclosing junk added by markdown - # and other htmlizers + # and other htmlizers/sanitizers $content=~s/^

//i; - $content=~s/<\/p>$//i; - chomp $content; + $content=~s/<\/p>\n*$//i; } return $content; @@ -1504,7 +1503,7 @@ sub loadindex () { if (! $config{rebuild}) { %pagesources=%pagemtime=%oldlinks=%links=%depends= %destsources=%renderedfiles=%pagecase=%pagestate= - %depends_simple=(); + %depends_simple=%typedlinks=%oldtypedlinks=(); } my $in; if (! open ($in, "<", "$config{wikistatedir}/indexdb")) { @@ -1570,6 +1569,14 @@ sub loadindex () { if (exists $d->{state}) { $pagestate{$page}=$d->{state}; } + if (exists $d->{typedlinks}) { + $typedlinks{$page}=$d->{typedlinks}; + + while (my ($type, $links) = each %{$typedlinks{$page}}) { + next unless %$links; + $oldtypedlinks{$page}{$type} = {%$links}; + } + } } $oldrenderedfiles{$page}=[@{$d->{dest}}]; } @@ -1618,6 +1625,10 @@ sub saveindex () { $index{page}{$src}{depends_simple} = $depends_simple{$page}; } + if (exists $typedlinks{$page} && %{$typedlinks{$page}}) { + $index{page}{$src}{typedlinks} = $typedlinks{$page}; + } + if (exists $pagestate{$page}) { foreach my $id (@hookids) { foreach my $key (keys %{$pagestate{$page}{$id}}) { @@ -1797,7 +1808,7 @@ sub add_depends ($$;$) { # Add explicit dependencies for influences. my $sub=pagespec_translate($pagespec); - return if $@; + return unless defined $sub; foreach my $p (keys %pagesources) { my $r=$sub->($p, location => $page); my $i=$r->influences; @@ -1927,16 +1938,22 @@ sub inject { use warnings; } -sub add_link ($$) { +sub add_link ($$;$) { my $page=shift; my $link=shift; + my $type=shift; push @{$links{$page}}, $link unless grep { $_ eq $link } @{$links{$page}}; + + if (defined $type) { + $typedlinks{$page}{$type}{$link} = 1; + } } -sub cmpspec_translate ($) { +sub sortspec_translate ($$) { my $spec = shift; + my $reverse = shift; my $code = ""; my @data; @@ -1972,17 +1989,13 @@ sub cmpspec_translate ($) { $code .= "-"; } - if (exists $IkiWiki::PageSpec::{"cmp_$word"}) { - if (exists $IkiWiki::PageSpec::{"check_cmp_$word"}) { - $IkiWiki::PageSpec::{"check_cmp_$word"}->($params); - } - + if (exists $IkiWiki::SortSpec::{"cmp_$word"}) { if (defined $params) { push @data, $params; - $code .= "IkiWiki::PageSpec::cmp_$word(\@_, \$data[$#data])"; + $code .= "IkiWiki::SortSpec::cmp_$word(\$data[$#data])"; } else { - $code .= "IkiWiki::PageSpec::cmp_$word(\@_, undef)"; + $code .= "IkiWiki::SortSpec::cmp_$word(undef)"; } } else { @@ -1995,6 +2008,10 @@ sub cmpspec_translate ($) { return sub { 0 }; } + if ($reverse) { + $code="-($code)"; + } + no warnings; return eval 'sub { '.$code.' }'; } @@ -2066,7 +2083,7 @@ sub pagespec_match ($$;@) { my $sub=pagespec_translate($spec); return IkiWiki::ErrorReason->new("syntax error in pagespec \"$spec\"") - if $@ || ! defined $sub; + if ! defined $sub; return $sub->($page, @params); } @@ -2084,7 +2101,9 @@ sub pagespec_match_list ($$;@) { my $sub=pagespec_translate($pagespec); error "syntax error in pagespec \"$pagespec\"" - if $@ || ! defined $sub; + if ! defined $sub; + my $sort=sortspec_translate($params{sort}, $params{reverse}) + if defined $params{sort}; my @candidates; if (exists $params{list}) { @@ -2097,21 +2116,19 @@ sub pagespec_match_list ($$;@) { ? grep { ! $params{filter}->($_) } keys %pagesources : keys %pagesources; } - - if (defined $params{sort}) { - my $f = cmpspec_translate($params{sort}); - - @candidates = sort { $f->($a, $b) } @candidates; - } - - @candidates=reverse(@candidates) if $params{reverse}; - - $depends{$page}{$pagespec} |= ($params{deptype} || $DEPEND_CONTENT); # clear params, remainder is passed to pagespec + $depends{$page}{$pagespec} |= ($params{deptype} || $DEPEND_CONTENT); my $num=$params{num}; delete @params{qw{num deptype reverse sort filter list}}; + # when only the top matches will be returned, it's efficient to + # sort before matching to pagespec, + if (defined $num && defined $sort) { + @candidates=IkiWiki::SortSpec::sort_pages( + $sort, @candidates); + } + my @matches; my $firstfail; my $count=0; @@ -2133,14 +2150,21 @@ sub pagespec_match_list ($$;@) { $depends_simple{$page}{lc $k} |= $i->{$k}; } - return @matches; + # when all matches will be returned, it's efficient to + # sort after matching + if (! defined $num && defined $sort) { + return IkiWiki::SortSpec::sort_pages( + $sort, @matches); + } + else { + return @matches; + } } sub pagespec_valid ($) { my $spec=shift; - my $sub=pagespec_translate($spec); - return ! $@; + return defined pagespec_translate($spec); } sub glob2re ($) { @@ -2260,26 +2284,34 @@ sub match_link ($$;@) { $link=derel($link, $params{location}); my $from=exists $params{location} ? $params{location} : ''; + my $linktype=$params{linktype}; + my $qualifier=''; + if (defined $linktype) { + $qualifier=" with type $linktype"; + } my $links = $IkiWiki::links{$page}; - return IkiWiki::FailReason->new("$page has no links", "" => 1) + return IkiWiki::FailReason->new("$page has no links", $page => $IkiWiki::DEPEND_LINKS, "" => 1) unless $links && @{$links}; my $bestlink = IkiWiki::bestlink($from, $link); foreach my $p (@{$links}) { if (length $bestlink) { - return IkiWiki::SuccessReason->new("$page links to $link", $page => $IkiWiki::DEPEND_LINKS, "" => 1) - if $bestlink eq IkiWiki::bestlink($page, $p); + if ((!defined $linktype || exists $IkiWiki::typedlinks{$page}{$linktype}{$p}) && $bestlink eq IkiWiki::bestlink($page, $p)) { + return IkiWiki::SuccessReason->new("$page links to $link$qualifier", $page => $IkiWiki::DEPEND_LINKS, "" => 1) + } } else { - return IkiWiki::SuccessReason->new("$page links to page $p matching $link", $page => $IkiWiki::DEPEND_LINKS, "" => 1) - if match_glob($p, $link, %params); + if ((!defined $linktype || exists $IkiWiki::typedlinks{$page}{$linktype}{$p}) && match_glob($p, $link, %params)) { + return IkiWiki::SuccessReason->new("$page links to page $p$qualifier, matching $link", $page => $IkiWiki::DEPEND_LINKS, "" => 1) + } my ($p_rel)=$p=~/^\/?(.*)/; $link=~s/^\///; - return IkiWiki::SuccessReason->new("$page links to page $p_rel matching $link", $page => $IkiWiki::DEPEND_LINKS, "" => 1) - if match_glob($p_rel, $link, %params); + if ((!defined $linktype || exists $IkiWiki::typedlinks{$page}{$linktype}{$p_rel}) && match_glob($p_rel, $link, %params)) { + return IkiWiki::SuccessReason->new("$page links to page $p_rel$qualifier, matching $link", $page => $IkiWiki::DEPEND_LINKS, "" => 1) + } } } - return IkiWiki::FailReason->new("$page does not link to $link", "" => 1); + return IkiWiki::FailReason->new("$page does not link to $link$qualifier", $page => $IkiWiki::DEPEND_LINKS, "" => 1); } sub match_backlink ($$;@) { @@ -2414,13 +2446,22 @@ sub match_ip ($$;@) { } } +package IkiWiki::SortSpec; + +# This is in the SortSpec namespace so that the $a and $b that sort() uses +# are easily available in this namespace, for cmp functions to use them. +sub sort_pages { + my $f=shift; + sort $f @_ +} + sub cmp_title { - IkiWiki::pagetitle(IkiWiki::basename($_[0])) + IkiWiki::pagetitle(IkiWiki::basename($a)) cmp - IkiWiki::pagetitle(IkiWiki::basename($_[1])) + IkiWiki::pagetitle(IkiWiki::basename($b)) } -sub cmp_mtime { $IkiWiki::pagemtime{$_[1]} <=> $IkiWiki::pagemtime{$_[0]} } -sub cmp_age { $IkiWiki::pagectime{$_[1]} <=> $IkiWiki::pagectime{$_[0]} } +sub cmp_mtime { $IkiWiki::pagemtime{$b} <=> $IkiWiki::pagemtime{$a} } +sub cmp_age { $IkiWiki::pagectime{$b} <=> $IkiWiki::pagectime{$a} } 1