@@ -40,31 +40,46 @@ public function __construct(Metas $metas, BuildConfig $buildConfig)
40
40
/**
41
41
* Returns an array of each JSON file string, keyed by the input filename
42
42
*
43
+ * @param string $masterDocument The file whose toctree should be read first
43
44
* @return string[]
44
45
*/
45
- public function generateJson (): array
46
+ public function generateJson (string $ masterDocument = ' index ' ): array
46
47
{
47
48
$ fs = new Filesystem ();
48
49
49
50
$ progressBar = new ProgressBar ($ this ->output ?: new NullOutput ());
50
51
$ progressBar ->setMaxSteps (\count ($ this ->metas ->getAll ()));
51
52
53
+ $ walkedFiles = [];
54
+ $ tocTreeHierarchy = $ this ->walkTocTreeAndReturnHierarchy (
55
+ $ masterDocument ,
56
+ $ walkedFiles
57
+ );
58
+ // for purposes of prev/next/parents, the "master document"
59
+ // behaves as if it's the first item in the toctree
60
+ $ tocTreeHierarchy = [$ masterDocument => []] + $ tocTreeHierarchy ;
61
+ $ flattenedTocTree = $ this ->flattenTocTree ($ tocTreeHierarchy );
62
+
52
63
$ fJsonFiles = [];
53
64
foreach ($ this ->metas ->getAll () as $ filename => $ metaEntry ) {
54
65
$ parserFilename = $ filename ;
55
66
$ jsonFilename = $ this ->buildConfig ->getOutputDir ().'/ ' .$ filename .'.fjson ' ;
56
67
57
68
$ crawler = new Crawler (file_get_contents ($ this ->buildConfig ->getOutputDir ().'/ ' .$ filename .'.html ' ));
58
69
70
+ $ next = $ this ->determineNext ($ parserFilename , $ flattenedTocTree , $ masterDocument );
71
+ $ prev = $ this ->determinePrev ($ parserFilename , $ flattenedTocTree );
59
72
$ data = [
60
73
'title ' => $ metaEntry ->getTitle (),
74
+ 'parents ' => $ this ->determineParents ($ parserFilename , $ tocTreeHierarchy ) ?: [],
61
75
'current_page_name ' => $ parserFilename ,
62
76
'toc ' => $ this ->generateToc ($ metaEntry , current ($ metaEntry ->getTitles ())[1 ]),
63
- 'next ' => $ this -> guessNext ( $ parserFilename ) ,
64
- 'prev ' => $ this -> guessPrev ( $ parserFilename ) ,
77
+ 'next ' => $ next ,
78
+ 'prev ' => $ prev ,
65
79
'rellinks ' => [
66
- $ this ->guessNext ($ parserFilename ),
67
- $ this ->guessPrev ($ parserFilename ),
80
+ // probably these shouldn't be straight copies of next/prev
81
+ $ next ,
82
+ $ prev ,
68
83
],
69
84
'body ' => $ crawler ->filter ('body ' )->html (),
70
85
];
@@ -109,128 +124,162 @@ private function generateToc(MetaEntry $metaEntry, ?array $titles): array
109
124
return $ tocTree ;
110
125
}
111
126
112
- private function guessNext (string $ parserFilename ): ?array
127
+ private function determineNext (string $ parserFilename, array $ flattenedTocTree ): ?array
113
128
{
114
- $ meta = $ this ->getMetaEntry ($ parserFilename , true );
115
-
116
- $ parentFile = $ meta ->getParent ();
129
+ $ foundCurrentFile = false ;
130
+ $ nextFileName = null ;
117
131
118
- // if current file is an index, next is the first chapter
119
- if ( ' index ' === $ parentFile && 1 === \count ( $ tocs = $ meta -> getTocs ()) && \count ( $ tocs [ 0 ]) > 0 ) {
120
- $ firstChapterMeta = $ this -> getMetaEntry ( $ tocs [ 0 ][ 0 ]) ;
132
+ foreach ( $ flattenedTocTree as $ filename ) {
133
+ if ( $ foundCurrentFile ) {
134
+ $ nextFileName = $ filename ;
121
135
122
- if (null === $ firstChapterMeta ) {
123
- return null ;
136
+ break ;
124
137
}
125
138
126
- return [
127
- 'title ' => $ firstChapterMeta ->getTitle (),
128
- 'link ' => $ firstChapterMeta ->getUrl (),
129
- ];
139
+ if ($ filename === $ parserFilename ) {
140
+ $ foundCurrentFile = true ;
141
+ }
130
142
}
131
143
132
- [$ toc , $ indexCurrentFile ] = $ this ->getNextPrevInformation ($ parserFilename );
133
-
134
- if (!isset ($ toc [$ indexCurrentFile + 1 ])) {
144
+ // no next document found!
145
+ if (null === $ nextFileName ) {
135
146
return null ;
136
147
}
137
148
138
- $ nextFileName = $ toc [$ indexCurrentFile + 1 ];
139
-
140
- $ nextMeta = $ this ->getMetaEntry ($ nextFileName );
141
-
142
- if (null === $ nextMeta ) {
143
- return null ;
144
- }
149
+ $ meta = $ this ->getMetaEntry ($ nextFileName );
145
150
146
151
return [
147
- 'title ' => $ nextMeta ->getTitle (),
148
- 'link ' => $ nextMeta ->getUrl (),
152
+ 'title ' => $ meta ->getTitle (),
153
+ 'link ' => $ meta ->getUrl (),
149
154
];
150
155
}
151
156
152
- private function guessPrev (string $ parserFilename ): ?array
157
+ private function determinePrev (string $ parserFilename, array $ flattenedTocTree ): ?array
153
158
{
154
- $ meta = $ this ->getMetaEntry ($ parserFilename , true );
155
- $ parentFile = $ meta ->getParent ();
156
-
157
- [$ toc , $ indexCurrentFile ] = $ this ->getNextPrevInformation ($ parserFilename );
158
-
159
- // if current file is the first one of the chapter, prev is the direct parent
160
- if (0 === $ indexCurrentFile ) {
161
- $ parentMeta = $ this ->getMetaEntry ($ parentFile );
162
-
163
- if (null === $ parentMeta ) {
164
- return null ;
159
+ $ previousFileName = null ;
160
+ $ foundCurrentFile = false ;
161
+ foreach ($ flattenedTocTree as $ filename ) {
162
+ if ($ filename === $ parserFilename ) {
163
+ $ foundCurrentFile = true ;
164
+ break ;
165
165
}
166
166
167
- return [
168
- 'title ' => $ parentMeta ->getTitle (),
169
- 'link ' => $ parentMeta ->getUrl (),
170
- ];
167
+ $ previousFileName = $ filename ;
171
168
}
172
169
173
- if (!isset ($ toc [$ indexCurrentFile - 1 ])) {
170
+ // no previous document found!
171
+ if (null === $ previousFileName || !$ foundCurrentFile ) {
174
172
return null ;
175
173
}
176
174
177
- $ prevFileName = $ toc [$ indexCurrentFile - 1 ];
178
-
179
- $ prevMeta = $ this ->getMetaEntry ($ prevFileName );
180
-
181
- if (null === $ prevMeta ) {
182
- return null ;
183
- }
175
+ $ meta = $ this ->getMetaEntry ($ previousFileName );
184
176
185
177
return [
186
- 'title ' => $ prevMeta ->getTitle (),
187
- 'link ' => $ prevMeta ->getUrl (),
178
+ 'title ' => $ meta ->getTitle (),
179
+ 'link ' => $ meta ->getUrl (),
188
180
];
189
181
}
190
182
191
- private function getNextPrevInformation (string $ parserFilename ): ?array
183
+ private function getMetaEntry (string $ parserFilename, bool $ throwOnMissing = false ): ?MetaEntry
192
184
{
193
- $ meta = $ this ->getMetaEntry ($ parserFilename , true );
194
- $ parentFile = $ meta ->getParent ();
185
+ $ metaEntry = $ this ->metas ->get ($ parserFilename );
195
186
196
- if (! $ parentFile ) {
197
- return [ null , null ];
198
- }
187
+ // this is possible if there are invalid references
188
+ if ( null === $ metaEntry ) {
189
+ $ message = sprintf ( ' Could not find MetaEntry for file "%s" ' , $ parserFilename );
199
190
200
- $ metaParent = $ this ->getMetaEntry ($ parentFile );
191
+ if ($ throwOnMissing ) {
192
+ throw new \Exception ($ message );
193
+ }
201
194
202
- if (null === $ metaParent || !$ metaParent ->getTocs () || 1 !== \count ($ metaParent ->getTocs ())) {
203
- return [null , null ];
195
+ if ($ this ->output ) {
196
+ $ this ->output ->note ($ message );
197
+ }
204
198
}
205
199
206
- $ toc = current ($ metaParent ->getTocs ());
200
+ return $ metaEntry ;
201
+ }
207
202
208
- if (\count ($ toc ) < 2 || !isset (array_flip ($ toc )[$ parserFilename ])) {
209
- return [null , null ];
210
- }
203
+ /**
204
+ * Creates a hierarchy of documents by crawling the toctree's
205
+ *
206
+ * This looks at the
207
+ * toc tree of the master document, following the first entry
208
+ * like a link, then repeating the process on the next document's
209
+ * toc tree (if it has one). When it hits a dead end, it would
210
+ * go back to the master document and click the second link.
211
+ * But, it skips any links that have been seen before. This
212
+ * is the logic behind how the prev/next parent information is created.
213
+ *
214
+ * Example result:
215
+ * [
216
+ * 'dashboards' => [],
217
+ * 'design' => [
218
+ * 'crud' => [],
219
+ * 'design/sub-page' => [],
220
+ * ],
221
+ * 'fields' => []
222
+ * ]
223
+ *
224
+ * See the JsonIntegrationTest for a test case.
225
+ */
226
+ private function walkTocTreeAndReturnHierarchy (string $ filename , array &$ walkedFiles ): array
227
+ {
228
+ $ hierarchy = [];
229
+ foreach ($ this ->getMetaEntry ($ filename )->getTocs () as $ toc ) {
230
+ foreach ($ toc as $ tocFilename ) {
231
+ // only walk a file one time, the first time you see it
232
+ if (in_array ($ tocFilename , $ walkedFiles , true )) {
233
+ continue ;
234
+ }
211
235
212
- $ indexCurrentFile = array_flip ($ toc )[$ parserFilename ];
236
+ $ walkedFiles [] = $ tocFilename ;
237
+
238
+ $ hierarchy [$ tocFilename ] = $ this ->walkTocTreeAndReturnHierarchy ($ tocFilename , $ walkedFiles );
239
+ }
240
+ }
213
241
214
- return [ $ toc , $ indexCurrentFile ] ;
242
+ return $ hierarchy ;
215
243
}
216
244
217
- private function getMetaEntry (string $ parserFilename , bool $ throwOnMissing = false ): ?MetaEntry
245
+ /**
246
+ * Takes the structure from walkTocTreeAndReturnHierarchy() and flattens it.
247
+ *
248
+ * For example:
249
+ *
250
+ * [dashboards, design, crud, design/sub-page, fields]
251
+ *
252
+ * @return string[]
253
+ */
254
+ private function flattenTocTree (array $ tocTreeHierarchy ): array
218
255
{
219
- $ metaEntry = $ this -> metas -> get ( $ parserFilename ) ;
256
+ $ files = [] ;
220
257
221
- // this is possible if there are invalid references
222
- if (null === $ metaEntry ) {
223
- $ message = sprintf ('Could not find MetaEntry for file "%s" ' , $ parserFilename );
258
+ foreach ($ tocTreeHierarchy as $ filename => $ tocTree ) {
259
+ $ files [] = $ filename ;
224
260
225
- if ($ throwOnMissing ) {
226
- throw new \Exception ($ message );
261
+ $ files = array_merge ($ files , $ this ->flattenTocTree ($ tocTree ));
262
+ }
263
+
264
+ return $ files ;
265
+ }
266
+
267
+ private function determineParents (string $ parserFilename , array $ tocTreeHierarchy , array $ parents = []): ?array
268
+ {
269
+ foreach ($ tocTreeHierarchy as $ filename => $ tocTree ) {
270
+ if ($ filename === $ parserFilename ) {
271
+ return $ parents ;
227
272
}
228
273
229
- if ($ this ->output ) {
230
- $ this ->output ->note ($ message );
274
+ $ subParents = $ this ->determineParents ($ parserFilename , $ tocTree , $ parents + [$ filename ]);
275
+
276
+ if (null !== $ subParents ) {
277
+ // the item WAS found and the parents were returned
278
+ return $ subParents ;
231
279
}
232
280
}
233
281
234
- return $ metaEntry ;
282
+ // item was not found
283
+ return null ;
235
284
}
236
285
}
0 commit comments