Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

flow: generate additional anchorids for compatibility #1235

Merged
merged 6 commits into from
Sep 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
package org.testingisdocumenting.znai.extensions.api;

import org.testingisdocumenting.znai.core.ComponentsRegistry;
import org.testingisdocumenting.znai.structure.AnchorIds;
import org.testingisdocumenting.znai.structure.DocStructure;

import java.nio.file.Path;
import java.util.Collections;

class ApiParametersAnchors {
private ApiParametersAnchors() {
Expand All @@ -29,7 +31,8 @@ static void registerLocalAnchors(ComponentsRegistry componentsRegistry,
Path markupPath,
ApiParameters apiParameters) {
DocStructure docStructure = componentsRegistry.docStructure();
apiParameters.collectAllAnchors().forEach(anchorId -> docStructure.registerLocalAnchor(markupPath, anchorId));
apiParameters.collectAllAnchors().forEach(anchorId -> docStructure.registerLocalAnchors(markupPath,
new AnchorIds(anchorId, Collections.emptyList())));
}

static String anchorIdFromNameAndPrefix(String prefix, String name) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@
import org.testingisdocumenting.znai.extensions.PluginParams;
import org.testingisdocumenting.znai.extensions.PluginParamsDefinition;
import org.testingisdocumenting.znai.extensions.features.PluginFeature;
import org.testingisdocumenting.znai.structure.AnchorIds;
import org.testingisdocumenting.znai.structure.DocStructure;
import org.testingisdocumenting.znai.utils.NameUtils;

import java.nio.file.Path;
import java.util.Collections;
import java.util.Map;

import static org.testingisdocumenting.znai.extensions.PluginParamsDefinitionCommon.TITLE_KEY;
Expand All @@ -47,16 +49,16 @@ public AnchorFeature(DocStructure docStructure, Path markupPath, PluginParams pl
public void updateProps(Map<String, Object> props) {
String anchorId = pluginParams.getOpts().get(ANCHOR_ID_KEY, "");
if (!anchorId.isEmpty()) {
docStructure.registerLocalAnchor(markupPath, anchorId);
docStructure.registerLocalAnchors(markupPath, new AnchorIds(anchorId, Collections.emptyList()));
return;
}

String title = pluginParams.getOpts().get(TITLE_KEY, props.getOrDefault(TITLE_KEY, "").toString());
if (!title.isEmpty()) {
String uniqueAnchorId = docStructure.generateUniqueAnchor(markupPath, NameUtils.idFromTitle(title));
docStructure.registerLocalAnchor(markupPath, uniqueAnchorId);
var uniqueAnchorIds = docStructure.generateUniqueAnchors(markupPath, NameUtils.idFromTitle(title));
docStructure.registerLocalAnchors(markupPath, uniqueAnchorIds);

props.put(ANCHOR_ID_KEY, uniqueAnchorId);
props.put(ANCHOR_ID_KEY, uniqueAnchorIds.main());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.testingisdocumenting.znai.parser.table.MarkupTableData;
import org.testingisdocumenting.znai.reference.DocReferences;
import org.testingisdocumenting.znai.resources.ResourcesResolver;
import org.testingisdocumenting.znai.structure.AnchorIds;
import org.testingisdocumenting.znai.structure.DocStructure;
import org.testingisdocumenting.znai.structure.DocUrl;
import org.testingisdocumenting.znai.structure.TocItem;
Expand Down Expand Up @@ -102,16 +103,17 @@ public void onSectionStart(String title, HeadingProps headingProps) {

Map<String, ?> headingPropsMap = headingProps.props();

String id = new PageSectionIdTitle(title, headingPropsMap).getId();
DocStructure docStructure = componentsRegistry.docStructure();
String sectionId = new PageSectionIdTitle(title, headingPropsMap).getId();
docStructure.onSectionOrSubHeading(path, 1, sectionId);

var anchorIds = docStructure.generateUniqueAnchors(path, "");
Map<String, Object> props = new LinkedHashMap<>(headingPropsMap);
props.put("id", id);
addAnchorIdsToProps(props, anchorIds);
props.put("title", title);

start(DocElementType.SECTION, props);

DocStructure docStructure = componentsRegistry.docStructure();
docStructure.registerLocalAnchor(path, id);
docStructure.onSectionOrSubHeading(path, 1, id);
docStructure.registerLocalAnchors(path, anchorIds);

isSectionStarted = true;
}
Expand Down Expand Up @@ -139,11 +141,11 @@ public void onSubHeading(int level, String title, HeadingProps headingProps) {
DocStructure docStructure = componentsRegistry.docStructure();
docStructure.onSectionOrSubHeading(path, level, idByTitle);

String id = docStructure.generateUniqueAnchor(path, "");
docStructure.registerLocalAnchor(path, id);
var ids = docStructure.generateUniqueAnchors(path, "");
docStructure.registerLocalAnchors(path, ids);

Map<String, Object> props = new LinkedHashMap<>(headingPropsMap);
props.put("id", id);
addAnchorIdsToProps(props, ids);
props.put("level", level);
props.put("title", title);
append(DocElementType.SUB_HEADING, props);
Expand Down Expand Up @@ -521,5 +523,10 @@ private String convertAndRegisterLocalFileToUrl(String url) {

return docStructure.fullUrl(auxiliaryFile.getDeployRelativePath().toString());
}

private void addAnchorIdsToProps(Map<String, Object> props, AnchorIds ids) {
props.put("id", ids.main());
props.put("additionalIds", ids.additional());
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright 2024 znai maintainers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.testingisdocumenting.znai.structure;

import java.util.List;

public record AnchorIds (String main, List<String> additional) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ public interface DocStructure {
String fullUrl(String relativeUrl);

void onSectionOrSubHeading(Path path, int level, String id);
String generateUniqueAnchor(Path path, String localId);
AnchorIds generateUniqueAnchors(Path path, String localId);

void registerGlobalAnchor(Path sourcePath, String anchorId);
void registerLocalAnchor(Path path, String anchorId);
void registerLocalAnchors(Path path, AnchorIds anchorIds);
String globalAnchorUrl(Path clientPath, String anchorId);
Optional<String> findGlobalAnchorUrl(String globalAnchorId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@
package org.testingisdocumenting.znai.structure;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

Expand All @@ -39,26 +36,57 @@ public void registerSectionOrSubHeading(Path path, Integer level, String id) {
headings.add(new LevelId(level, id));
}

public String generateId(Path path, String nonUniqueId) {
public AnchorIds generateIds(Path path, String nonUniqueId) {
// we create multiple ids: long version and short version
// both ids should work and be recognizable, but we visually expose the long version
var prefixes = buildPrefixes(path, nonUniqueId);
var main = updateWithCountSuffixIfRequired(path, prefixes.usingAllHeadings);
var alternative = updateWithCountSuffixIfRequired(path, prefixes.usingLastHeading);

boolean arePrefixesEqual = prefixes.usingAllHeadings.equals(prefixes.usingLastHeading);
var alternativeList = arePrefixesEqual ?
Collections.<String>emptyList():
Collections.singletonList(alternative);
return new AnchorIds(main, alternativeList);
}

private String updateWithCountSuffixIfRequired(Path path, String prefix) {
var pathsCounts = usedHeadingsCountByPath.computeIfAbsent(path, k -> new HashMap<>());

Integer count = pathsCounts.getOrDefault(prefix, 0);
pathsCounts.put(prefix, count + 1);

if (count == 0) {
return prefix;
}

return prefix + "-" + (count + 1);
}

private Prefixes buildPrefixes(Path path, String nonUniqueId) {
var headings = getOrCreateHeadings(path);
String prefix = headings.stream()

String fullPrefix = headings.stream()
.map(LevelId::getId)
.filter(id -> !id.isEmpty())
.collect(Collectors.joining("-"));

if (!nonUniqueId.isEmpty()) {
prefix = prefix + (prefix.isEmpty() ? nonUniqueId : "-" + nonUniqueId);
String lastHeadingOnly = headings.isEmpty() ? "" : headings.get(headings.size() - 1).id;

return new Prefixes(combinePrefixAndId(fullPrefix, nonUniqueId),
combinePrefixAndId(lastHeadingOnly, nonUniqueId));
}

private String combinePrefixAndId(String prefix, String id) {
if (prefix.isEmpty()) {
return id;
}

var usedHeadingsCount = usedHeadingsCountByPath.computeIfAbsent(path, k -> new HashMap<>());
Integer count = usedHeadingsCount.get(prefix);
if (count == null) {
usedHeadingsCount.put(prefix, 1);
if (id.isEmpty()) {
return prefix;
}

usedHeadingsCount.put(prefix, count + 1);
return prefix + "-" + (count + 1);
return prefix + "-" + id;
}

private List<LevelId> getOrCreateHeadings(Path path) {
Expand All @@ -83,4 +111,6 @@ public String getId() {
return id;
}
}

record Prefixes (String usingAllHeadings, String usingLastHeading) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public static String idFromTitle(final String title) {
if (title == null)
return null;

String onlyTextAndNumbers = title.replaceAll("[^a-zA-Z0-9-_./ ]", "");
String onlyTextAndNumbers = title.replaceAll("[^a-zA-Z0-9-_/ ]", "");
return onlyTextAndNumbers.toLowerCase().replaceAll("[\\s./]+", "-");
}

Expand Down
23 changes: 23 additions & 0 deletions znai-core/src/main/resources/lunrjs/indexCreation.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,30 @@
* limitations under the License.
*/

var createStopWordFilter = function (stopWords) {
var words = stopWords.reduce(function (memo, stopWord) {
memo[stopWord] = stopWord
return memo
}, {})
return function (token) {
if (token && words[token.toString()] !== token.toString()) return token
}
}

var stopWordFilter = createStopWordFilter([
'a',
'am',
'an',
'at',
'be',
'so',
'to'
])

znaiSearchIdx = lunr(function () {
this.pipeline.remove(lunr.stemmer)
this.pipeline.remove(lunr.stopWordFilter)
this.pipeline.add(stopWordFilter)
this.ref('id')
this.field('section')
this.field('pageTitle')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ class JsonIncludePluginTest {
props.should == [data : expectedFullData,
autoTitle : true,
title : "test.json",
anchorId : "test-json",
anchorId : "testjson",
highlightValues: [],
highlightKeys : []]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ class MarkdownParserTest {
@Test
void "header badge"() {
parse('# my header {badge: "v1.32"}')
content.should == [[title: 'my header' , id: 'my-header', badge: 'v1.32', type: 'Section']]
content.should == [[title: 'my header' , id: 'my-header', additionalIds: [], badge: 'v1.32', type: 'Section']]
}

@Test
Expand Down Expand Up @@ -223,39 +223,39 @@ world""")
@Test
void "top level sections"() {
parse("# Section\ntext text")
content.should == [[type: 'Section', id: "section", title: "Section", content:[
content.should == [[type: 'Section', id: "section", additionalIds: [], title: "Section", content:[
[type: "Paragraph", content: [[type: "SimpleText", text: "text text"]]]]]]
}

@Test
void "top level section without text"() {
parse("# ")
content.should == [[type: 'Section', id: "", title: ""]]
content.should == [[type: 'Section', id: "", additionalIds: [], title: ""]]
}

@Test
void "second level section"() {
parse("## Secondary Section \ntext text", Paths.get("new-file.md"))
content.should == [[type: 'SubHeading', level: 2, title: 'Secondary Section', id: 'secondary-section'],
content.should == [[type: 'SubHeading', level: 2, title: 'Secondary Section', id: 'secondary-section', additionalIds: []],
[type: 'Paragraph', content: [[type: 'SimpleText', text: 'text text']]]]
}

@Test
void "second level section without text"() {
parse("## ", Paths.get("empty-header.md"))
content.should == [[type: 'SubHeading', level: 2, title: '', id: '']]
content.should == [[type: 'SubHeading', level: 2, title: '', id: '', additionalIds: []]]
}

@Test
void "header inline code text is allowed"() {
parse('# my header about `thing` here {badge: "v3.4"}')
content.should == [[title: 'my header about thing here', id: 'my-header-about-thing-here', badge: 'v3.4', type: 'Section']]
content.should == [[title: 'my header about thing here', id: 'my-header-about-thing-here', additionalIds: [], badge: 'v3.4', type: 'Section']]
}

@Test
void "sub-header inline code text is allowed"() {
parse('## my header about `thing` here {badge: "v3.4"}', Paths.get("sub-header.md"))
content.should == [[title: 'my header about thing here', id: 'my-header-about-thing-here', badge: 'v3.4', type: 'SubHeading', level: 2]]
content.should == [[title: 'my header about thing here', id: 'my-header-about-thing-here', additionalIds: [], badge: 'v3.4', type: 'SubHeading', level: 2]]
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package org.testingisdocumenting.znai.parser

import org.testingisdocumenting.znai.structure.AnchorIds
import org.testingisdocumenting.znai.structure.DocStructure
import org.testingisdocumenting.znai.structure.DocUrl
import org.testingisdocumenting.znai.structure.TableOfContents
Expand Down Expand Up @@ -68,17 +69,18 @@ class TestDocStructure implements DocStructure {
}

@Override
String generateUniqueAnchor(Path path, String localId) {
return uniqueAnchorIdGenerator.generateId(path, localId)
AnchorIds generateUniqueAnchors(Path path, String localId) {
return uniqueAnchorIdGenerator.generateIds(path, localId)
}

@Override
void registerGlobalAnchor(Path sourcePath, String anchorId) {
}

@Override
void registerLocalAnchor(Path path, String anchorId) {
registeredLocalLinks.add(anchorId)
void registerLocalAnchors(Path path, AnchorIds anchorIds) {
registeredLocalLinks.add(anchorIds.main())
anchorIds.additional().forEach(registeredLocalLinks::add)
}

@Override
Expand Down
Loading
Loading