]> sipb.mit.edu Git - ikiwiki.git/blobdiff - IkiWiki.pm
Merge remote branch 'davrieb/autotag' into autotag
[ikiwiki.git] / IkiWiki.pm
index 6ea0fb3f358c1fc48551db2ea9f3af9a363b1ff6..2f26a16ce5b361480d3e1b56e425f27020dd6f6c 100644 (file)
@@ -14,7 +14,8 @@ 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 %typedlinks %oldtypedlinks};
+           %forcerebuild %loaded_plugins %typedlinks %oldtypedlinks
+           %autofiles %del_hash};
 
 use Exporter q{import};
 our @EXPORT = qw(hook debug error template htmlpage deptype
@@ -22,7 +23,7 @@ our @EXPORT = qw(hook debug error template htmlpage deptype
                 htmllink readfile writefile pagetype srcfile pagename
                 displaytime will_render gettext ngettext urlto targetpage
                 add_underlay pagetitle titlepage linkpage newpagefile
-                inject add_link
+                inject add_link add_autofile
                  %config %links %pagestate %wikistate %renderedfiles
                  %pagesources %destsources %typedlinks);
 our $VERSION = 3.00; # plugin interface version, next is ikiwiki version
@@ -37,6 +38,7 @@ our $DEPEND_LINKS=4;
 # Optimisation.
 use Memoize;
 memoize("abs2rel");
+memoize("sortspec_translate");
 memoize("pagespec_translate");
 memoize("template_file");
 
@@ -439,10 +441,9 @@ sub getsetup () {
                safe => 0,
                rebuild => 0,
        },
-       getctime => {
+       gettime => {
                type => "internal",
-               default => 0,
-               description => "running in getctime mode",
+               description => "running in gettime mode",
                safe => 0,
                rebuild => 0,
        },
@@ -1164,7 +1165,7 @@ sub htmlize ($$$$) {
        my $content=shift;
        
        my $oneline = $content !~ /\n/;
-
+       
        if (exists $hooks{htmlize}{$type}) {
                $content=$hooks{htmlize}{$type}{call}->(
                        page => $page,
@@ -1185,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/^<p>//i;
-               $content=~s/<\/p>$//i;
-               chomp $content;
+               $content=~s/<\/p>\n*$//i;
        }
 
        return $content;
@@ -1512,6 +1512,7 @@ sub loadindex () {
                        open ($in, "<", "$config{wikistatedir}/indexdb") || return;
                }
                else {
+                       $config{gettime}=1; # first build
                        return;
                }
        }
@@ -1790,6 +1791,10 @@ sub rcs_getctime ($) {
        $hooks{rcs}{rcs_getctime}{call}->(@_);
 }
 
+sub rcs_getmtime ($) {
+       $hooks{rcs}{rcs_getmtime}{call}->(@_);
+}
+
 sub rcs_receive () {
        $hooks{rcs}{rcs_receive}{call}->();
 }
@@ -1951,6 +1956,95 @@ sub add_link ($$;$) {
        }
 }
 
+sub sortspec_translate ($$) {
+       my $spec = shift;
+       my $reverse = shift;
+
+       my $code = "";
+       my @data;
+       while ($spec =~ m{
+               \s*
+               (-?)            # group 1: perhaps negated
+               \s*
+               (               # group 2: a word
+                       \w+\([^\)]*\)   # command(params)
+                       |
+                       [^\s]+          # or anything else
+               )
+               \s*
+       }gx) {
+               my $negated = $1;
+               my $word = $2;
+               my $params = undef;
+
+               if ($word =~ m/^(\w+)\((.*)\)$/) {
+                       # command with parameters
+                       $params = $2;
+                       $word = $1;
+               }
+               elsif ($word !~ m/^\w+$/) {
+                       error(sprintf(gettext("invalid sort type %s"), $word));
+               }
+
+               if (length $code) {
+                       $code .= " || ";
+               }
+
+               if ($negated) {
+                       $code .= "-";
+               }
+
+               if (exists $IkiWiki::SortSpec::{"cmp_$word"}) {
+                       if (defined $params) {
+                               push @data, $params;
+                               $code .= "IkiWiki::SortSpec::cmp_$word(\$data[$#data])";
+                       }
+                       else {
+                               $code .= "IkiWiki::SortSpec::cmp_$word(undef)";
+                       }
+               }
+               else {
+                       error(sprintf(gettext("unknown sort type %s"), $word));
+               }
+       }
+
+       if (! length $code) {
+               # undefined sorting method... sort arbitrarily
+               return sub { 0 };
+       }
+
+       if ($reverse) {
+               $code="-($code)";
+       }
+
+       no warnings;
+       return eval 'sub { '.$code.' }';
+}
+
+sub add_autofile ($$) {
+       my $autofile=shift;
+       my $plugin=shift;
+
+       if (srcfile($autofile, 1)) {
+               return 0;
+       }
+
+       my ($file, $page) = verify_src_file("$config{srcdir}/$autofile", $config{srcdir});
+
+       if ((!defined $file) ||
+       (exists $pagestate{$page}{$plugin}{autofile_deleted})) {
+               return 0;
+       }
+
+       if (exists $del_hash{$file}) {
+               $pagestate{$page}{$plugin}{autofile_deleted}=1;
+               return 0;
+       }
+
+       $autofiles{$file}=$plugin;
+       return 1;
+}
+
 sub pagespec_translate ($) {
        my $spec=shift;
 
@@ -2037,6 +2131,8 @@ sub pagespec_match_list ($$;@) {
        my $sub=pagespec_translate($pagespec);
        error "syntax error in pagespec \"$pagespec\""
                if ! defined $sub;
+       my $sort=sortspec_translate($params{sort}, $params{reverse})
+               if defined $params{sort};
 
        my @candidates;
        if (exists $params{list}) {
@@ -2049,39 +2145,19 @@ sub pagespec_match_list ($$;@) {
                        ? grep { ! $params{filter}->($_) } keys %pagesources
                        : keys %pagesources;
        }
-
-       if (defined $params{sort}) {
-               my $f;
-               if ($params{sort} eq 'title') {
-                       $f=sub { pagetitle(basename($a)) cmp pagetitle(basename($b)) };
-               }
-               elsif ($params{sort} eq 'title_natural') {
-                       eval q{use Sort::Naturally};
-                       if ($@) {
-                               error(gettext("Sort::Naturally needed for title_natural sort"));
-                       }
-                       $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};
-       
-       $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;
@@ -2103,7 +2179,15 @@ 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 ($) {
@@ -2391,4 +2475,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($a))
+       cmp
+       IkiWiki::pagetitle(IkiWiki::basename($b))
+}
+
+sub cmp_mtime { $IkiWiki::pagemtime{$b} <=> $IkiWiki::pagemtime{$a} }
+sub cmp_age { $IkiWiki::pagectime{$b} <=> $IkiWiki::pagectime{$a} }
+
 1