idea
[ikiwiki.git] / doc / todo / allow_plugins_to_add_sorting_methods.mdwn
1 [[!template id=gitbranch branch=smcv/sort-hooks author="[[Simon_McVittie|smcv]]"]]
2 [[!tag patch]]
3
4 The available [[ikiwiki/pagespec/sorting]] methods are currently hard-coded in
5 IkiWiki.pm, making it difficult to add any extra sorting mechanisms. I've
6 prepared a branch which adds 'sort' as a hook type and uses it to implement a
7 new `meta_title` sort type.
8
9 Someone could use this hook to make `\[[!inline sort=title]]` prefer the meta
10 title over the page name, but for compatibility, I'm not going to (I do wonder
11 whether it would be worth making sort=name an alias for the current sort=title,
12 and changing the meaning of sort=title in 4.0, though).
13
14 Gitweb:
15 <http://git.pseudorandom.co.uk/smcv/ikiwiki.git?a=shortlog;h=refs/heads/sort-hooks>
16
17 I briefly tried to turn *all* the current sort types into hook functions, and
18 have some of them pre-registered, but decided that probably wasn't a good idea.
19 That earlier version of the branch is also available for comparison:
20
21 <http://git.pseudorandom.co.uk/smcv/ikiwiki.git?a=shortlog;h=refs/heads/sort-hooks-excessive>
22
23 (The older version is untested, and probably doesn't really work as-is - I
24 misunderstood the details of how the built-in function `sort` works when using
25 `$a` and `$b`. The newer version has been tested, and has a regression test for
26 its core functionality.)
27
28 This hook *isn't* (yet) sufficient to implement [[plugins/contrib/report]]'s
29 NIH'd sorting mechanisms:
30
31 * `report` can sort by any [[plugins/contrib/field]], whereas this one has a
32   finite number of hooks: if the `field` plugin's functionality is desirable,
33   perhaps parameterized sort mechanisms similar to pagespec match functions
34   would be useful? Then the `field` plugin could register
35   `hook(type => "sort", id => "field")` and you could have
36   `\[[!inline ... sort="field(Mood)"]]` or something?
37
38 * `report` can sort by multiple criteria, with independent direction-changing:
39   if this is desirable, perhaps `pagespec_match_list` could be enhanced to
40   interpret `sort="x -y z(w)"` as sorting by (pseudocode)
41   `{ $cmp_x->($a, $b) || (-$cmp_y->($a, $b)) || $cmp_z->($a, $b, "w") }`?
42
43 > I've now added both of these features to the sort-hooks branch. --[[smcv]]
44
45 >> I wonder if IkiWiki would benefit from the concept of a "sortspec", like a [[ikiwiki/PageSpec]] but dedicated to sorting lists of pages rather than defining lists of pages?  Rather than defining a sort-hook, define a SortSpec class, and enable people to add their own sort methods as functions defined inside that class, similarly to the way they can add their own pagespec definitions. --[[KathrynAndersen]]
46
47 >>> [[!template id=gitbranch branch=smcv/sort-package author="[[Simon_McVittie|smcv]]"]]
48 >>> I'd be inclined to think that's overkill, but it wasn't very hard to
49 >>> implement, and in a way is more elegant. I set it up so sort mechanisms
50 >>> share the `IkiWiki::PageSpec` package, but with a `cmp_` prefix. Gitweb:
51 >>> <http://git.pseudorandom.co.uk/smcv/ikiwiki.git?a=shortlog;h=refs/heads/sort-package>
52
53 >>>> I agree it seems more elegant, so I have focused on it.
54 >>>>
55 >>>> I don't know about reusing `IkiWiki::PageSpec` for this.
56 >>>>
57 >>>> I would be inclined to drop the `check_` stuff.
58 >>>>
59 >>>> Wouldn't it make sense to have `meta(title)` instead
60 >>>> of `meta_title`?
61 >>>>
62 >>>> As I read the regexp in `cmpspec_translate`, the "command"
63 >>>> is required to have params. They should be optional, 
64 >>>> to match the documentation and because most sort methods
65 >>>> do not need parameters.
66 >>>>
67 >>>> I wonder if it would make sense to add some combining keywords, so
68 >>>> a sortspec reads like `sort="age then ascending title"`
69 >>>> In a way, this reduces the amount of syntax that needs to be learned.
70 >>>> I like the "then" (and it could allow other operations than
71 >>>> simple combination, if any others make sense). Not so sure about the
72 >>>> "ascending", which could be "reverse" instead, but "descending age" and
73 >>>> "ascending age" both seem useful to be able to explicitly specify.
74 >>>> --[[Joey]]
75
76 ## Documentation from sort-hooks branch
77
78 ### sort hook (added to [[plugins/write]])
79
80        hook(type => "sort", id => "foo", call => \&sort_by_foo);
81
82 This hook adds an additional [[ikiwiki/pagespec/sorting]] order or overrides
83 an existing one.
84
85 The callback is given two page names followed by the parameter as arguments, and
86 returns negative, zero or positive if the first page should come before,
87 close to (i.e. undefined order), or after the second page.
88
89 For instance, the built-in `title` sort order could be reimplemented as
90
91        sub sort_by_title {
92                pagetitle(basename($_[0])) cmp pagetitle(basename($_[1]));
93        }
94
95 and to sort by an arbitrary `meta` value, you could use:
96
97        # usage: sort="meta(description)"
98        sub sort_by_meta {
99                my $param = $_[2];
100                error "sort=meta requires a parameter" unless defined $param;
101                my $left = $pagestate{$_[0]}{meta}{$param};
102                $left = "" unless defined $left;
103                my $right = $pagestate{$_[1]}{meta}{$param};
104                $right = "" unless defined $right;
105                return $left cmp $right;
106        }
107
108
109 ### meta_title sort order (conditionally added to [[ikiwiki/pagespec/sorting]])
110
111 * `meta_title` - Order according to the `\[[!meta title="foo" sort="bar"]]`
112   or `\[[!meta title="foo"]]` [[ikiwiki/directive]], or the page name if no
113   full title was set.
114
115   > I feel it sould be clearer to call that "sortas", since "sort=" is used
116   > to specify a sort method in other directives. --[[Joey]]
117
118 ### Multiple sort orders (added to [[ikiwiki/pagespec/sorting]])
119
120 In addition, you can combine several sort orders and/or reverse the order of
121 sorting, with a string like `age -title` (which would sort by age, then by
122 title in reverse order if two pages have the same age).
123
124 ### meta title sort parameter (added to [[ikiwiki/directive/meta]])
125
126 An optional `sort` parameter will be used preferentially when
127 [[ikiwiki/pagespec/sorting]] by `meta_title`:
128
129        \[[!meta title="The Beatles" sort="Beatles, The"]]
130
131        \[[!meta title="David Bowie" sort="Bowie, David"]]
132
133 ## Documentation from sort-package branch
134
135 The changes to [[ikiwiki/pagespec/sorting]] are the same.
136 The changes to [[plugins/write]] are replaced by:
137
138 ### Sorting plugins
139
140 Similarly, it's possible to write plugins that add new functions as
141 [[ikiwiki/pagespec/sorting]] methods. To achieve this, add a function to
142 the IkiWiki::PageSpec package named `cmp_foo`, which will be used when sorting
143 by `foo` or `foo(...)` is requested.
144
145 The function will be passed three or more parameters. The first two are
146 page names, and the third is `undef` if invoked as `foo`, or the parameter
147 `"bar"` if invoked as `foo(bar)`. It may also be passed additional, named
148 parameters.
149
150 It should return the same thing as Perl's `cmp` and `<=>` operators: negative
151 if the first argument is less than the second, positive if the first argument
152 is greater, or zero if they are considered equal. It may also raise an
153 error using `error`, for instance if it needs a parameter but one isn't
154 provided.
155
156 You can also define a function called `check_cmp_foo` in the same package.
157 If you do, it will be called while preparing to sort by `foo` or `foo(bar)`,
158 with argument `undef` or `"bar"` respectively; it may raise an error using
159 `error`, if sorting like that isn't going to work.