From: Joey Hess Date: Wed, 7 Apr 2010 03:15:33 +0000 (-0400) Subject: Merge remote branch 'smcv/ready/sort-package' X-Git-Url: https://sipb.mit.edu/gitweb.cgi/ikiwiki.git/commitdiff_plain/bab8fec52468b780485bdf96a37a593bd033c7e3?hp=-c Merge remote branch 'smcv/ready/sort-package' Conflicts: debian/NEWS --- bab8fec52468b780485bdf96a37a593bd033c7e3 diff --combined IkiWiki.pm index 2415307d4,da36494fb..6d2f4dac3 --- a/IkiWiki.pm +++ b/IkiWiki.pm @@@ -14,7 -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 +24,7 @@@ 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,6 -37,7 +37,7 @@@ our $DEPEND_LINKS=4 # Optimisation. use Memoize; memoize("abs2rel"); + memoize("sortspec_translate"); memoize("pagespec_translate"); memoize("template_file"); @@@ -1164,7 -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, @@@ -1185,9 -1186,10 +1186,9 @@@ 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; @@@ -1502,7 -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")) { @@@ -1568,14 -1570,6 +1569,14 @@@ 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}}]; } @@@ -1624,10 -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}}) { @@@ -1807,7 -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; @@@ -1937,19 -1927,74 +1938,79 @@@ 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 sortspec_translate ($) { + my $spec = 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 }; + } + + no warnings; + return eval 'sub { '.$code.' }'; + } + sub pagespec_translate ($) { my $spec=shift; @@@ -2017,7 -2062,7 +2078,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); } @@@ -2035,7 -2080,7 +2096,7 @@@ sub pagespec_match_list ($$;@) my $sub=pagespec_translate($pagespec); error "syntax error in pagespec \"$pagespec\"" - if $@ || ! defined $sub; + if ! defined $sub; my @candidates; if (exists $params{list}) { @@@ -2050,27 -2095,8 +2111,8 @@@ } 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 = IkiWiki::SortSpec::sort_pages($params{sort}, + @candidates); } @candidates=reverse(@candidates) if $params{reverse}; @@@ -2108,7 -2134,8 +2150,7 @@@ sub pagespec_valid ($) { my $spec=shift; - my $sub=pagespec_translate($spec); - return ! $@; + return defined pagespec_translate($spec); } sub glob2re ($) { @@@ -2228,34 -2255,26 +2270,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 ($$;@) { @@@ -2390,4 -2409,25 +2432,25 @@@ sub match_ip ($$;@) } } + package IkiWiki::SortSpec; + + # This is in the SortSpec namespace so that the $a and $b that sort() uses + # $IkiWiki::SortSpec::a and $IkiWiki::SortSpec::b, so that plugins' cmp + # functions can access them easily. + sub sort_pages + { + my $f = IkiWiki::sortspec_translate(shift); + + return 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 diff --combined debian/NEWS index e1cb00473,614eb11f8..b796154fa --- a/debian/NEWS +++ b/debian/NEWS @@@ -1,15 -1,10 +1,19 @@@ -ikiwiki (3.20100320) UNRELEASED; urgency=low +ikiwiki (3.20100406) unstable; urgency=low ++ ++ The title_natural sort method (as used by the inline directive, etc) ++ have been moved to the new sortnaturally plugin, which is not enabled ++ by default since it requires the Sort::Naturally perl module. - The sort="title_natural" option on [[!inline]] etc. now requires the - new sortnaturally plugin. This is not enabled by default, because it requires - the Sort::Naturally module. + Starting from this version, the `tagged()` pagespec only matches tags, + not regular wikilinks. If your wiki accidentially relied on the old, + buggy behavior, you might need to change pagespecs to use `link()`. - -- Simon McVittie Sat, 03 Apr 2010 13:46:08 +0100 + To support the above change, 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. + + -- Simon McVittie Tue, 06 Apr 2010 20:53:07 +0100 ikiwiki (3.20091017) unstable; urgency=low diff --combined doc/plugins/write.mdwn index 71ff1fd29,f42cc86ae..05ddf2215 --- a/doc/plugins/write.mdwn +++ b/doc/plugins/write.mdwn @@@ -633,22 -633,6 +633,22 @@@ reference. Do not modify this hash dire $links{"foo"} = ["bar", "baz"]; +### `%typedlinks` + +The `%typedlinks` hash records links of specific types. Do not modify this +hash directly; call `add_link()`. The keys are page names, and the values +are hash references. In each page's hash reference, the keys are link types +defined by plugins, and the values are hash references with link targets +as keys, and 1 as a dummy value, something like this: + + $typedlinks{"foo"} = { + tag => { short_word => 1, metasyntactic_variable => 1 }, + next_page => { bar => 1 }, + }; + +Ordinary [[WikiLinks|ikiwiki/WikiLink]] appear in `%links`, but not in +`%typedlinks`. + ### `%pagesources` The `%pagesources` has can be used to look up the source filename @@@ -955,14 -939,11 +955,14 @@@ Optionally, a third parameter can be pa filename of the page. For example, `targetpage("foo", "rss", "feed")` will yield something like `foo/feed.rss`. -### `add_link($$)` +### `add_link($$;$)` This adds a link to `%links`, ensuring that duplicate links are not added. Pass it the page that contains the link, and the link text. +An optional third parameter sets the link type. If not specified, +it is an ordinary [[ikiwiki/WikiLink]]. + ## Miscellaneous ### Internal use pages @@@ -1129,6 -1110,24 +1129,24 @@@ For example, "backlink(foo)" is influen they match; "created_before(foo)" is influenced by the metadata of foo; while "glob(*)" is not influenced by the contents of any page. + ### Sorting plugins + + Similarly, it's possible to write plugins that add new functions as + [[ikiwiki/pagespec/sorting]] methods. To achieve this, add a function to + the IkiWiki::SortSpec package named `cmp_foo`, which will be used when sorting + by `foo` or `foo(...)` is requested. + + The names of pages to be compared are in the global variables `$a` and `$b` + in the IkiWiki::SortSpec package. The function should return the same thing + as Perl's `cmp` and `<=>` operators: negative if `$a` is less than `$b`, + positive if `$a` is greater, or zero if they are considered equal. It may + also raise an error using `error`, for instance if it needs a parameter but + one isn't provided. + + The function will also be passed one or more parameters. The first is + `undef` if invoked as `foo`, or the parameter `"bar"` if invoked as `foo(bar)`; + it may also be passed additional, named parameters. + ### Setup plugins The ikiwiki setup file is loaded using a pluggable mechanism. If you look