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

add inline parsing for admonition titles to enable formatting #607

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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 @@ -3,8 +3,10 @@
import com.vladsch.flexmark.ast.Paragraph;
import com.vladsch.flexmark.ast.ParagraphContainer;
import com.vladsch.flexmark.util.ast.Block;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.util.sequence.BasedSequence;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;

Expand All @@ -14,39 +16,20 @@
public class AdmonitionBlock extends Block implements ParagraphContainer {
private BasedSequence openingMarker = BasedSequence.NULL;
private BasedSequence info = BasedSequence.NULL;
protected BasedSequence titleOpeningMarker = BasedSequence.NULL;
protected BasedSequence title = BasedSequence.NULL;
protected BasedSequence titleClosingMarker = BasedSequence.NULL;

@NotNull
@Override
public BasedSequence[] getSegments() {
return new BasedSequence[] {
openingMarker,
info,
titleOpeningMarker,
title,
titleClosingMarker,
};
}

@NotNull
@Override
public BasedSequence[] getSegmentsForChars() {
return new BasedSequence[] {
openingMarker,
info,
titleOpeningMarker,
title,
titleClosingMarker,
info
};
}

@Override
public void getAstExtra(@NotNull StringBuilder out) {
segmentSpanChars(out, openingMarker, "open");
segmentSpanChars(out, info, "info");
delimitedSegmentSpanChars(out, titleOpeningMarker, title, titleClosingMarker, "title");
}

public AdmonitionBlock() {
Expand Down Expand Up @@ -78,45 +61,32 @@ public BasedSequence getInfo() {
return info;
}

public BasedSequence getTitle() {
return title;
}

public BasedSequence getTitleOpeningMarker() {
return titleOpeningMarker;
@Nullable
public AdmonitionTitle getTitleNode() {
Node child = getFirstChild();
if (child instanceof AdmonitionTitle)
return (AdmonitionTitle) child;
return null;
}

public void setTitleOpeningMarker(BasedSequence titleOpeningMarker) {
this.titleOpeningMarker = titleOpeningMarker;
public BasedSequence getTitle() {
AdmonitionTitle title = getTitleNode();
return title == null ? BasedSequence.NULL : title.getText();
}

public void setTitle(BasedSequence title) {
this.title = title;
public BasedSequence getTitleOpeningMarker() {
AdmonitionTitle title = getTitleNode();
return title == null ? BasedSequence.NULL : title.getOpeningMarker();
}

public BasedSequence getTitleClosingMarker() {
return titleClosingMarker;
}

public void setTitleClosingMarker(BasedSequence titleClosingMarker) {
this.titleClosingMarker = titleClosingMarker;
AdmonitionTitle title = getTitleNode();
return title == null ? BasedSequence.NULL : title.getClosingMarker();
}

public BasedSequence getTitleChars() {
return spanningChars(titleOpeningMarker, title, titleClosingMarker);
}

public void setTitleChars(BasedSequence titleChars) {
if (titleChars != null && titleChars != BasedSequence.NULL) {
int titleCharsLength = titleChars.length();
titleOpeningMarker = titleChars.subSequence(0, 1);
title = titleChars.subSequence(1, titleCharsLength - 1);
titleClosingMarker = titleChars.subSequence(titleCharsLength - 1, titleCharsLength);
} else {
titleOpeningMarker = BasedSequence.NULL;
title = BasedSequence.NULL;
titleClosingMarker = BasedSequence.NULL;
}
AdmonitionTitle title = getTitleNode();
return title == null ? BasedSequence.NULL : title.getChars();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.vladsch.flexmark.ext.admonition;

import com.vladsch.flexmark.ast.DelimitedNodeImpl;

public class AdmonitionTitle extends DelimitedNodeImpl {
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.vladsch.flexmark.ast.ListItem;
import com.vladsch.flexmark.ast.util.Parsing;
import com.vladsch.flexmark.ext.admonition.AdmonitionBlock;
import com.vladsch.flexmark.ext.admonition.AdmonitionTitle;
import com.vladsch.flexmark.parser.InlineParser;
import com.vladsch.flexmark.parser.block.*;
import com.vladsch.flexmark.util.ast.Block;
import com.vladsch.flexmark.util.data.DataHolder;
Expand Down Expand Up @@ -71,6 +73,15 @@ public void closeBlock(ParserState state) {
block.setCharsFromContent();
}

@Override
public void parseInlines(InlineParser inlineParser) {
super.parseInlines(inlineParser);
AdmonitionTitle title = block.getTitleNode();
if (title != null) {
inlineParser.parse(title.getText(), title);
}
}

public static class Factory implements CustomBlockParserFactory {
@Nullable
@Override
Expand Down Expand Up @@ -185,7 +196,14 @@ public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockPar
AdmonitionBlockParser admonitionBlockParser = new AdmonitionBlockParser(options, contentOffset);
admonitionBlockParser.block.setOpeningMarker(openingMarker);
admonitionBlockParser.block.setInfo(info);
admonitionBlockParser.block.setTitleChars(titleChars);
if (titleChars.isNotNull()) {
AdmonitionTitle title = new AdmonitionTitle();
title.setOpeningMarker(titleChars.subSequence(0, 1));
title.setText(titleChars.subSequence(1, titleChars.length() - 1));
title.setClosingMarker(titleChars.subSequence(titleChars.length() - 1));
title.setCharsFromSegments();
admonitionBlockParser.block.prependChild(title);
}

return BlockStart.of(admonitionBlockParser)
.atIndex(line.length());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package com.vladsch.flexmark.ext.admonition.internal;

import com.vladsch.flexmark.ext.admonition.AdmonitionBlock;
import com.vladsch.flexmark.ext.admonition.AdmonitionTitle;
import com.vladsch.flexmark.formatter.*;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.util.data.DataHolder;
import com.vladsch.flexmark.util.sequence.RepeatedSequence;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

public class AdmonitionNodeFormatter implements NodeFormatter {
Expand All @@ -27,21 +27,35 @@ public Set<Class<?>> getNodeClasses() {
@Nullable
@Override
public Set<NodeFormattingHandler<?>> getNodeFormattingHandlers() {
return new HashSet<>(Collections.singletonList(
new NodeFormattingHandler<>(AdmonitionBlock.class, AdmonitionNodeFormatter.this::render)
));
return Set.of(
new NodeFormattingHandler<>(AdmonitionBlock.class, AdmonitionNodeFormatter.this::render),
new NodeFormattingHandler<>(AdmonitionTitle.class, AdmonitionNodeFormatter.this::render)
);
}

private void render(AdmonitionTitle node, NodeFormatterContext context, MarkdownWriter markdown) {
markdown.append(node.getOpeningMarker());
context.renderChildren(node);
markdown.append(node.getClosingMarker());
}

private void render(AdmonitionBlock node, NodeFormatterContext context, MarkdownWriter markdown) {
markdown.blankLine();
markdown.append(node.getOpeningMarker()).append(' ');
markdown.appendNonTranslating(node.getInfo());
if (node.getTitle().isNotNull()) {
markdown.append(' ').append('"').appendTranslating(node.getTitle()).append('"');
AdmonitionTitle title = node.getTitleNode();
if (title != null) {
markdown.append(' ');
context.render(title);
}
markdown.line();
markdown.pushPrefix().addPrefix(RepeatedSequence.repeatOf(" ", options.contentIndent).toString());
context.renderChildren(node);
Node next = title == null ? node.getFirstChild() : title.getNext();
while (next != null) {
Node child = next;
next = child.getNext();
context.render(child);
}
markdown.blankLine();
markdown.popPrefix();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.vladsch.flexmark.ext.admonition.internal;

import com.vladsch.flexmark.ext.admonition.AdmonitionBlock;
import com.vladsch.flexmark.ext.admonition.AdmonitionTitle;
import com.vladsch.flexmark.html.HtmlWriter;
import com.vladsch.flexmark.html.renderer.*;
import com.vladsch.flexmark.util.ast.Document;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.util.data.DataHolder;
import com.vladsch.flexmark.util.html.Attribute;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -31,6 +33,7 @@ public AdmonitionNodeRenderer(DataHolder options) {
public Set<NodeRenderingHandler<?>> getNodeRenderingHandlers() {
Set<NodeRenderingHandler<?>> set = new HashSet<>();
set.add(new NodeRenderingHandler<>(AdmonitionBlock.class, this::render));
set.add(new NodeRenderingHandler<>(AdmonitionTitle.class, this::render));
return set;
}

Expand Down Expand Up @@ -71,58 +74,65 @@ public void renderDocument(@NotNull NodeRendererContext context, @NotNull HtmlWr
}
}

private static void outputTitle(String type, HtmlWriter html, Runnable output) {
html.attr(Attribute.CLASS_ATTR, "adm-heading").withAttr(ADMONITION_HEADING_PART).tag("div").line();
html.attr(Attribute.CLASS_ATTR, "adm-icon").withAttr(ADMONITION_ICON_PART).tag("svg").raw("<use xlink:href=\"#adm-").raw(type).raw("\" />").closeTag("svg");
html.withAttr(ADMONITION_TITLE_PART).tag("span", output);
html.closeTag("div").line();
}

private void render(AdmonitionTitle node, NodeRendererContext context, HtmlWriter html) {
if (node.getText().isEmpty()) return;
String type = options.unresolvedQualifier;
if (node.getParent() instanceof AdmonitionBlock) {
type = ((AdmonitionBlock) node.getParent()).getInfo().toString().toLowerCase();
type = this.options.qualifierTypeMap.getOrDefault(type, options.unresolvedQualifier);
}
outputTitle(type, html, () -> context.renderChildren(node));
}

private void render(AdmonitionBlock node, NodeRendererContext context, HtmlWriter html) {
String info = node.getInfo().toString().toLowerCase();
String type = this.options.qualifierTypeMap.get(info);
if (type == null) {
type = options.unresolvedQualifier;
}

String title;
if (node.getTitle().isNull()) {
title = this.options.qualifierTitleMap.get(info);
if (title == null) {
title = info.substring(0, 1).toUpperCase() + info.substring(1);
}
} else {
title = node.getTitle().toString();
}

String openClose;
if (node.getOpeningMarker().equals("???")) openClose = " adm-collapsed";
else if (node.getOpeningMarker().equals("???+")) openClose = "adm-open";
else openClose = null;

if (title.isEmpty()) {
html.srcPos(node.getChars()).withAttr()
.attr(Attribute.CLASS_ATTR, "adm-block")
.attr(Attribute.CLASS_ATTR, "adm-" + type)
.tag("div", false).line();

html.attr(Attribute.CLASS_ATTR, "adm-body").withAttr(ADMONITION_BODY_PART).tag("div").indent().line();

context.renderChildren(node);

html.unIndent().closeTag("div").line();
html.closeTag("div").line();
} else {
html.srcPos(node.getChars())
.attr(Attribute.CLASS_ATTR, "adm-block").attr(Attribute.CLASS_ATTR, "adm-" + type);

if (openClose != null) {
html.attr(Attribute.CLASS_ATTR, openClose).attr(Attribute.CLASS_ATTR, "adm-" + type);
}

html.withAttr().tag("div", false).line();
html.attr(Attribute.CLASS_ATTR, "adm-heading").withAttr(ADMONITION_HEADING_PART).tag("div").line();
html.attr(Attribute.CLASS_ATTR, "adm-icon").withAttr(ADMONITION_ICON_PART).tag("svg").raw("<use xlink:href=\"#adm-").raw(type).raw("\" />").closeTag("svg");
html.withAttr(ADMONITION_TITLE_PART).tag("span").text(title).closeTag("span").line();
html.closeTag("div").line();

html.attr(Attribute.CLASS_ATTR, "adm-body").withAttr(ADMONITION_BODY_PART).withCondIndent().tagLine("div", () -> context.renderChildren(node));
html.srcPos(node.getChars()).withAttr()
.attr(Attribute.CLASS_ATTR, "adm-block")
.attr(Attribute.CLASS_ATTR, "adm-" + type);
if (openClose != null) {
html.attr(Attribute.CLASS_ATTR, openClose);
}
html.tag("div", false).line();

AdmonitionTitle title = node.getTitleNode();

if (title == null) { // standard title
outputTitle(type, html, () -> {
String automaticTitle = this.options.qualifierTitleMap.get(info);
if (automaticTitle == null)
automaticTitle = info.substring(0, 1).toUpperCase() + info.substring(1);
html.text(automaticTitle);
});
} else if (!title.getText().isEmpty()) {
outputTitle(type, html, () -> context.renderChildren(title));
}

html.closeTag("div").line();
html.attr(Attribute.CLASS_ATTR, "adm-body").withAttr(ADMONITION_BODY_PART).tag("div").indent().line();
Node next = title == null ? node.getFirstChild() : title.getNext();
while (next != null) {
Node child = next;
next = child.getNext();
context.render(child);
}
html.unIndent().closeTag("div").line();
html.closeTag("div").line();
}

public static class Factory implements NodeRendererFactory {
Expand Down