blob: 00f7ec125df0e08d1a3fbbd5e361f868e020829b [file] [log] [blame]
// Copyright (C) 2018 The Android Open Source Project
//
// 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 com.google.gerrit.server.documentation;
import com.vladsch.flexmark.ast.Heading;
import com.vladsch.flexmark.ast.Node;
import com.vladsch.flexmark.ext.anchorlink.AnchorLink;
import com.vladsch.flexmark.ext.anchorlink.internal.AnchorLinkNodeRenderer;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.html.HtmlRenderer.HtmlRendererExtension;
import com.vladsch.flexmark.html.HtmlWriter;
import com.vladsch.flexmark.html.renderer.DelegatingNodeRendererFactory;
import com.vladsch.flexmark.html.renderer.NodeRenderer;
import com.vladsch.flexmark.html.renderer.NodeRendererContext;
import com.vladsch.flexmark.html.renderer.NodeRendererFactory;
import com.vladsch.flexmark.html.renderer.NodeRenderingHandler;
import com.vladsch.flexmark.profiles.pegdown.Extensions;
import com.vladsch.flexmark.profiles.pegdown.PegdownOptionsAdapter;
import com.vladsch.flexmark.util.options.DataHolder;
import com.vladsch.flexmark.util.options.MutableDataHolder;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class MarkdownFormatterHeader {
static class HeadingExtension implements HtmlRendererExtension {
@Override
public void rendererOptions(final MutableDataHolder options) {
// add any configuration settings to options you want to apply to everything, here
}
@Override
public void extend(final HtmlRenderer.Builder rendererBuilder, final String rendererType) {
rendererBuilder.nodeRendererFactory(new HeadingNodeRenderer.Factory());
}
static HeadingExtension create() {
return new HeadingExtension();
}
}
static class HeadingNodeRenderer implements NodeRenderer {
public HeadingNodeRenderer() {}
@Override
public Set<NodeRenderingHandler<?>> getNodeRenderingHandlers() {
return new HashSet<>(
Arrays.asList(
new NodeRenderingHandler<>(
AnchorLink.class,
(node, context, html) -> HeadingNodeRenderer.this.render(node, context)),
new NodeRenderingHandler<>(Heading.class, HeadingNodeRenderer.this::render)));
}
void render(final AnchorLink node, final NodeRendererContext context) {
Node parent = node.getParent();
if (parent instanceof Heading && ((Heading) parent).getLevel() == 1) {
// render without anchor link
context.renderChildren(node);
} else {
context.delegateRender();
}
}
static boolean haveExtension(int extensions, int flags) {
return (extensions & flags) != 0;
}
static boolean haveAllExtensions(int extensions, int flags) {
return (extensions & flags) == flags;
}
void render(final Heading node, final NodeRendererContext context, final HtmlWriter html) {
if (node.getLevel() == 1) {
// render without anchor link
final int extensions = context.getOptions().get(PegdownOptionsAdapter.PEGDOWN_EXTENSIONS);
if (context.getHtmlOptions().renderHeaderId
|| haveExtension(extensions, Extensions.ANCHORLINKS)
|| haveAllExtensions(
extensions, Extensions.EXTANCHORLINKS | Extensions.EXTANCHORLINKS_WRAP)) {
String id = context.getNodeId(node);
if (id != null) {
html.attr("id", id);
}
}
if (context.getHtmlOptions().sourcePositionParagraphLines) {
html.srcPos(node.getChars())
.withAttr()
.tagLine(
"h" + node.getLevel(),
() -> {
html.srcPos(node.getText()).withAttr().tag("span");
context.renderChildren(node);
html.tag("/span");
});
} else {
html.srcPos(node.getText())
.withAttr()
.tagLine("h" + node.getLevel(), () -> context.renderChildren(node));
}
} else {
context.delegateRender();
}
}
public static class Factory implements DelegatingNodeRendererFactory {
@Override
public NodeRenderer create(final DataHolder options) {
return new HeadingNodeRenderer();
}
@Override
public Set<Class<? extends NodeRendererFactory>> getDelegates() {
Set<Class<? extends NodeRendererFactory>> delegates = new HashSet<>();
delegates.add(AnchorLinkNodeRenderer.Factory.class);
return delegates;
}
}
}
}