Skip to content

Commit

Permalink
build: replace remark with flexmark
Browse files Browse the repository at this point in the history
Signed-off-by: Fred Bricon <[email protected]>
  • Loading branch information
fbricon committed Dec 20, 2023
1 parent 0d3437e commit ae99a24
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 86 deletions.
8 changes: 5 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,11 @@ sourceSets {

dependencies {
implementation("org.zeroturnaround:zt-zip:1.14")
implementation("com.kotcrab.remark:remark:1.2.0") //FIXME use lsp4ij's flexmark instead
implementation("org.jsoup:jsoup:1.14.2") //FIXME use lsp4ij's jsoup instead
implementation("com.google.code.gson:gson:2.10.1")
implementation("org.jsoup:jsoup:1.17.1")
implementation("com.vladsch.flexmark:flexmark-html2md-converter:0.64.8") {
exclude(group="com.vladsch.flexmark", module= "flexmark-jira-converter")
}
implementation("com.google.code.gson:gson:2.10.1") //Need to ensure we don't get telemetry's old gson version
implementation("io.quarkus:quarkus-core:$quarkusVersion") {
isTransitive = false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public String getJavadoc(PsiMethod method, DocumentFormat documentFormat) {

@Override
public String getJavadoc(PsiMember method, com.redhat.qute.commons.DocumentFormat documentFormat) {
boolean markdown = DocumentFormat.Markdown.equals(documentFormat);
boolean markdown = DocumentFormat.Markdown.name().toLowerCase().equals(documentFormat.name().toLowerCase());
Reader reader = markdown ? JavadocContentAccess.getMarkdownContentReader(method)
: JavadocContentAccess.getPlainTextContentReader(method);
return reader != null ? toString(reader) : null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@


import java.io.Reader;
import java.lang.reflect.Field;

import org.jsoup.safety.Cleaner;
import org.jsoup.safety.Safelist;
import com.overzealous.remark.Options;
import com.overzealous.remark.Options.Tables;
import com.overzealous.remark.Remark;
import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter;
import com.vladsch.flexmark.parser.PegdownExtensions;
import com.vladsch.flexmark.util.data.DataKey;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -30,34 +30,27 @@
public class JavaDoc2MarkdownConverter extends AbstractJavaDocConverter {
private static final Logger LOGGER = LoggerFactory.getLogger(JavaDoc2MarkdownConverter.class);

private static Remark remark;

static {
Options options = new Options();
options.tables = Tables.MULTI_MARKDOWN;
options.hardwraps = true;
options.inlineLinks = true;
options.autoLinks = true;
options.reverseHtmlSmartPunctuation = true;
remark = new Remark(options);
//Stop remark from stripping file and jdt protocols in an href
try {
Field cleanerField = Remark.class.getDeclaredField("cleaner");
cleanerField.setAccessible(true);

Cleaner c = (Cleaner) cleanerField.get(remark);

Field safelistField = Cleaner.class.getDeclaredField("safelist");
safelistField.setAccessible(true);

Safelist s = (Safelist) safelistField.get(c);

s.addProtocols("a", "href", "file", "jdt");
s.addProtocols("img", "src", "file");
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
LOGGER.error("Unable to modify jsoup to include file and jdt protocols", e);
}
}
final static public DataKey<Integer> HTML_EXTENSIONS = new DataKey<>("HTML_EXTENSIONS", 0
//| Extensions.ABBREVIATIONS
//| Extensions.EXTANCHORLINKS /*| Extensions.EXTANCHORLINKS_WRAP*/
//| Extensions.AUTOLINKS
//| Extensions.DEFINITIONS
| PegdownExtensions.FENCED_CODE_BLOCKS
//| Extensions.FORCELISTITEMPARA
//| Extensions.HARDWRAPS
//| Extensions.ATXHEADERSPACE
//| Extensions.QUOTES
//| Extensions.SMARTS
//| Extensions.RELAXEDHRULES
//| Extensions.STRIKETHROUGH
//| Extensions.SUPPRESS_HTML_BLOCKS
//| Extensions.SUPPRESS_INLINE_HTML
//| Extensions.TABLES
//| Extensions.TASKLISTITEMS
//| Extensions.WIKILINKS
//| Extensions.TRACE_PARSER
);
private static final FlexmarkHtmlConverter CONVERTER = FlexmarkHtmlConverter.builder().build();

public JavaDoc2MarkdownConverter(Reader reader) {
super(reader);
Expand All @@ -68,7 +61,52 @@ public JavaDoc2MarkdownConverter(String javadoc) {
}

@Override
String convert(String rawHtml) {
return remark.convert(rawHtml);
public String convert(String html) {
Document document = Jsoup.parse(html);
//Add missing table headers if necessary, else most Markdown renderers will crap out
document.select("table").forEach(JavaDoc2MarkdownConverter::addMissingTableHeaders);

String markdown = CONVERTER.convert(document);
if (markdown.endsWith("\n")) {// FlexmarkHtmlConverter keeps adding an extra line
markdown = markdown.substring(0, markdown.length() - 1);
}
return markdown;
}

/**
* Adds a new row header if the given table doesn't have any.
*
* @param table
* the HTML table to check for a header
*/
private static void addMissingTableHeaders(Element table) {
int numCols = 0;
for (Element child : table.children()) {
if ("thead".equals(child.nodeName())) {
// Table already has a header, nothing else to do
return;
}
if ("tbody".equals(child.nodeName())) {
Elements rows = child.getElementsByTag("tr");
if (!rows.isEmpty()) {
for (Element row : rows) {
int colSize = row.getElementsByTag("td").size();
//Keep the biggest column size
if (colSize > numCols) {
numCols = colSize;
}
}
}
}
}
if (numCols > 0) {
//Create a new header row based on the number of columns already found
Element newHeader = new Element("tr");
for (int i = 0; i < numCols; i++) {
newHeader.appendChild(new Element("th"));
}
//Insert header row in 1st position in the table
table.insertChildren(0, newHeader);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public void testgetFieldJavadoc() throws Exception {
DocumentFormat.Markdown);

String actual = QuteSupportForTemplate.getInstance().getJavadoc(params, getJDTUtils(), new EmptyProgressIndicator());
String expected = " The name of the item ";
String expected = "The name of the item";
assertEquals(expected, actual);
}

Expand All @@ -57,9 +57,11 @@ public void testgetMethodJavadoc() throws Exception {
DocumentFormat.Markdown);

String actual = QuteSupportForTemplate.getInstance().getJavadoc(params, getJDTUtils(), new EmptyProgressIndicator());
String expected = " Returns the derived items. \n" +
" * Returns:\n" +
" - the derived items";
String expected = """
Returns the derived items.
* **Returns:**
* the derived items""";
assertEquals(expected, actual);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,18 @@ public void testKubernetes() throws Exception {

// io.dekorate.kubernetes.annotation.KubernetesApplication
p(null, "kubernetes.name", "java.lang.String",
"The name of the application. This value will be used for naming Kubernetes resources like: - Deployment - Service and so on ... If no value is specified it will attempt to determine the name using the following rules: If its a maven/gradle project use the artifact id. Else if its a bazel project use the name. Else if the system property app.name is present it will be used. Else find the project root folder and use its name (root folder detection is done by moving to the parent folder until .git is found)."
+ System.lineSeparator() + "" + System.lineSeparator() + " * **Returns:**" + System.lineSeparator()
+ " " + System.lineSeparator() + " * The specified application name.",
"""
The name of the application. This value will be used for naming Kubernetes resources like: - Deployment - Service and so on ... If no value is specified it will attempt to determine the name using the following rules: If its a maven/gradle project use the artifact id. Else if its a bazel project use the name. Else if the system property app.name is present it will be used. Else find the project root folder and use its name (root folder detection is done by moving to the parent folder until .git is found).
* **Returns:**
* The specified application name.""",
true, "io.dekorate.kubernetes.annotation.KubernetesApplication", null, "name()Ljava/lang/String;", 0, null),

p(null, "kubernetes.readiness-probe.initial-delay-seconds", "int", "The amount of time to wait in seconds before starting to probe." + //
System.lineSeparator() + "" + System.lineSeparator() + //
" * **Returns:**" + System.lineSeparator() + //
" " + System.lineSeparator() + " * The initial delay.",
p(null, "kubernetes.readiness-probe.initial-delay-seconds", "int", """
The amount of time to wait in seconds before starting to probe.
* **Returns:**
* The initial delay.""",
true, "io.dekorate.kubernetes.annotation.Probe", null, "initialDelaySeconds()I", 0, "0"),

p(null, "kubernetes.annotations[*].key", "java.lang.String", null, true,
Expand All @@ -56,10 +59,10 @@ public void testKubernetes() throws Exception {
"protocol()Lio/dekorate/kubernetes/annotation/Protocol;", 0, "TCP"),

p(null, "kubernetes.deployment.target", "java.lang.String",
"To enable the generation of OpenShift resources, you need to include OpenShift in the target platforms: `kubernetes.deployment.target=openshift`."
+ System.lineSeparator() + ""
+ "If you need to generate resources for both platforms (vanilla Kubernetes and OpenShift), then you need to include both (coma separated)."
+ System.lineSeparator() + "" + "`kubernetes.deployment.target=kubernetes, openshift`.",
"""
To enable the generation of OpenShift resources, you need to include OpenShift in the target platforms: `kubernetes.deployment.target=openshift`.
If you need to generate resources for both platforms (vanilla Kubernetes and OpenShift), then you need to include both (coma separated).
`kubernetes.deployment.target=kubernetes, openshift`.""",
true, null, null, null, 0, "kubernetes"),
p(null, "kubernetes.registry", "java.lang.String", "Specify the docker registry.", true, null, null, null, 0,
null));
Expand All @@ -82,18 +85,20 @@ public void testOpenshift() throws Exception {
MicroProfileProjectInfo info = PropertiesManager.getInstance().getMicroProfileProjectInfo(module, MicroProfilePropertiesScope.SOURCES_AND_DEPENDENCIES, ClasspathKind.SRC, PsiUtilsLSImpl.getInstance(myProject), DocumentFormat.Markdown, new EmptyProgressIndicator());

assertProperties(info,

// io.dekorate.openshift.annotation.OpenshiftApplication
p(null, "openshift.name", "java.lang.String",
"The name of the application. This value will be used for naming Kubernetes resources like: - Deployment - Service and so on ... If no value is specified it will attempt to determine the name using the following rules: If its a maven/gradle project use the artifact id. Else if its a bazel project use the name. Else if the system property app.name is present it will be used. Else find the project root folder and use its name (root folder detection is done by moving to the parent folder until .git is found)."
+ System.lineSeparator() + "" + System.lineSeparator() + " * **Returns:**" + System.lineSeparator()
+ " " + System.lineSeparator() + " * The specified application name.",
"""
The name of the application. This value will be used for naming Kubernetes resources like: - Deployment - Service and so on ... If no value is specified it will attempt to determine the name using the following rules: If its a maven/gradle project use the artifact id. Else if its a bazel project use the name. Else if the system property app.name is present it will be used. Else find the project root folder and use its name (root folder detection is done by moving to the parent folder until .git is found).
* **Returns:**
* The specified application name.""",
true, "io.dekorate.openshift.annotation.OpenshiftApplication", null, "name()Ljava/lang/String;", 0, null),

p(null, "openshift.readiness-probe.initial-delay-seconds", "int", "The amount of time to wait in seconds before starting to probe." + //
System.lineSeparator() + "" + System.lineSeparator() + //
" * **Returns:**" + System.lineSeparator() + //
" " + System.lineSeparator() + " * The initial delay.", true,
p(null, "openshift.readiness-probe.initial-delay-seconds", "int", """
The amount of time to wait in seconds before starting to probe.
* **Returns:**
* The initial delay.""", true,
"io.dekorate.kubernetes.annotation.Probe", null, "initialDelaySeconds()I", 0, "0"),

p(null, "openshift.annotations[*].key", "java.lang.String", null, true,
Expand All @@ -109,7 +114,6 @@ public void testOpenshift() throws Exception {
assertPropertiesDuplicate(info);

assertHints(info,

h("io.dekorate.kubernetes.annotation.Protocol", null, true, "io.dekorate.kubernetes.annotation.Protocol",
vh("TCP", null, null), //
vh("UDP", null, null)));
Expand All @@ -124,28 +128,27 @@ public void testS2i() throws Exception {
MicroProfileProjectInfo info = PropertiesManager.getInstance().getMicroProfileProjectInfo(module, MicroProfilePropertiesScope.SOURCES_AND_DEPENDENCIES, ClasspathKind.SRC, PsiUtilsLSImpl.getInstance(myProject), DocumentFormat.Markdown, new EmptyProgressIndicator());

assertProperties(info,

// io.dekorate.s2i.annotation.S2iBuild
p(null, "s2i.docker-file", "java.lang.String", "The relative path of the Dockerfile, from the module root." + //
System.lineSeparator() + "" + System.lineSeparator() + //
" * **Returns:**" + //
System.lineSeparator() + //
" " + //
System.lineSeparator() + //
" * The relative path.", true, "io.dekorate.s2i.annotation.S2iBuild", null,
p(null, "s2i.docker-file", "java.lang.String", """
The relative path of the Dockerfile, from the module root.
* **Returns:**
* The relative path.""", true, "io.dekorate.s2i.annotation.S2iBuild", null,
"dockerFile()Ljava/lang/String;", 0, "Dockerfile"),

p(null, "s2i.group", "java.lang.String",
"The group of the application. This value will be use as image user."
+ System.lineSeparator() + "" + System.lineSeparator() + " * **Returns:**" + System.lineSeparator()
+ " " + System.lineSeparator() + " * The specified group name.",
"""
The group of the application. This value will be use as image user.
* **Returns:**
* The specified group name.""",
true, "io.dekorate.s2i.annotation.S2iBuild", null, "group()Ljava/lang/String;", 0,
null),

p("quarkus-container-image-s2i", "quarkus.s2i.jar-directory", "java.lang.String",
"The directory where the jar is added during the assemble phase." + //
"\n" + //
"This is dependent on the S2I image and should be supplied if a non default image is used.",
"""
The directory where the jar is added during the assemble phase.
This is dependent on the S2I image and should be supplied if a non default image is used.""",
true, "io.quarkus.container.image.s2i.deployment.S2iConfig", "jarDirectory", null, 1,
"/deployments/"));

Expand All @@ -159,17 +162,15 @@ public void testS2i() throws Exception {
public void testDocker() throws Exception {
Module module = loadMavenProject(QuarkusMavenProjectName.kubernetes, true);
MicroProfileProjectInfo info = PropertiesManager.getInstance().getMicroProfileProjectInfo(module, MicroProfilePropertiesScope.SOURCES_AND_DEPENDENCIES, ClasspathKind.SRC, PsiUtilsLSImpl.getInstance(myProject), DocumentFormat.Markdown, new EmptyProgressIndicator());
String description = """
The relative path of the Dockerfile, from the module root.
* **Returns:**
* The relative path.""";
assertProperties(info,

// io.dekorate.docker.annotation.DockerBuild
p(null, "docker.docker-file", "java.lang.String", "The relative path of the Dockerfile, from the module root." + //
System.lineSeparator() + "" + System.lineSeparator() + //
" * **Returns:**" + //
System.lineSeparator() + //
" " + //
System.lineSeparator() + //
" * The relative path.", true, "io.dekorate.docker.annotation.DockerBuild", null,
p(null, "docker.docker-file", "java.lang.String", description, true, "io.dekorate.docker.annotation.DockerBuild", null,
"dockerFile()Ljava/lang/String;", 0, "Dockerfile"));

assertPropertiesDuplicate(info);
Expand Down

0 comments on commit ae99a24

Please sign in to comment.