Allow sorting to be combined and/or reversed

master
Simon McVittie 2010-03-24 23:51:48 +00:00
parent b0ae19872d
commit 60edd2dc31
4 changed files with 91 additions and 26 deletions

View File

@ -2005,6 +2005,64 @@ sub pagespec_match ($$;@) {
return $sub->($page, @params);
}
sub get_sort_function {
my $method = $_[0];
if ($method =~ m/\s/) {
my @methods = map { get_sort_function($_) } split(' ', $method);
return sub {
foreach my $method (@methods) {
my $answer = $method->($_[0], $_[1]);
return $answer if $answer;
}
return 0;
};
}
my $sense = 1;
if ($method =~ s/^-//) {
$sense = -1;
}
my $token = $method;
my $parameter = undef;
if ($method =~ m/^(\w+)\((.*)\)$/) {
$token = $1;
$parameter = $2;
}
if (exists $hooks{sort}{$token}{call}) {
my $callback = $hooks{sort}{$token}{call};
return sub { $sense * $callback->($_[0], $_[1], $parameter) };
}
if ($method eq 'title') {
return sub { $sense * (pagetitle(basename($_[0])) cmp pagetitle(basename($_[1]))) };
}
if ($method eq 'title_natural') {
eval q{use Sort::Naturally};
if ($@) {
error(gettext("Sort::Naturally needed for title_natural sort"));
}
return sub { $sense * Sort::Naturally::ncmp(pagetitle(basename($_[0])), pagetitle(basename($_[1]))) };
}
if ($method eq 'mtime') {
return sub { $sense * ($pagemtime{$_[1]} <=> $pagemtime{$_[0]}) };
}
if ($method eq 'age') {
return sub { $sense * ($pagectime{$_[1]} <=> $pagectime{$_[0]}) };
}
error sprintf(gettext("unknown sort type %s"), $method);
}
sub pagespec_match_list ($$;@) {
my $page=shift;
my $pagespec=shift;
@ -2034,31 +2092,9 @@ sub pagespec_match_list ($$;@) {
}
if (defined $params{sort}) {
my $f;
my $f = get_sort_function($params{sort});
if (exists $hooks{sort}{$params{sort}}{call}) {
$f = sub { $hooks{sort}{$params{sort}}{call}($a, $b) };
}
elsif ($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 = sort { $f->($a, $b) } @candidates;
}
@candidates=reverse(@candidates) if $params{reverse};

View File

@ -15,6 +15,10 @@ orders can be specified.
full title was set.
"""]]
In addition, you can combine several sort orders and/or reverse the order of
sorting, with a string like `age -title` (which would sort by age, then by
title in reverse order if two pages have the same age).
Plugins can add additional sort orders, so more might be available on this
wiki.

View File

@ -593,7 +593,9 @@ function of the ikiwiki wrapper when it is being generated.
hook(type => "sort", id => "foo", call => \&sort_by_foo);
This hook adds an additional [[ikiwiki/pagespec/sorting]] order or overrides
an existing one. The callback is given two page names as arguments, and
an existing one.
The callback is given two page names followed by the parameter as arguments, and
returns negative, zero or positive if the first page should come before,
close to (i.e. undefined order), or after the second page.
@ -603,6 +605,19 @@ For instance, the built-in `title` sort order could be reimplemented as
pagetitle(basename($_[0])) cmp pagetitle(basename($_[1]));
}
and to sort by an arbitrary `meta` value, you could use:
# usage: sort="meta(description)"
sub sort_by_meta {
my $param = $_[2];
error "sort=meta requires a parameter" unless defined $param;
my $left = $pagestate{$_[0]}{meta}{$param};
$left = "" unless defined $left;
my $right = $pagestate{$_[1]}{meta}{$param};
$right = "" unless defined $right;
return $left cmp $right;
}
## Exported variables
Several variables are exported to your plugin when you `use IkiWiki;`

View File

@ -1,7 +1,7 @@
#!/usr/bin/perl
use warnings;
use strict;
use Test::More tests => 89;
use Test::More tests => 90;
BEGIN { use_ok("IkiWiki"); }
@ -20,6 +20,13 @@ hook(type => "sort", id => "path", call => sub { $_[0] cmp $_[1] });
"post/2" => "post/2.mdwn",
"post/3" => "post/3.mdwn",
);
$IkiWiki::pagectime{foo} = 2;
$IkiWiki::pagectime{foo2} = 2;
$IkiWiki::pagectime{foo3} = 1;
$IkiWiki::pagectime{bar} = 3;
$IkiWiki::pagectime{"post/1"} = 6;
$IkiWiki::pagectime{"post/2"} = 6;
$IkiWiki::pagectime{"post/3"} = 6;
$links{foo}=[qw{post/1 post/2}];
$links{foo2}=[qw{bar}];
$links{foo3}=[qw{bar}];
@ -38,6 +45,9 @@ is_deeply([pagespec_match_list("foo", "post/*", sort => "title",
["post/1", "post/2"]);
is_deeply([pagespec_match_list("foo", "*", sort => "path", num => 2)],
["bar", "foo"]);
is_deeply([pagespec_match_list("foo", "foo* or bar*",
sort => "-age title")], # oldest first, break ties by title
["foo3", "foo", "foo2", "bar"]);
my $r=eval { pagespec_match_list("foo", "beep") };
ok(eval { pagespec_match_list("foo", "beep") } == 0);
ok(! $@, "does not fail with error when unable to match anything");