Skip to content

Commit be4c1ca

Browse files
search: fix search-entries for index page (#1446)
1 parent 9f2a0a1 commit be4c1ca

5 files changed

Lines changed: 101 additions & 36 deletions

File tree

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2026 znai maintainers
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.testingisdocumenting.znai.search;
18+
19+
import org.testingisdocumenting.znai.core.DocMeta;
20+
import org.testingisdocumenting.znai.markdown.PageMarkdownSection;
21+
import org.testingisdocumenting.znai.structure.DocStructure;
22+
import org.testingisdocumenting.znai.structure.DocUrl;
23+
import org.testingisdocumenting.znai.structure.TocItem;
24+
25+
import java.util.List;
26+
27+
import static java.util.stream.Collectors.toList;
28+
29+
public class GlobalSearchEntriesBuilder {
30+
private final DocMeta docMeta;
31+
private final DocStructure docStructure;
32+
private final GlobalSearchEntries globalSearchEntries;
33+
34+
public GlobalSearchEntriesBuilder(DocMeta docMeta, DocStructure docStructure) {
35+
this.docMeta = docMeta;
36+
this.docStructure = docStructure;
37+
this.globalSearchEntries = new GlobalSearchEntries();
38+
}
39+
40+
public GlobalSearchEntries getGlobalSearchEntries() {
41+
return globalSearchEntries;
42+
}
43+
44+
public void addSearchEntries(TocItem tocItem, List<PageMarkdownSection> sections) {
45+
List<GlobalSearchEntry> entries = sections.stream()
46+
.filter(section -> !(section.title().isEmpty() && section.markdown().trim().isEmpty()))
47+
.map(section -> new GlobalSearchEntry(
48+
docMeta,
49+
tocItem,
50+
section,
51+
searchEntryUrl(tocItem, section)))
52+
.collect(toList());
53+
54+
globalSearchEntries.addAll(entries);
55+
}
56+
57+
String searchEntryUrl(TocItem tocItem, PageMarkdownSection section) {
58+
if (tocItem.isIndex()) {
59+
String anchorIdWithHash = section.id().isEmpty() ? "" : "#" + section.id();
60+
return docStructure.fullUrl(anchorIdWithHash);
61+
}
62+
63+
DocUrl docUrl = new DocUrl(tocItem.getDirName(), tocItem.getFileNameWithoutExtension(), section.id());
64+
return docStructure.createUrl(null, docUrl);
65+
}
66+
}

znai-core/src/test/groovy/org/testingisdocumenting/znai/search/GlobalSearchEntriesTest.groovy

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package org.testingisdocumenting.znai.search
2020
import org.junit.Test
2121
import org.testingisdocumenting.znai.core.DocMeta
2222
import org.testingisdocumenting.znai.markdown.PageMarkdownSection
23+
import org.testingisdocumenting.znai.parser.TestDocStructure
2324
import org.testingisdocumenting.znai.structure.TocItem
2425

2526
class GlobalSearchEntriesTest {
@@ -36,16 +37,11 @@ class GlobalSearchEntriesTest {
3637
tocItem2.setChapterTitle('chapter 2')
3738
def section2 = new PageMarkdownSection('section2', 'section 2', 'text 2')
3839

39-
def tocItemIndex = TocItem.createIndex()
40-
def sectionIndex = new PageMarkdownSection('', '', 'index text')
41-
4240
def entries = new GlobalSearchEntries()
4341
entries.addAll([
4442
new GlobalSearchEntry(docMeta, tocItem1, section1, '/doc-id/title1'),
45-
new GlobalSearchEntry(docMeta, tocItem2, section2, '/doc-id/title2'),
46-
new GlobalSearchEntry(docMeta, tocItemIndex, sectionIndex, '/doc-id')])
43+
new GlobalSearchEntry(docMeta, tocItem2, section2, '/doc-id/title2')])
4744

48-
println entries.toXml()
4945
entries.toXml().should == '<znai>\n' +
5046
' <entry>\n' +
5147
' <url>/doc-id/title1</url>\n' +
@@ -69,20 +65,33 @@ class GlobalSearchEntriesTest {
6965
' <text>text 2</text>\n' +
7066
' </text>\n' +
7167
' </entry>\n' +
72-
' <entry>\n' +
73-
' <url>/doc-id</url>\n' +
74-
' <fullTitle>Test Doc</fullTitle>\n' +
75-
' <pageTitle></pageTitle>\n' +
76-
' <chapterTitle></chapterTitle>\n' +
77-
' <pageSectionTitle></pageSectionTitle>\n' +
78-
' <text>\n' +
79-
' <score>STANDARD</score>\n' +
80-
' <text>index text</text>\n' +
81-
' </text>\n' +
82-
' </entry>\n' +
8368
'</znai>\n'
8469
}
8570

71+
@Test
72+
void "should generate index page search entry url without index in path"() {
73+
def docMeta = new DocMeta([title: 'Test Doc'])
74+
docMeta.setId('doc-id')
75+
def docStructure = new TestDocStructure()
76+
77+
def builder = new GlobalSearchEntriesBuilder(docMeta, docStructure)
78+
79+
def tocItemIndex = TocItem.createIndex()
80+
builder.addSearchEntries(tocItemIndex, [
81+
new PageMarkdownSection('', '', 'index text'),
82+
new PageMarkdownSection('intro', 'Introduction', 'intro text')])
83+
84+
def tocItem = new TocItem('chapter', 'page', 'md')
85+
tocItem.setPageTitleIfNoTocOverridePresent('page')
86+
tocItem.setChapterTitle('chapter')
87+
builder.addSearchEntries(tocItem, [
88+
new PageMarkdownSection('section', 'section', 'text')])
89+
90+
builder.searchEntryUrl(tocItemIndex, new PageMarkdownSection('', '', '')).should == '/test-doc/'
91+
builder.searchEntryUrl(tocItemIndex, new PageMarkdownSection('intro', 'Introduction', '')).should == '/test-doc/#intro'
92+
builder.searchEntryUrl(tocItem, new PageMarkdownSection('section', 'section', '')).should == '/test-doc/chapter/page#section'
93+
}
94+
8695
@Test
8796
void "should handle ansi sequences"() {
8897
def docMeta = new DocMeta([title: 'title', type: 'Guide'])
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* Fix: `search-entries.xml` correctly render index page url
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 1.85.2
2+
3+
:include-markdowns: 1.85.2
4+
15
# 1.85
26

37
:include-markdowns: 1.85

znai-website-gen/src/main/java/org/testingisdocumenting/znai/website/WebSite.java

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import org.testingisdocumenting.znai.html.*;
2929
import org.testingisdocumenting.znai.html.reactjs.HtmlReactJsPage;
3030
import org.testingisdocumenting.znai.html.reactjs.ReactJsBundle;
31-
import org.testingisdocumenting.znai.markdown.PageMarkdownSection;
3231
import org.testingisdocumenting.znai.parser.MarkupParser;
3332
import org.testingisdocumenting.znai.parser.MarkupParserResult;
3433
import org.testingisdocumenting.znai.parser.commonmark.MarkdownParser;
@@ -74,7 +73,7 @@ public class WebSite implements Log {
7473

7574
private Map<TocItem, Page> pageByTocItem;
7675
private Map<TocItem, MarkupParserResult> parserResultByTocItem;
77-
private final GlobalSearchEntries globalSearchEntries;
76+
private GlobalSearchEntriesBuilder globalSearchEntriesBuilder;
7877
private final LocalSearchEntries localSearchEntries;
7978

8079
private TableOfContents toc;
@@ -130,7 +129,6 @@ private WebSite(Configuration siteConfig) {
130129
searchIndexJavaScript = WebResource.moduleWithPath(SEARCH_INDEX_FILE_NAME);
131130
auxiliaryFilesRegistry = new AuxiliaryFilesRegistry();
132131
markupParsingConfiguration = MarkupParsingConfigurations.byName(cfg.documentationType);
133-
globalSearchEntries = new GlobalSearchEntries();
134132
localSearchEntries = new LocalSearchEntries();
135133
auxiliaryFilesLastUpdateTime = new HashMap<>();
136134

@@ -397,6 +395,7 @@ private void createTopLevelToc() {
397395
private void createDocStructure() {
398396
docStructure = new WebSiteDocStructure(componentsRegistry, docMeta, toc, markupParsingConfiguration);
399397
componentsRegistry.setDocStructure(docStructure);
398+
globalSearchEntriesBuilder = new GlobalSearchEntriesBuilder(docMeta, docStructure);
400399
}
401400

402401
private void createResourceResolvers() {
@@ -590,24 +589,10 @@ private void updateTocItemWithPageMeta(TocItem tocItem, PageMeta pageMeta) {
590589
}
591590

592591
private void updateSearchEntries(TocItem tocItem, MarkupParserResult parserResult) {
593-
List<GlobalSearchEntry> siteSearchEntries = parserResult.markdown().sections().stream()
594-
.filter(section -> !(section.title().isEmpty() && section.markdown().trim().isEmpty()))
595-
.map(section -> new GlobalSearchEntry(
596-
docMeta,
597-
tocItem,
598-
section,
599-
searchEntryUrl(tocItem, section)))
600-
.collect(toList());
601-
602-
globalSearchEntries.addAll(siteSearchEntries);
592+
globalSearchEntriesBuilder.addSearchEntries(tocItem, parserResult.markdown().sections());
603593
localSearchEntries.add(new PageLocalSearchEntries(tocItem, parserResult.searchEntries()));
604594
}
605595

606-
private String searchEntryUrl(TocItem tocItem, PageMarkdownSection section) {
607-
DocUrl docUrl = new DocUrl(tocItem.getDirName(), tocItem.getFileNameWithoutExtension(), section.id());
608-
return docStructure.createUrl(null, docUrl);
609-
}
610-
611596
// each markup file may refer other files like code snippets or diagrams
612597
// we maintain dependency between them, so we know which one triggers what page refresh during preview mode
613598
//
@@ -663,7 +648,7 @@ private void generateSearchIndex() {
663648
String jsIndexScript = localSearchEntries.buildIndexScript();
664649
deployer.deploy("search-index.js", jsIndexScript);
665650

666-
String xmlExternalIndex = globalSearchEntries.toXml();
651+
String xmlExternalIndex = globalSearchEntriesBuilder.getGlobalSearchEntries().toXml();
667652
deployer.deploy("search-entries.xml", xmlExternalIndex);
668653
}
669654

0 commit comments

Comments
 (0)