sort-order could usefully be overridden for meta author, too
[ikiwiki] / doc / todo / allow_plugins_to_add_sorting_methods.mdwn
1 [[!tag patch]]
2
3 The available [[ikiwiki/pagespec/sorting]] methods are currently hard-coded in
4 IkiWiki.pm, making it difficult to add any extra sorting mechanisms. I've
5 prepared a branch which adds 'sort' as a hook type and uses it to implement a
6 new `meta_title` sort type.
7
8 Someone could use this hook to make `\[[!inline sort=title]]` prefer the meta
9 title over the page name, but for compatibility, I'm not going to (I do wonder
10 whether it would be worth making sort=name an alias for the current sort=title,
11 and changing the meaning of sort=title in 4.0, though).
12
13 *[sort-hooks branch now withdrawn in favour of sort-package --s]*
14
15 I briefly tried to turn *all* the current sort types into hook functions, and
16 have some of them pre-registered, but decided that probably wasn't a good idea.
17 That earlier version of the branch is also available for comparison:
18
19 *[also withdrawn in favour of sort-package --s]*
20
21 >> 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]]
22
23 >>> [[!template id=gitbranch branch=smcv/sort-package author="[[Simon_McVittie|smcv]]"]]
24 >>> I'd be inclined to think that's overkill, but it wasn't very hard to
25 >>> implement, and in a way is more elegant. I set it up so sort mechanisms
26 >>> share the `IkiWiki::PageSpec` package, but with a `cmp_` prefix. Gitweb:
27 >>> <http://git.pseudorandom.co.uk/smcv/ikiwiki.git?a=shortlog;h=refs/heads/sort-package>
28
29 >>>> I agree it seems more elegant, so I have focused on it.
30 >>>>
31 >>>> I don't know about reusing `IkiWiki::PageSpec` for this.
32 >>>> --[[Joey]]
33
34 >>>>> Fair enough, `IkiWiki::SortSpec::cmp_foo` would be just
35 >>>>> as easy, or `IkiWiki::Sorting::cmp_foo` if you don't like
36 >>>>> introducing "sort spec" in the API. I took a cue from
37 >>>>> [[ikiwiki/pagespec/sorting]] being a subpage of
38 >>>>> [[ikiwiki/pagespec]], and decided that yes, sorting is
39 >>>>> a bit like a pagespec :-) Which name would you prefer? --s
40
41 >>>> I would be inclined to drop the `check_` stuff. --J
42
43 >>>>> It basically exists to support `title_natural`, to avoid
44 >>>>> firing up the whole import mechanism on every `cmp`
45 >>>>> (although I suppose that could just be a call to a
46 >>>>> memoized helper function). It also lets sort specs that
47 >>>>> *must* have a parameter, like
48 >>>>> [[field|plugins/contrib/field/discussion]], fail early
49 >>>>> (again, not so valuable).
50 >>>>>
51 >>>>> The former function could be achieved at a small
52 >>>>> compatibility cost by putting `title_natural` in a new
53 >>>>> `sortnatural` plugin (that fails to load if you don't
54 >>>>> have `title_natural`), if you'd prefer - that's what would
55 >>>>> have happened if `title_natural` was written after this
56 >>>>> code had been merged, I suspect. Would you prefer this? --s
57
58 >>>> Wouldn't it make sense to have `meta(title)` instead
59 >>>> of `meta_title`? --J
60
61 >>>>> Yes, you're right. I added parameters to support `field`,
62 >>>>> and didn't think about making `meta` use them too.
63 >>>>> However, `title` does need a special case to make it
64 >>>>> default to the basename instead of the empty string.
65 >>>>>
66 >>>>> Another special case for `title` is to use `titlesort`
67 >>>>> first (the name `titlesort` is derived from Ogg/FLAC
68 >>>>> tags, which can have `titlesort` and `artistsort`).
69 >>>>> I could easily extend that to other metas, though;
70 >>>>> in fact, for e.g. book lists it would be nice for
71 >>>>> `field(bookauthor)` to behave similarly, so you can
72 >>>>> display "Douglas Adams" but sort by "Adams, Douglas".
73 >>>>>
74 >>>>> `meta_title` is also meant to be a prototype of how
75 >>>>> `sort=title` could behave in 4.0 or something - sorting
76 >>>>> by page name (which usually sorts in approximately the
77 >>>>> same place as the meta-title, but occasionally not), while
78 >>>>> displaying meta-titles, does look quite odd. --s
79
80 >>>> As I read the regexp in `cmpspec_translate`, the "command"
81 >>>> is required to have params. They should be optional, 
82 >>>> to match the documentation and because most sort methods
83 >>>> do not need parameters. --J
84
85 >>>>> No, `$2` is either `\w+\([^\)]*\)` or `[^\s]+` (with the
86 >>>>> latter causing an error later if it doesn't also match `\w+`).
87 >>>>> This branch doesn't add any parameterized sort methods,
88 >>>>> in fact, although I did provide one on
89 >>>>> [[field's_discussion_page|plugins/contrib/report/discussion]]. --s
90
91 >>>> I wonder if it would make sense to add some combining keywords, so
92 >>>> a sortspec reads like `sort="age then ascending title"`
93 >>>> In a way, this reduces the amount of syntax that needs to be learned.
94 >>>> I like the "then" (and it could allow other operations than
95 >>>> simple combination, if any others make sense). Not so sure about the
96 >>>> "ascending", which could be "reverse" instead, but "descending age" and
97 >>>> "ascending age" both seem useful to be able to explicitly specify.
98 >>>> --[[Joey]]
99
100 >>>>> Perhaps. I do like the simplicity of [[KathrynAndersen]]'s syntax
101 >>>>> from [[plugins/contrib/report]] (which I copied verbatim, except for
102 >>>>> turning sort-by-`field` into a parameterized spec).
103 >>>>>
104 >>>>> If we're getting into English-like (or at least SQL-like) queries,
105 >>>>> it might make sense to change the signature of the hook function
106 >>>>> so it's a function to return a key, e.g.
107 >>>>> `sub key_age { return -%pagemtime{$_[0]) }`. Then we could sort like
108 >>>>> this:
109 >>>>>
110 >>>>>     field(artistsort) or field(artist) or constant(Various Artists) then meta(titlesort) or meta(title) or title
111 >>>>>
112 >>>>> with "or" binding more closely than "then". Does this seem valuable?
113 >>>>> I think the implementation would be somewhat more difficult. and
114 >>>>> it's probably getting too complicated to be worthwhile, though?
115 >>>>> (The keys that actually benefit from this could just
116 >>>>> have smarter cmp functions, I think.)
117 >>>>>
118 >>>>> If the hooks return keys rather than cmp results, then we could even
119 >>>>> have "lowercase" as an adjective used like "ascending"... maybe.
120 >>>>> However, there are two types of adjective here: "lowercase"
121 >>>>> really applies to the keys, whereas "ascending" applies to the "cmp"
122 >>>>> result. Again, I think this is getting too complex, and could just
123 >>>>> be solved with smarter cmp functions.
124 >>>>>
125 >>>>> Unfortunately, `sort="ascending mtime"` actually sorts by *descending*
126 >>>>> timestamp (but`sort=age` is fine, because `age` could be defined as
127 >>>>> now minus `ctime`). `sort=freshness` isn't right either, because
128 >>>>> "sort by freshness" seems as though it ought to mean freshest first,
129 >>>>> but "sort by ascending freshness" means put the least fresh first. If
130 >>>>> we have ascending and descending keywords which are optional, I don't
131 >>>>> think we really want different sort types to have different default
132 >>>>> directions - it seems clearer to have `ascending` always be a no-op,
133 >>>>> and `descending` always negate.
134 >>>>>
135 >>>>> Perhaps we could borrow from `meta updated` and use `update_age`?
136 >>>>> `updateage` would perhaps be a more normal IkiWiki style - but that
137 >>>>> makes me think that updateage is a quantity analagous to tonnage or
138 >>>>> voltage, with more or less recently updated pages being said to have
139 >>>>> more or less updateage. I don't know whether that's good or bad :-)
140 >>>>>
141 >>>>> I'm sure there's a much better word, but I can't see it. Do you have
142 >>>>> a better idea? --s
143
144 ## Documentation from sort-package branch
145
146 ### meta_title sort order (conditionally added to [[ikiwiki/pagespec/sorting]])
147
148 * `meta_title` - Order according to the `\[[!meta title="foo" sort="bar"]]`
149   or `\[[!meta title="foo"]]` [[ikiwiki/directive]], or the page name if no
150   full title was set.
151
152   > I feel it sould be clearer to call that "sortas", since "sort=" is used
153   > to specify a sort method in other directives. --[[Joey]]
154
155   >> Fair enough, that's easy to do. --[[smcv]]
156
157 ### Multiple sort orders (added to [[ikiwiki/pagespec/sorting]])
158
159 In addition, you can combine several sort orders and/or reverse the order of
160 sorting, with a string like `age -title` (which would sort by age, then by
161 title in reverse order if two pages have the same age).
162
163 ### meta title sort parameter (added to [[ikiwiki/directive/meta]])
164
165 An optional `sort` parameter will be used preferentially when
166 [[ikiwiki/pagespec/sorting]] by `meta_title`:
167
168        \[[!meta title="The Beatles" sort="Beatles, The"]]
169
170        \[[!meta title="David Bowie" sort="Bowie, David"]]
171
172 > I now realise that `author` should also have this, again for use
173 > with (Western) names. --s
174
175 ### Sorting plugins (added to [[plugins/write]])
176
177 Similarly, it's possible to write plugins that add new functions as
178 [[ikiwiki/pagespec/sorting]] methods. To achieve this, add a function to
179 the IkiWiki::PageSpec package named `cmp_foo`, which will be used when sorting
180 by `foo` or `foo(...)` is requested.
181
182 The function will be passed three or more parameters. The first two are
183 page names, and the third is `undef` if invoked as `foo`, or the parameter
184 `"bar"` if invoked as `foo(bar)`. It may also be passed additional, named
185 parameters.
186
187 It should return the same thing as Perl's `cmp` and `<=>` operators: negative
188 if the first argument is less than the second, positive if the first argument
189 is greater, or zero if they are considered equal. It may also raise an
190 error using `error`, for instance if it needs a parameter but one isn't
191 provided.
192
193 You can also define a function called `check_cmp_foo` in the same package.
194 If you do, it will be called while preparing to sort by `foo` or `foo(bar)`,
195 with argument `undef` or `"bar"` respectively; it may raise an error using
196 `error`, if sorting like that isn't going to work.