Skip to content

Commit 710ef38

Browse files
committed
Bypass Markdown -> HTML Conversion for Markdown Comments
- For JEP 467, JDT parses the markdown comments into the AST Model as a Javadoc node (TagElement & TextElement and Name/MemberRef/MethodRef for links), converting to HTML. JDT-LS converts the HTML back to Markdown using Remark. - This bypasses the HTML -> Markdown conversion by attempting to render the Javadoc comments directly as Markdown, in part because the content is already (mostly) Markdown Signed-off-by: Roland Grunberg <[email protected]>
1 parent 806786e commit 710ef38

File tree

1 file changed

+177
-5
lines changed

1 file changed

+177
-5
lines changed

org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/javadoc/JavadocContentAccess2.java

+177-5
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,33 @@
1515

1616
import java.io.IOException;
1717
import java.io.Reader;
18+
import java.io.StringReader;
1819
import java.net.URI;
1920
import java.net.URISyntaxException;
2021
import java.util.ArrayList;
22+
import java.util.Arrays;
23+
import java.util.Deque;
24+
import java.util.LinkedList;
2125
import java.util.List;
2226

2327
import org.eclipse.core.runtime.CoreException;
28+
import org.eclipse.jdt.core.IBuffer;
2429
import org.eclipse.jdt.core.IJavaElement;
2530
import org.eclipse.jdt.core.IJavaModelStatusConstants;
31+
import org.eclipse.jdt.core.ILocalVariable;
2632
import org.eclipse.jdt.core.IMember;
2733
import org.eclipse.jdt.core.IMethod;
2834
import org.eclipse.jdt.core.IPackageFragment;
35+
import org.eclipse.jdt.core.ISourceRange;
36+
import org.eclipse.jdt.core.ITypeParameter;
2937
import org.eclipse.jdt.core.JavaModelException;
3038
import org.eclipse.jdt.core.dom.ASTNode;
3139
import org.eclipse.jdt.core.dom.Javadoc;
40+
import org.eclipse.jdt.core.dom.MemberRef;
41+
import org.eclipse.jdt.core.dom.MethodRef;
42+
import org.eclipse.jdt.core.dom.MethodRefParameter;
43+
import org.eclipse.jdt.core.dom.Name;
44+
import org.eclipse.jdt.core.dom.SimpleName;
3245
import org.eclipse.jdt.core.dom.TagElement;
3346
import org.eclipse.jdt.core.dom.TextElement;
3447
import org.eclipse.jdt.core.manipulation.internal.javadoc.CoreJavaDocSnippetStringEvaluator;
@@ -38,6 +51,8 @@
3851
import org.eclipse.jdt.core.manipulation.internal.javadoc.CoreMarkdownAccessImpl;
3952
import org.eclipse.jdt.core.manipulation.internal.javadoc.IJavadocContentFactory;
4053
import org.eclipse.jdt.core.manipulation.internal.javadoc.JavadocLookup;
54+
import org.eclipse.jdt.internal.core.manipulation.JavaManipulationPlugin;
55+
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
4156
import org.eclipse.jdt.internal.ui.viewsupport.CoreJavaElementLinks;
4257
import org.eclipse.jdt.ls.core.internal.JDTUtils;
4358
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
@@ -70,18 +85,149 @@ public static Reader getPlainTextContentReader(IMember member) throws JavaModelE
7085

7186
public static Reader getMarkdownContentReader(IJavaElement element) {
7287

88+
CoreJavadocAccess access = createJdtLsJavadocAccess();
7389
try {
74-
CoreJavadocAccess access = createJdtLsJavadocAccess();
75-
String rawHtml = access.getHTMLContent(element, true);
76-
Reader markdownReader = new JavaDoc2MarkdownConverter(rawHtml).getAsReader();
77-
return markdownReader;
90+
String content = getJavaDocNode(element);
91+
Javadoc node = CoreJavadocContentAccessUtility.getJavadocNode(element, content);
92+
// TODO: elements beyond the 0th are javadoc tags
93+
StringBuilder buf = new StringBuilder();
94+
if (content != null && content.startsWith("///")) {
95+
for (Object tag : node.tags()) {
96+
buf.append(" \n");
97+
collectTagElements(content, element, (TagElement) tag, buf);
98+
}
99+
return buf.length() > 0 ? new StringReader(buf.substring(1)) : new StringReader(content);
100+
} else {
101+
String rawHtml = access.getHTMLContent(element, true);
102+
Reader markdownReader = new JavaDoc2MarkdownConverter(rawHtml).getAsReader();
103+
return markdownReader;
104+
}
78105
} catch (IOException | CoreException e) {
79106

80107
}
81108

82109
return null;
83110
}
84111

112+
private static void collectTagElements(String content, IJavaElement element, TagElement tag, StringBuilder buf) {
113+
if (tag.getTagName() != null) {
114+
String heading = switch (tag.getTagName()) {
115+
case TagElement.TAG_API_NOTE -> "API Note:";
116+
case TagElement.TAG_AUTHOR -> "Author:";
117+
case TagElement.TAG_IMPL_SPEC -> "Impl Spec:";
118+
case TagElement.TAG_IMPL_NOTE -> "Impl Note:";
119+
case TagElement.TAG_PARAM -> "Parameters:";
120+
case TagElement.TAG_PROVIDES -> "Provides:";
121+
case TagElement.TAG_RETURN -> "Returns:";
122+
case TagElement.TAG_THROWS -> "Throws:";
123+
case TagElement.TAG_EXCEPTION -> "Throws:";
124+
case TagElement.TAG_SINCE -> "Since:";
125+
case TagElement.TAG_SEE -> "See:";
126+
case TagElement.TAG_VERSION -> "See:";
127+
case TagElement.TAG_USES -> "Uses:";
128+
default -> "";
129+
};
130+
buf.append("* **" + heading + "**");
131+
buf.append(" \n");
132+
buf.append(" * ");
133+
}
134+
Deque<ASTNode> queue = new LinkedList<>();
135+
queue.addAll(tag.fragments());
136+
while (!queue.isEmpty()) {
137+
ASTNode e = queue.pop();
138+
if (e instanceof TagElement t) {
139+
if ("@link".equals(t.getTagName()) || "@linkplain".equals(t.getTagName())) {
140+
collectLinkedTag(element, t, buf);
141+
} else {
142+
collectTagElements(content, element, t, buf);
143+
}
144+
} else if (e instanceof TextElement) {
145+
buf.append(((TextElement) e).getText());
146+
} else if ("@see".equals(tag.getTagName())) {
147+
collectLinkedTag(element, tag, buf);
148+
} else {
149+
}
150+
151+
ASTNode next = queue.peek();
152+
if (next != null) {
153+
int currEnd = e.getStartPosition() + e.getLength();
154+
int nextStart = next.getStartPosition();
155+
if (currEnd != nextStart) {
156+
buf.append(" \n");
157+
} else {
158+
buf.append(" ");
159+
}
160+
}
161+
}
162+
}
163+
164+
private static void collectLinkedTag(IJavaElement element, TagElement t, StringBuilder buf) {
165+
List children = t.fragments();
166+
if (t.fragments().size() > 0) {
167+
try {
168+
String[] res;
169+
String linkTitle;
170+
if (t.fragments().size() == 2) {
171+
linkTitle = ((TextElement) t.fragments().get(0)).getText();
172+
res = collectLinkElement((ASTNode) children.get(1));
173+
} else {
174+
res = collectLinkElement((ASTNode) children.get(0));
175+
linkTitle = res[0];
176+
}
177+
buf.append("[" + linkTitle + "]");
178+
String uri = JdtLsJavadocAccessImpl.createLinkURIHelper(CoreJavaElementLinks.JAVADOC_SCHEME, element, res[0], res.length > 1 ? res[1] : null,
179+
res.length > 2 ? Arrays.asList(res).subList(2, res.length).toArray(new String[0]) : null);
180+
buf.append("(" + uri + ")");
181+
} catch (URISyntaxException ex) {
182+
JavaManipulationPlugin.log(ex);
183+
}
184+
}
185+
}
186+
187+
private static String[] collectLinkElement(ASTNode e) {
188+
String refTypeName = null;
189+
String refMemberName = null;
190+
String[] refMethodParamTypes = null;
191+
String[] refMethodParamNames = null;
192+
if (e instanceof Name) {
193+
Name name = (Name) e;
194+
refTypeName = name.getFullyQualifiedName();
195+
} else if (e instanceof MemberRef) {
196+
MemberRef memberRef = (MemberRef) e;
197+
Name qualifier = memberRef.getQualifier();
198+
refTypeName = qualifier == null ? "" : qualifier.getFullyQualifiedName(); //$NON-NLS-1$
199+
refMemberName = memberRef.getName().getIdentifier();
200+
} else if (e instanceof MethodRef) {
201+
MethodRef methodRef = (MethodRef) e;
202+
Name qualifier = methodRef.getQualifier();
203+
refTypeName = qualifier == null ? "" : qualifier.getFullyQualifiedName(); //$NON-NLS-1$
204+
refMemberName = methodRef.getName().getIdentifier();
205+
List<MethodRefParameter> params = methodRef.parameters();
206+
int ps = params.size();
207+
refMethodParamTypes = new String[ps];
208+
refMethodParamNames = new String[ps];
209+
for (int i = 0; i < ps; i++) {
210+
MethodRefParameter param = params.get(i);
211+
refMethodParamTypes[i] = ASTNodes.asString(param.getType());
212+
SimpleName paramName = param.getName();
213+
if (paramName != null) {
214+
refMethodParamNames[i] = paramName.getIdentifier();
215+
}
216+
}
217+
} else if (e instanceof TextElement) {
218+
refTypeName = ((TextElement) e).getText();
219+
}
220+
List<String> result = new ArrayList<>();
221+
result.add(refTypeName);
222+
if (refMemberName != null) {
223+
result.add(refMemberName);
224+
}
225+
if (refMethodParamTypes != null) {
226+
result.addAll(Arrays.asList(refMethodParamTypes));
227+
}
228+
return result.toArray(new String[0]);
229+
}
230+
85231
/**
86232
* @return
87233
*/
@@ -139,6 +285,28 @@ public IJavadocAccess createJavadocAccess(IJavaElement element, Javadoc javadoc,
139285
}
140286
};
141287

288+
public static String getJavaDocNode(IJavaElement element) throws JavaModelException {
289+
IMember member;
290+
if (element instanceof ILocalVariable) {
291+
member = ((ILocalVariable) element).getDeclaringMember();
292+
} else if (element instanceof ITypeParameter) {
293+
member = ((ITypeParameter) element).getDeclaringMember();
294+
} else if (element instanceof IMember) {
295+
member = (IMember) element;
296+
} else {
297+
return null;
298+
}
299+
300+
IBuffer buf = member.getOpenable().getBuffer();
301+
if (buf == null) {
302+
return null; // no source attachment found
303+
}
304+
305+
ISourceRange javadocRange = member.getJavadocRange();
306+
String rawJavadoc = buf.getText(javadocRange.getOffset(), javadocRange.getLength());
307+
return rawJavadoc;
308+
}
309+
142310
private static class JdtLsJavadocAccessImpl extends CoreJavadocAccessImpl {
143311

144312
/**
@@ -323,7 +491,11 @@ protected String markSnippet(String text, boolean isInSnippet) {
323491

324492
@Override
325493
protected String createLinkURI(String scheme, IJavaElement element, String refTypeName, String refMemberName, String[] refParameterTypes) throws URISyntaxException {
326-
URI javadocURI = CoreJavaElementLinks.createURIAsUri(scheme, fElement, refTypeName, refMemberName, refParameterTypes);
494+
return createLinkURIHelper(scheme, fElement, refTypeName, refMemberName, refParameterTypes);
495+
}
496+
497+
public static String createLinkURIHelper(String scheme, IJavaElement element, String refTypeName, String refMemberName, String[] refParameterTypes) throws URISyntaxException {
498+
URI javadocURI = CoreJavaElementLinks.createURIAsUri(scheme, element, refTypeName, refMemberName, refParameterTypes);
327499
IJavaElement linkTarget = CoreJavaElementLinks.parseURI(javadocURI);
328500
if (linkTarget == null) {
329501
return "";

0 commit comments

Comments
 (0)