X-Git-Url: http://git.vanrenterghem.biz/git.ikiwiki.info.git/blobdiff_plain/cda8592a5fd5595d12f0503bfcd2e0640e0afc86..a09c79ccf1b9523c4b82db8846435c81f7404d44:/doc/todo/dependency_types.mdwn diff --git a/doc/todo/dependency_types.mdwn b/doc/todo/dependency_types.mdwn index 58b5ee955..7714f2891 100644 --- a/doc/todo/dependency_types.mdwn +++ b/doc/todo/dependency_types.mdwn @@ -105,11 +105,11 @@ unnecessary page rebuilds: I propose the following. --[[Joey]] -* Add a second type of dependency, call it an "contentless dependency". +* Add a second type of dependency, call it an "presence dependency". * `add_depends` defaults to adding a regular ("full") dependency, as before. (So nothing breaks.) -* `add_depends($page, $spec, content => 0)` adds an contentless dependency. -* `refresh` only looks at added/removed pages when resolving contentless +* `add_depends($page, $spec, presence => 0)` adds an presence dependency. +* `refresh` only looks at added/removed pages when resolving presence dependencies. This seems straightforwardly doable. I'd like [[Will]]'s feedback on it, if @@ -129,28 +129,194 @@ I implemented the above in a branch. Then I found some problems: * Something simple like pagecount, that seems like it could use a - contentless dependency, can have a pagespec that uses metadata, like + presence dependency, can have a pagespec that uses metadata, like `author()` or `copyright()`. -* pagestats, orphans and brokenlinks cannot use contentless dependencies +* pagestats, orphans and brokenlinks cannot use presence dependencies because they need to update when links change. -Now I'm thinking about having a contentless dependency look at page +Now I'm thinking about having a special dependency look at page metadata, and fire if the metadata changes. And it seems links should either be included in that, or there should be a way to make a dependency that fires when a page's links change. (And what about backlinks?) It's easy to see when a page's links change, since there is `%oldlinks`. To see when metadata is changed is harder, since it's stored in the -pagestate by the meta plugin. +pagestate by the meta plugin. Also, there are many different types of +metadata, that would need to be matched with the pagespecs somehow. Quick alternative: Make add_depends look at the pagespec. Ie, if it -is a simple page name, or a glob, we know a contentless dependency +is a simple page name, or a glob, we know a presence dependency can be valid. If's more complex, convert the dependency from -contentless to full. +presence to full. -There is a lot to dislike about this method. Its parsing of the -pagespec, as currently implemented, does not let plugins add new types of -pagespecs that are contentless. Its pagespec parsing is also subject to +There is a lot to dislike about this method. Its parsing of the pagespec, +as currently implemented, does not let plugins add new types of pagespecs +that only care about presence. Its pagespec parsing is also subject to false negatives (though these should be somewhat rare, and no false positives). Still, it does work, and it makes things like simple maps and pagecounts much more efficient. + +---- + +#### Will's first pass feedback. + +If the API is going to be updated, then it would be good to make it forward compatible. +I'd like for the API to be extendible to what is useful for complex pagespecs, even if we +that is a little redundant at the moment. + +My attempt to play with this is in my git repo. [[!template id=gitbranch branch=origin/depends-spec author="[[will]]"]] +That branch is a little out of date, but if you just look at the changes in IkiWiki.pm you'll see the concept I was looking at. +I added an "add_depends_spec()" function that adds a dependency on the pagespec passed to it. If the set of matched pages +changes, then the dependent page is rebuilt. At the moment the implementation uses the same hack used by map and inline - +just add all the pages that currently exist as traditional content dependencies. + +> As I note below, a problem with this approach is that it has to try +> matching the pagespec against every page, redundantly with the work done +> by the plugin. (But there are ways to avoid that redundant matching.) +> --[[Joey]] + +Getting back to commenting on your proposal: + +Just talking about the definition of a "presence dependency" for the moment, and ignoring implementation. Is a +"presence dependency" supposed to cause an update when a page disappears? I assume so. Is a presence dependency +supposed to cause an update when a pages existence hasn't changed, but it no longer matches the pagespec. +(e.g. you use `created_before(test_page)` in a pagespec, and there was a page, `new_page`, that was created +after `test_page`. `new_page` will not match the spec. Now we'll delete and then re-create `test_page`. Now +`new_page` will match the spec, and yet `new_page` itself hasn't changed. Nor has its 'presence' - it was present +before and it is present now. Should this cause a re-build of any page that has a 'presence' dependency on the spec? + +> Yes, a presence dep will trigger when a page is added, or removed. + +> Your example is valid.. but it's also not handled right by normal, +> (content) dependencies, for the same reasons. --[[Joey]] + +I think that is another version of the problem you encountered with meta-data. + +In the longer term I was thinking we'd have to introduce a concept of 'internal pagespec dependencies'. Note that I'm +defining 'internal' pagespec dependencies differently to the pagespec dependencies I defined above. Perhaps an example: +If you had a pagespec that was `created_before(test_page)`, then you could list all pages created before `test_page` +with a `map` directive. The map directive would add a pagespec dependency on `created_before(test_page)`. +Internally, there would be a second page-spec parsing function that discovers which pages a given pagespec +depends on. As well as the function `match_created_before()`, we'd have to add a new function `depend_created_before()`. +This new function would return a list of pages, which when any of them change, the output of `match_created_before()` +would change. In this example, it would just return `test_page`. + +These lists of dependent pages could just be concatenated for every `match_...()` function in a pagespec - you can ignore +the boolean formula aspects of the pagespec for this. If a content dependency were added on these pages, then I think +the correct rebuilds would occur. + +In all, this is a surprisingly difficult problem to solve perfectly. Consider the following case: + +PageA.mdwn: + +> [ShavesSelf] + +PageB.mdwn + +> Doesn't shave self. + +ShavedByBob.mdwn: + +> [!include pages="!link(ShavesSelf)"] + +Does ShavedByBob.mdwn include itself? + +(Yeah - in IkiWiki currently links are included by include, but the idea holds. I had a good example a while back, but I can't think of it right now.) + +sigh. + +-- [[Will]] + +> I have also been thinking about some sort of analysis pass over pagespecs +> to determine what metadata, pages, etc they depend on. It is indeed +> tricky to do. Even if it's just limited to returning a list of pages +> as you suggest. +> +> Consider: For a `*` glob, it has to return a list of all pages +> in the wiki. Which is expensive. And what if the pagespec is +> something like `* and backlink(index)`? Without analyising the +> boolean relationship between terms, the returned list +> will have many more items in it than it should. Or do we not make +> globs return their matches? (If so we have to deal with those +> with one of the other methods disucssed.) --[[Joey]] + +---- + +### Link dependencies + +* `add_depends($page, $spec, links => 1, presence => 1)` + adds a links + presence dependency. +* `refresh` only rebuilds a page with a links dependency if + pages matched by the pagespec gain or lose links. (What the link + actually points to may change independent of this, due to changes + elsewhere, without it firing.) +* So, brokenlinks can fire whenever any links in any of the + pages it's tracking change, or when pages are added or + removed. +* To determine if a pagespec is valid to be used with a links dependency, + use the same set that are valid for presence dependencies. But also + allow `backlinks()` to be used in it, since that matches pages + that the page links to, which is just what link dependencies are + triggered on. + +---- + +### the removal problem + +So far I have not addressed fixing the removal problem (which Will +discusses above). + +Summary of problem: A has a dependency on a pagespec such as +"bugs/* and !link(done)". B currently matches. Then B is updated, +in a way that makes A's dependency not match it (ie, it links to done). +Now A is not updated, because ikiwiki does not realize that it +depended on B before. + +This was worked around to fix [[bugs/inline_page_not_updated_on_removal]] +by inline and map adding explicit dependencies on each page that appears +on them. Then a change to B triggers the explicit dep. While this works, +it's 1) ugly 2) probably not implemented by all plugins that could +be affected by this problem (ie, linkmap) and 3) is most of the reason why +we grew the complication of `depends_simple`. + +One way to fix this is to include with each dependency, a list of pages +that currently match it. If the list changes, the dependency is triggered. + +Should be doable, but may involve more work than +currently. Consider that a dependency on "bugs/*" currently +is triggered by just checking until *one* page is found to match it. +But to store the list, *every* page would have to be tried against it. +Unless the list can somehow be intelligently updated, looking at only the +changed pages. + +---- + +What if there were a function that added a dependency, and at the same time +returned a list of pages matching the pagespec? Plugins that use this would +be exactly the ones, like inline and map, for which this is a problem, and +which already do a match pass over all pages. + +Adding explicit dependencies during this pass would thus be nearly free. +Not 100% free since it would add explicit deps for things that are not +shown on an inline that limits its display to the first sorted N items. +I suppose we could reach 100% free by making the function also handle +sorting and limiting, though that could be overkill. + +---- + +Found a further complication in presence dependencies. Map now uses +presence dependencies when adding its explicit dependencies on pages. But +this defeats the purpose of the explicit dependencies! Because, now, +when B is changed to not match a pagespec, the A's presence dep does +not fire. + +I didn't think things through when switching it to use presense +dependencies there. But, if I change it to use full dependencies, then all +the work that was done to allow map to use presence dependencies for its +main pagespec is for naught. The map will once again have to update +whenever *any* content of the page changes. + +This points toward the conclusion that explicit dependencies, however they +are added, are not the right solution at all. Some other approach, such as +maintaining the list of pages that match a dependency, and noticing when it +changes, is needed.