diff --git a/src/net/fabricmc/fernflower/api/FabricJavadocStyle.java b/src/net/fabricmc/fernflower/api/FabricJavadocStyle.java
new file mode 100644
index 0000000000..984163fb68
--- /dev/null
+++ b/src/net/fabricmc/fernflower/api/FabricJavadocStyle.java
@@ -0,0 +1,12 @@
+package net.fabricmc.fernflower.api;
+
+public enum FabricJavadocStyle {
+ /**
+ * The traditional Javadoc style (default)
+ */
+ HTML,
+ /**
+ * The Markdown Javadoc style introduced in Java 23
+ */
+ MARKDOWN,
+}
diff --git a/src/net/fabricmc/fernflower/api/IFabricJavadocProvider.java b/src/net/fabricmc/fernflower/api/IFabricJavadocProvider.java
index ed4fe7d836..b6a8885cd5 100644
--- a/src/net/fabricmc/fernflower/api/IFabricJavadocProvider.java
+++ b/src/net/fabricmc/fernflower/api/IFabricJavadocProvider.java
@@ -9,7 +9,7 @@
* Provides (optional) javadoc for Classes/Methods/Fields encountered by
* {@link org.jetbrains.java.decompiler.main.ClassWriter}.
*
- * May be set as a property in the constructor of {@link org.jetbrains.java.decompiler.main.Fernflower} by using
+ *
May be set as a property in the constructor of {@link org.jetbrains.java.decompiler.main.Fernflower} by using
* the key {@code IFabricJavadocProvider.PROPERTY_NAME}
*/
public interface IFabricJavadocProvider {
@@ -20,4 +20,16 @@ public interface IFabricJavadocProvider {
String getFieldDoc(StructClass structClass, StructField structField);
String getMethodDoc(StructClass structClass, StructMethod structMethod);
+
+ default FabricJavadocStyle getClassJavadocStyle(StructClass structClass) {
+ return FabricJavadocStyle.HTML;
+ }
+
+ default FabricJavadocStyle getFieldJavadocStyle(StructClass structClass, StructField structField) {
+ return getClassJavadocStyle(structClass);
+ }
+
+ default FabricJavadocStyle getMethodJavadocStyle(StructClass structClass, StructMethod structMethod) {
+ return getClassJavadocStyle(structClass);
+ }
}
\ No newline at end of file
diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java
index 2a7ccf90d1..c9ea3c270a 100644
--- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java
+++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java
@@ -2,6 +2,8 @@
package org.jetbrains.java.decompiler.main;
import java.util.concurrent.atomic.AtomicBoolean;
+
+import net.fabricmc.fernflower.api.FabricJavadocStyle;
import net.fabricmc.fernflower.api.IFabricJavadocProvider;
import org.jetbrains.java.decompiler.api.plugin.StatementWriter;
import org.jetbrains.java.decompiler.code.CodeConstants;
@@ -689,7 +691,7 @@ private void writeClassDefinition(ClassNode node, TextBuffer buffer, int indent)
}
if (javadocProvider != null) {
- appendJavadoc(buffer, javadocProvider.getClassDoc(cl), indent);
+ appendJavadoc(buffer, javadocProvider.getClassJavadocStyle(cl), javadocProvider.getClassDoc(cl), indent);
}
appendAnnotations(buffer, indent, cl, -1);
@@ -876,7 +878,7 @@ public void writeField(ClassWrapper wrapper, StructClass cl, StructField fd, Tex
}
if (javadocProvider != null) {
- appendJavadoc(buffer, javadocProvider.getFieldDoc(cl, fd), indent);
+ appendJavadoc(buffer, javadocProvider.getFieldJavadocStyle(cl, fd), javadocProvider.getFieldDoc(cl, fd), indent);
}
Set writtenAnnotations = appendAnnotations(buffer, indent, fd, TypeAnnotation.FIELD);
@@ -1130,7 +1132,7 @@ public boolean writeMethod(ClassNode node, StructMethod mt, int methodIndex, Tex
}
if (javadocProvider != null) {
- appendJavadoc(buffer, javadocProvider.getMethodDoc(cl, mt), indent);
+ appendJavadoc(buffer, javadocProvider.getMethodJavadocStyle(cl, mt), javadocProvider.getMethodDoc(cl, mt), indent);
}
Set writtenAnnotations = appendAnnotations(buffer, indent, mt, TypeAnnotation.METHOD_RETURN_TYPE);
@@ -1786,13 +1788,22 @@ private static void appendComment(TextBuffer buffer, String comment, int indent)
buffer.appendIndent(indent).append("// $VF: ").append(comment).appendLineSeparator();
}
- private static void appendJavadoc(TextBuffer buffer, String javaDoc, int indent) {
+ private static void appendJavadoc(TextBuffer buffer, FabricJavadocStyle javadocStyle, String javaDoc, int indent) {
if (javaDoc == null) return;
- buffer.appendIndent(indent).append("/**").appendLineSeparator();
- for (String s : javaDoc.split("\n")) {
- buffer.appendIndent(indent).append(" * ").append(s).appendLineSeparator();
+ switch (javadocStyle) {
+ case HTML -> {
+ buffer.appendIndent(indent).append("/**").appendLineSeparator();
+ for (String s : javaDoc.split("\n")) {
+ buffer.appendIndent(indent).append(" * ").append(s).appendLineSeparator();
+ }
+ buffer.appendIndent(indent).append(" */").appendLineSeparator();
+ }
+ case MARKDOWN -> {
+ for (String s : javaDoc.split("\n")) {
+ buffer.appendIndent(indent).append("/// ").append(s).appendLineSeparator();
+ }
+ }
}
- buffer.appendIndent(indent).append(" */").appendLineSeparator();
}
public static void appendSyntheticClassComment(StructClass cl, TextBuffer buffer) {
diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java
index 70bf9bc9c8..434cde8a67 100644
--- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java
+++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java
@@ -1,6 +1,7 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.java.decompiler;
+import net.fabricmc.fernflower.api.FabricJavadocStyle;
import net.fabricmc.fernflower.api.IFabricJavadocProvider;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.jetbrains.java.decompiler.struct.StructClass;
@@ -98,6 +99,29 @@ public String getMethodDoc(StructClass structClass, StructMethod structMethod) {
return "Method javadoc for '" + structMethod.getName() + "'";
}
});
+ registerSet("Markdown Javadoc", () -> {
+ register(JAVA_8, "TestMarkdownJavadoc");
+ }, IFabricJavadocProvider.PROPERTY_NAME, new IFabricJavadocProvider() {
+ @Override
+ public String getClassDoc(StructClass structClass) {
+ return "Class javadoc for '" + structClass.qualifiedName + "'";
+ }
+
+ @Override
+ public String getFieldDoc(StructClass structClass, StructField structField) {
+ return "Field javadoc for '" + structField.getName() + "'";
+ }
+
+ @Override
+ public String getMethodDoc(StructClass structClass, StructMethod structMethod) {
+ return "Method javadoc for '" + structMethod.getName() + "'";
+ }
+
+ @Override
+ public FabricJavadocStyle getClassJavadocStyle(StructClass structClass) {
+ return FabricJavadocStyle.MARKDOWN;
+ }
+ });
// TODO: converter renaming different on different platforms?
registerSet("Renaming", () -> registerFailable(JAVA_8, "TestRenameEntities"),
IFernflowerPreferences.BYTECODE_SOURCE_MAPPING, "1",
diff --git a/testData/results/pkg/TestMarkdownJavadoc.dec b/testData/results/pkg/TestMarkdownJavadoc.dec
new file mode 100644
index 0000000000..504834c43e
--- /dev/null
+++ b/testData/results/pkg/TestMarkdownJavadoc.dec
@@ -0,0 +1,11 @@
+package pkg;
+
+/// Class javadoc for 'pkg/TestMarkdownJavadoc'
+public class TestMarkdownJavadoc {
+ /// Field javadoc for 'field'
+ public int field;
+
+ /// Method javadoc for 'method'
+ public void method() {
+ }
+}
diff --git a/testData/src/java8/pkg/TestMarkdownJavadoc.java b/testData/src/java8/pkg/TestMarkdownJavadoc.java
new file mode 100644
index 0000000000..d897d85946
--- /dev/null
+++ b/testData/src/java8/pkg/TestMarkdownJavadoc.java
@@ -0,0 +1,9 @@
+package pkg;
+
+public class TestMarkdownJavadoc {
+ public int field;
+
+ public void method() {
+
+ }
+}
\ No newline at end of file