problem with the patch
[ikiwiki] / doc / bugs / map_is_inconsistent_about_bare_directories.mdwn
1 The [[plugins/map]] plugin has inconsistent behaviour.  In particular, I have in my wiki some directory structures holding files without wikitext pointers (I point directly to the files from elsewhere).  For example, imagine the following file structure in the source dir:
2
3     ; ls -R dirA dirB
4     dirA:
5     subA        subB
6     
7     dirA/subA:
8     filea.mdwn  fileb.mdwn
9     
10     dirA/subB:
11     filec.mdwn  filed.mdwn
12     
13     dirB:
14     subA        subC
15     
16     dirB/subA:
17     filea.mdwn
18     
19     dirB/subC:
20     fileb.mdwn  filec.mdwn
21
22 When I use map to make a map of this, the result looks more like this:
23
24 <ul>
25 <li><span class="createlink">? dirA</span>
26 <ul>
27 <li><span class="createlink">? subA</span>
28 <ul>
29 <li>filea
30 </li>
31 </ul>
32 <ul>
33 <li>fileb
34 </li>
35 </ul>
36 <ul>
37 <li>filec
38 </li>
39 <li>filed
40 </li>
41 </ul>
42 </li>
43 </ul>
44 </li>
45 <li><span class="createlink">? dirB</span>
46 <ul>
47 <li><span class="createlink">? subA</span>
48 <ul>
49 <li>filea
50 </li>
51 </ul>
52 </li>
53 </ul>
54 <ul>
55 <li><span class="createlink">? subC</span>
56 <ul>
57 <li>fileb
58 </li>
59 </ul>
60 <ul>
61 <li>filec
62 </li>
63 </ul>
64 </li>
65 </ul>
66 </li>
67 </ul>
68
69 Note that while the dirA/subA directory exists with a create link, the dirA/subB directory is missing from the map.  Interestingly, dirB/subC is shown in the map.  If you add a second file to dirB/subA then dirB/subC disappears as well.
70
71 I could imagine including all 'bare' directories in the map, and I could imagine including no 'bare' directories in the map.  Just including the first bare directory seems a strange intermediate point.
72
73 Attached is a [[patch]] that fixes the issue.  The current map code makes one pass over the sorted list of pages.  This adds an initial pass that goes through and makes sure that all parent directories are included.  With this initial pass added, the following pass could probably be simplified.
74
75 One solution could also use the [[plugins/autoindex]] plugin to make sure that parent pages actually exist.  This is really only a stop-gap solution until the patch is applied - map still needs to be made bug-free.
76
77 Note: This patch adds items to a map while it is in a foreach loop over a sorted list of keys from that same map.  Changing a map while iterating through it is normally problematic.  I'm assuming the sort insulates the code from this - I do not need to iterate over any of the newly added elements.
78
79 > This patch causes a small bug in the case where the map includes pages in
80 > two subdirs, and does not explicitly include either subdir. In that case,
81 > the map is supposed to show both subdirs, but use special styling an no
82 > link for either. With the patch, which otherwise seems to work ok, some
83 > of the non-included subdirs are displayed as if included. (I
84 > assume because the loop you added does in fact include them..)
85
86 > Example of the problem: \[[!map pages="plugins/contrib/* or plugins/type/*"]]
87 > --[[Joey]]
88
89     diff --git a/IkiWiki/Plugin/map.pm b/IkiWiki/Plugin/map.pm
90     index 5b6a843..16de45e 100644
91     --- a/IkiWiki/Plugin/map.pm
92     +++ b/IkiWiki/Plugin/map.pm
93     @@ -67,6 +67,39 @@ sub preprocess (@) { #{{{
94         # are removed.
95         add_depends($params{page}, join(" or ", keys %mapitems));
96      
97     +   # Include all the parent directories in the map
98     +   my $lastbase="";
99     +   my $commonbase = "";
100     +   $commonbase = $common_prefix if defined $common_prefix && length $common_prefix;
101     +   foreach my $item (sort keys %mapitems) {
102     +           $item=~s/^\Q$common_prefix\E\///
103     +                   if defined $common_prefix && length $common_prefix;
104     +           my $itembase=IkiWiki::dirname($item);
105     +           if ($itembase ne $lastbase) {
106     +                   # find the common dir
107     +                   my @a=split(/\//, $itembase);
108     +                   my @b=split(/\//, $lastbase);
109     +                   my $common_dir=$commonbase;
110     +                   while (@a && @b && $a[0] eq $b[0]) {
111     +                           if (length $common_dir) {
112     +                                   $common_dir.="/";
113     +                           }
114     +                           $common_dir.=shift(@a);
115     +                           shift @b;
116     +                   }
117     +                   # add all the dirs down to the current base
118     +                   while (@a) {
119     +                           if (length $common_dir) {
120     +                                   $common_dir.="/";
121     +                           }
122     +                           $common_dir.=shift(@a);
123     +                           $mapitems{$common_dir}=''
124     +                                   unless defined $mapitems{$common_dir};
125     +                   }
126     +                   $lastbase = $itembase;
127     +           }
128     +   }
129     +
130         # Create the map.
131         my $parent="";
132         my $indent=0;
133
134 -- [[users/Will]]