|
15 | 15 |
|
16 | 16 | import java.io.IOException;
|
17 | 17 | import java.io.Reader;
|
| 18 | +import java.io.StringReader; |
18 | 19 | import java.net.URI;
|
19 | 20 | import java.net.URISyntaxException;
|
20 | 21 | import java.util.ArrayList;
|
| 22 | +import java.util.Arrays; |
| 23 | +import java.util.Deque; |
| 24 | +import java.util.LinkedList; |
21 | 25 | import java.util.List;
|
22 | 26 |
|
23 | 27 | import org.eclipse.core.runtime.CoreException;
|
| 28 | +import org.eclipse.jdt.core.IBuffer; |
24 | 29 | import org.eclipse.jdt.core.IJavaElement;
|
25 | 30 | import org.eclipse.jdt.core.IJavaModelStatusConstants;
|
| 31 | +import org.eclipse.jdt.core.ILocalVariable; |
26 | 32 | import org.eclipse.jdt.core.IMember;
|
27 | 33 | import org.eclipse.jdt.core.IMethod;
|
28 | 34 | import org.eclipse.jdt.core.IPackageFragment;
|
| 35 | +import org.eclipse.jdt.core.ISourceRange; |
| 36 | +import org.eclipse.jdt.core.ITypeParameter; |
29 | 37 | import org.eclipse.jdt.core.JavaModelException;
|
30 | 38 | import org.eclipse.jdt.core.dom.ASTNode;
|
31 | 39 | 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; |
32 | 45 | import org.eclipse.jdt.core.dom.TagElement;
|
33 | 46 | import org.eclipse.jdt.core.dom.TextElement;
|
34 | 47 | import org.eclipse.jdt.core.manipulation.internal.javadoc.CoreJavaDocSnippetStringEvaluator;
|
|
38 | 51 | import org.eclipse.jdt.core.manipulation.internal.javadoc.CoreMarkdownAccessImpl;
|
39 | 52 | import org.eclipse.jdt.core.manipulation.internal.javadoc.IJavadocContentFactory;
|
40 | 53 | 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; |
41 | 56 | import org.eclipse.jdt.internal.ui.viewsupport.CoreJavaElementLinks;
|
42 | 57 | import org.eclipse.jdt.ls.core.internal.JDTUtils;
|
43 | 58 | import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
|
@@ -70,18 +85,149 @@ public static Reader getPlainTextContentReader(IMember member) throws JavaModelE
|
70 | 85 |
|
71 | 86 | public static Reader getMarkdownContentReader(IJavaElement element) {
|
72 | 87 |
|
| 88 | + CoreJavadocAccess access = createJdtLsJavadocAccess(); |
73 | 89 | 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 | + } |
78 | 105 | } catch (IOException | CoreException e) {
|
79 | 106 |
|
80 | 107 | }
|
81 | 108 |
|
82 | 109 | return null;
|
83 | 110 | }
|
84 | 111 |
|
| 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 | + |
85 | 231 | /**
|
86 | 232 | * @return
|
87 | 233 | */
|
@@ -139,6 +285,28 @@ public IJavadocAccess createJavadocAccess(IJavaElement element, Javadoc javadoc,
|
139 | 285 | }
|
140 | 286 | };
|
141 | 287 |
|
| 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 | + |
142 | 310 | private static class JdtLsJavadocAccessImpl extends CoreJavadocAccessImpl {
|
143 | 311 |
|
144 | 312 | /**
|
@@ -323,7 +491,11 @@ protected String markSnippet(String text, boolean isInSnippet) {
|
323 | 491 |
|
324 | 492 | @Override
|
325 | 493 | 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); |
327 | 499 | IJavaElement linkTarget = CoreJavaElementLinks.parseURI(javadocURI);
|
328 | 500 | if (linkTarget == null) {
|
329 | 501 | return "";
|
|
0 commit comments