Skip to content

Commit 0c6842e

Browse files
committed
fix sirthias#211, github task list item parsing bug
fix sirthias#212, autolinks including emphasis and strikethrough markers
1 parent dcd5b62 commit 0c6842e

25 files changed

+755
-138
lines changed

CHANGELOG

+31
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,34 @@
1+
Version after 1.6.0
2+
--------------------------
3+
4+
### The following are added to allow easy customization of ToHtmlSerializer without rewriting it.
5+
6+
NOTE: these changes are backwards compatible if you don't override the new functions or you can override them to customize the HTML output
7+
or leave it as is to get the old behaviour.
8+
9+
- Added `preview(Node node, String tag, Attributes attributes, boolean tocGenerationVisit)` to `ToHtmlSerializer`, called before every node that has a tag so that derived classes can modify attributes output in the HTML, returned attributes will be output in the order they were added to Attributes. Re-use the passed in parameter or create a new one.
10+
11+
- Added `Printer.preview(node, String tag, attributes, boolean tocGenerationVisit)` to Printer so that the serializer's preview() can be accessed by plugins and verbatim serializers. Together with Attributes methods you can change classes, add attributes based on tags and node parameters without rewriting the whole serializer.
12+
13+
Note: `tocGenerationVisit` will be true if the output is for TOC rendering. You will get the same nodes once with `tocGenerationVisit` true and once false for nodes that are part of TOC headers. In all cases the passed in attributes contain the default attributes as they are now rendered by ToHtmlSerializer. If you return them unmolested, you will get output as it is now.
14+
15+
- Added `ToHtmlSerializer.printTaskListItemMarker(Printer printer, TaskListNode node, boolean isParaWrapped)` that prints the task list item marker, default prints an input checkbox, `isParaWrapped` is true when the contents of `li` tag are wrapped in `<p></p>` just in case it makes a difference to what you want to output.
16+
17+
- Changed `DefaultVerbatimSerializer` to also print attributes returned by the call to `preview()` before closing the <code tag. Passed in attributes will contain the class of the node.getType() value.
18+
19+
- Added `String computeHeaderId(HeaderNode node, AnchorLinkNode anchorLinkNode, String headerText)` to `ToHtmlSerializer` called before generating any HTML in the top most RootNode processing for all headers, depth first traversal. Returning an empty string will output Header without id attribute. Returning any other value will output header with that id and if there is an anchor link it will also change its reference and name attribute to match the returned value. Use this to override how anchor link references are generated. Additional benefit if you override this function is that you will always know what id to expect for the header and can generate the right reference.
20+
21+
This way you can create your own logic for generating the reference link ids to match your requirements by overriding only a single member of ToHtmlSerializer. Similarly, to change the way task lists are generated it is also a single override.
22+
23+
In addition, another member override: `preview(...)` that passes the node, the node's tag and current attribute set will allow you to add/remove/append class or any other attribute. By default the class that implements `Attributes` can handle multiple calls to add(name, value) with the same name. It will append value as a space delimited list. So out of the box you can just add("class", whateverClass) to attributes for nodes of your choice to add extra classes.
24+
25+
This function is also called for VerbatimNode from the DefaultVerbatimSerializer so you can add/remove/change the class assigned to `<code>`.
26+
27+
- Fixed autolinks included trailing emphasis characters so `**[email protected]**` would parse as ** '[email protected]**' instead of ** '[email protected]' ** bold email, same for links. Now emphasis (and strikethrough if selected in options) markers are excluded from the trailing autolink text.
28+
29+
- Fixed task list items would create an erroneous AST if the space after the `[ ]` or `[x]` was left out which should parse as not a task list item but a normal bullet item.
30+
31+
132
Version 1.6.0 (2015-09-18)
233
--------------------------
334
- Fixed collision between ANCHORLINKS and STRIKETHROUGH, WIKILINKS extensions (#161)

build.sbt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name := "pegdown"
22

3-
version := "1.6.6"
3+
version := "1.6.7"
44

55
homepage := Some(new URL("http://pegdown.org"))
66

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package org.pegdown;
2+
3+
import java.util.ArrayList;
4+
import java.util.HashMap;
5+
import java.util.List;
6+
7+
public class Attributes {
8+
public final HashMap<String, String> attrMap = new HashMap<String, String>();
9+
public final ArrayList<String> attrOrder = new ArrayList<String>();
10+
11+
public Attributes() {
12+
}
13+
14+
public Attributes(String name, String value) {
15+
add(name, value);
16+
}
17+
18+
public Attributes(List<LinkRenderer.Attribute> attributes) {
19+
addAll(attributes);
20+
}
21+
22+
public Attributes add(String attribute, String value) {
23+
addDelimitedValue(attribute, " ", value);
24+
return this;
25+
}
26+
27+
public Attributes addAll(List<LinkRenderer.Attribute> attributes) {
28+
for (LinkRenderer.Attribute attribute : attributes) {
29+
add(attribute.name, attribute.value);
30+
}
31+
return this;
32+
}
33+
34+
public Attributes addDelimitedValue(String attribute, String delim, String value) {
35+
if (attrMap.containsKey(attribute)) {
36+
attrMap.put(attribute, attrMap.get(attribute) + delim + value);
37+
} else {
38+
attrMap.put(attribute, value);
39+
attrOrder.add(attribute);
40+
}
41+
return this;
42+
}
43+
44+
public Attributes replace(String attribute, String value) {
45+
if (attrMap.containsKey(attribute)) {
46+
attrMap.put(attribute, value);
47+
} else {
48+
attrMap.put(attribute, value);
49+
attrOrder.add(attribute);
50+
}
51+
return this;
52+
}
53+
54+
public Attributes removeDelimitedValue(String name, String delim, String value, boolean skipEmpties) {
55+
if (attrMap.containsKey(name)) {
56+
String[] classList = attrMap.get(name).split(delim);
57+
String classAttr = "";
58+
for (String classValue : classList) {
59+
if ((!skipEmpties || !classValue.isEmpty()) && !classValue.equals(value)) {
60+
classAttr += delim + classValue;
61+
}
62+
}
63+
64+
// vsch: we always put it back so as not to change the order of when it was defined, otherwise tests may fail
65+
attrMap.put(name, classAttr);
66+
}
67+
return this;
68+
}
69+
70+
// vsch: use these to manipulate classes in preview()
71+
public Attributes removeClass(String value) {
72+
return removeDelimitedValue("class", " ", value, true);
73+
}
74+
75+
public Attributes addClass(String value) {
76+
return add("class", value);
77+
}
78+
79+
public Attributes replaceClass(String value) {
80+
return replace("class", value);
81+
}
82+
83+
public boolean hasClass(String value) {
84+
String classAttr = " " + get("class", "") + " ";
85+
return classAttr.contains(" " + value + " ");
86+
}
87+
88+
public boolean contains(String attribute) {
89+
return attrMap.containsKey(attribute);
90+
}
91+
92+
public String get(String attribute, String valueIfMissing) {
93+
if (!attrMap.containsKey(attribute)) return valueIfMissing;
94+
return attrMap.get(attribute);
95+
}
96+
97+
// vsch: NOTE: only lowercase "class" attribute is trimmed and skipped if it is empty, the rest are output as is, if you want something else take care of it in preview()
98+
void print(Printer printer) {
99+
for (String name : attrOrder) {
100+
assert attrMap.containsKey(name) : "Unexpected, key: " + name + " is not in attrMap, must have been manipulated outside of Attribute class";
101+
if (name.equals("class")) {
102+
String classAttr = attrMap.get(name).trim();
103+
if (!classAttr.isEmpty()) {
104+
printer.print(' ').print(name).print('=').print('"').print(attrMap.get(name).replace("\\", "\\\\").replace("\"", "\\\"")).print('"');
105+
}
106+
} else {
107+
printer.print(' ').print(name).print('=').print('"').print(attrMap.get(name).replace("\\", "\\\\").replace("\"", "\\\"")).print('"');
108+
}
109+
}
110+
}
111+
}

src/main/java/org/pegdown/DefaultVerbatimSerializer.java

+5-7
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ public class DefaultVerbatimSerializer implements VerbatimSerializer {
88

99
@Override
1010
public void serialize(final VerbatimNode node, final Printer printer) {
11-
printer.println().print("<pre><code");
11+
Attributes attributes = new Attributes();
12+
1213
if (!StringUtils.isEmpty(node.getType())) {
13-
printAttribute(printer, "class", node.getType());
14+
attributes.add("class", node.getType());
1415
}
15-
printer.print(">");
16+
17+
printer.println().print("<pre><code").print(printer.preview(node, "code", attributes, false)).print('>');
1618
String text = node.getText();
1719
// print HTML breaks for all initial newlines
1820
while (text.charAt(0) == '\n') {
@@ -23,8 +25,4 @@ public void serialize(final VerbatimNode node, final Printer printer) {
2325
printer.print("</code></pre>");
2426

2527
}
26-
27-
private void printAttribute(final Printer printer, final String name, final String value) {
28-
printer.print(' ').print(name).print('=').print('"').print(value).print('"');
29-
}
3028
}

src/main/java/org/pegdown/Extensions.java

+15
Original file line numberDiff line numberDiff line change
@@ -182,4 +182,19 @@ public interface Extensions {
182182

183183
static final int ALL_OPTIONALS = (ATXHEADERSPACE | RELAXEDHRULES | TASKLISTITEMS | EXTANCHORLINKS | FOOTNOTES);
184184
static final int ALL_WITH_OPTIONALS = ALL | (ATXHEADERSPACE | RELAXEDHRULES | TASKLISTITEMS | FOOTNOTES);
185+
186+
/**
187+
* These are GitHub main repo document processing compatibility flags
188+
*/
189+
static final int GITHUB_DOCUMENT_COMPATIBLE = (FENCED_CODE_BLOCKS | TABLES | AUTOLINKS | ANCHORLINKS | TASKLISTITEMS | STRIKETHROUGH | ATXHEADERSPACE | RELAXEDHRULES);
190+
191+
/**
192+
* These are GitHub wiki page processing compatibility flags
193+
*/
194+
static final int GITHUB_WIKI_COMPATIBLE = (GITHUB_DOCUMENT_COMPATIBLE | WIKILINKS);
195+
196+
/**
197+
* These are GitHub comment (issues, pull requests and comments) processing compatibility flags
198+
*/
199+
static final int GITHUB_COMMENT_COMPATIBLE = (GITHUB_DOCUMENT_COMPATIBLE | HARDWRAPS);
185200
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package org.pegdown;
2+
3+
import org.pegdown.ast.AnchorLinkNode;
4+
import org.pegdown.ast.HeaderNode;
5+
6+
public interface HeaderIdComputer {
7+
String computeHeaderId(HeaderNode node, AnchorLinkNode anchorLinkNode, String headerText);
8+
}

0 commit comments

Comments
 (0)