8275788: Create code element with suitable attributes for code snippets
Reviewed-by: prappo
This commit is contained in:
parent
7de653e428
commit
19075b3f6b
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets
test/langtools/jdk/javadoc/doclet/testSnippetTag
@ -381,24 +381,22 @@ public class TagletWriterImpl extends TagletWriter {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Content snippetTagOutput(Element element, SnippetTree tag, StyledText content) {
|
||||
String copyText = resources.getText("doclet.Copy_snippet_to_clipboard");
|
||||
String copiedText = resources.getText("doclet.Copied_snippet_to_clipboard");
|
||||
HtmlTree copy = HtmlTree.DIV(HtmlStyle.snippetContainer,
|
||||
HtmlTree.A("#", new HtmlTree(TagName.IMG)
|
||||
.put(HtmlAttr.SRC, htmlWriter.pathToRoot.resolve(DocPaths.CLIPBOARD_SVG).getPath())
|
||||
.put(HtmlAttr.ALT, copyText))
|
||||
.addStyle(HtmlStyle.snippetCopy)
|
||||
.put(HtmlAttr.ONCLICK, "copySnippet(this)")
|
||||
.put(HtmlAttr.ARIA_LABEL, copyText)
|
||||
.put(HtmlAttr.DATA_COPIED, copiedText));
|
||||
HtmlTree pre = new HtmlTree(TagName.PRE)
|
||||
.setStyle(HtmlStyle.snippet);
|
||||
pre.add(Text.of(utils.normalizeNewlines("\n")));
|
||||
protected Content snippetTagOutput(Element element, SnippetTree tag, StyledText content,
|
||||
String id, String lang) {
|
||||
HtmlTree pre = new HtmlTree(TagName.PRE).setStyle(HtmlStyle.snippet);
|
||||
if (id != null && !id.isBlank()) {
|
||||
pre.put(HtmlAttr.ID, id);
|
||||
}
|
||||
HtmlTree code = new HtmlTree(TagName.CODE)
|
||||
.add(HtmlTree.EMPTY); // Make sure the element is always rendered
|
||||
if (lang != null && !lang.isBlank()) {
|
||||
code.addStyle("language-" + lang);
|
||||
}
|
||||
|
||||
content.consumeBy((styles, sequence) -> {
|
||||
CharSequence text = utils.normalizeNewlines(sequence);
|
||||
if (styles.isEmpty()) {
|
||||
pre.add(text);
|
||||
code.add(text);
|
||||
} else {
|
||||
Element e = null;
|
||||
String t = null;
|
||||
@ -443,10 +441,20 @@ public class TagletWriterImpl extends TagletWriter {
|
||||
c = HtmlTree.SPAN(Text.of(sequence));
|
||||
classes.forEach(((HtmlTree) c)::addStyle);
|
||||
}
|
||||
pre.add(c);
|
||||
code.add(c);
|
||||
}
|
||||
});
|
||||
return copy.add(pre);
|
||||
String copyText = resources.getText("doclet.Copy_snippet_to_clipboard");
|
||||
String copiedText = resources.getText("doclet.Copied_snippet_to_clipboard");
|
||||
HtmlTree snippetContainer = HtmlTree.DIV(HtmlStyle.snippetContainer,
|
||||
HtmlTree.A("#", new HtmlTree(TagName.IMG)
|
||||
.put(HtmlAttr.SRC, htmlWriter.pathToRoot.resolve(DocPaths.CLIPBOARD_SVG).getPath())
|
||||
.put(HtmlAttr.ALT, copyText))
|
||||
.addStyle(HtmlStyle.snippetCopy)
|
||||
.put(HtmlAttr.ONCLICK, "copySnippet(this)")
|
||||
.put(HtmlAttr.ARIA_LABEL, copyText)
|
||||
.put(HtmlAttr.DATA_COPIED, copiedText));
|
||||
return snippetContainer.add(pre.add(code));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -261,7 +261,21 @@ public class SnippetTaglet extends BaseTaglet {
|
||||
assert inlineSnippet != null || externalSnippet != null;
|
||||
StyledText text = inlineSnippet != null ? inlineSnippet : externalSnippet;
|
||||
|
||||
return writer.snippetTagOutput(holder, snippetTag, text);
|
||||
String lang = null;
|
||||
AttributeTree langAttr = attributes.get("lang");
|
||||
if (langAttr != null && langAttr.getValueKind() != AttributeTree.ValueKind.EMPTY) {
|
||||
lang = stringOf(langAttr.getValue());
|
||||
} else if (containsClass) {
|
||||
lang = "java";
|
||||
} else if (containsFile) {
|
||||
lang = languageFromFileName(fileObject.getName());
|
||||
}
|
||||
AttributeTree idAttr = attributes.get("id");
|
||||
String id = idAttr == null || idAttr.getValueKind() == AttributeTree.ValueKind.EMPTY
|
||||
? null
|
||||
: stringOf(idAttr.getValue());
|
||||
|
||||
return writer.snippetTagOutput(holder, snippetTag, text, id, lang);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -298,6 +312,16 @@ public class SnippetTaglet extends BaseTaglet {
|
||||
.collect(Collectors.joining());
|
||||
}
|
||||
|
||||
private String languageFromFileName(String fileName) {
|
||||
// TODO: find a way to extend/customize the list of recognized file name extensions
|
||||
if (fileName.endsWith(".java")) {
|
||||
return "java";
|
||||
} else if (fileName.endsWith(".properties")) {
|
||||
return "properties";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void error(TagletWriter writer, Element holder, DocTree tag, String key, Object... args) {
|
||||
writer.configuration().getMessages().error(
|
||||
writer.configuration().utils.getCommentHelper(holder).getDocTreePath(tag), key, args);
|
||||
|
@ -181,10 +181,13 @@ public abstract class TagletWriter {
|
||||
*
|
||||
* @param element The element that owns the doc comment
|
||||
* @param snippetTag the snippet tag
|
||||
* @param id the value of the id attribute, or null if not defined
|
||||
* @param lang the value of the lang attribute, or null if not defined
|
||||
*
|
||||
* @return the output
|
||||
*/
|
||||
protected abstract Content snippetTagOutput(Element element, SnippetTree snippetTag, StyledText text);
|
||||
protected abstract Content snippetTagOutput(Element element, SnippetTree snippetTag, StyledText text,
|
||||
String id, String lang);
|
||||
|
||||
/**
|
||||
* Returns the output for a {@code {@systemProperty...}} tag.
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8266666
|
||||
* @bug 8266666 8275788
|
||||
* @summary Implementation for snippets
|
||||
* @library /tools/lib ../../lib
|
||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||
@ -84,144 +84,237 @@ public class TestSnippetTag extends JavadocTester {
|
||||
}
|
||||
|
||||
/*
|
||||
* While the "id" and "lang" attributes are advertised in JEP 413, they are
|
||||
* currently unused by the implementation. The goal of this test is to make
|
||||
* sure that specifying these attributes causes no errors and exhibits no
|
||||
* unexpected behavior.
|
||||
* Make sure the "id" and "lang" attributes defined in JEP 413 are rendered
|
||||
* properly as recommended by the HTML5 specification.
|
||||
*/
|
||||
@Test
|
||||
public void testIdAndLangAttributes(Path base) throws IOException {
|
||||
Path srcDir = base.resolve("src");
|
||||
Path outDir = base.resolve("out");
|
||||
|
||||
// A record of a snippet content and matching expected attribute values
|
||||
record SnippetAttributes(String content, String id, String lang) {
|
||||
public String idAttribute() {
|
||||
return id == null ? "" : " id=\"" + id + "\"";
|
||||
}
|
||||
public String langAttribute() {
|
||||
return lang == null ? "" : " class=\"language-" + lang + "\"";
|
||||
}
|
||||
}
|
||||
|
||||
final var snippets = List.of(
|
||||
"""
|
||||
{@snippet id="foo" :
|
||||
Hello, Snippet!
|
||||
}
|
||||
""",
|
||||
"""
|
||||
{@snippet id="foo":
|
||||
Hello, Snippet!
|
||||
}
|
||||
""",
|
||||
"""
|
||||
{@snippet id='foo' :
|
||||
Hello, Snippet!
|
||||
}
|
||||
""",
|
||||
"""
|
||||
{@snippet id='foo':
|
||||
Hello, Snippet!
|
||||
}
|
||||
""",
|
||||
"""
|
||||
{@snippet id=foo :
|
||||
Hello, Snippet!
|
||||
}
|
||||
""",
|
||||
new SnippetAttributes("""
|
||||
{@snippet id="foo1" :
|
||||
Hello, Snippet!
|
||||
}
|
||||
""", "foo1", null),
|
||||
new SnippetAttributes("""
|
||||
{@snippet id="foo2":
|
||||
Hello, Snippet!
|
||||
}
|
||||
""", "foo2", null),
|
||||
new SnippetAttributes("""
|
||||
{@snippet id='foo3' :
|
||||
Hello, Snippet!
|
||||
}
|
||||
""", "foo3", null),
|
||||
new SnippetAttributes("""
|
||||
{@snippet id='foo4':
|
||||
Hello, Snippet!
|
||||
}
|
||||
""", "foo4", null),
|
||||
new SnippetAttributes("""
|
||||
{@snippet id=foo5 :
|
||||
Hello, Snippet!
|
||||
}
|
||||
""", "foo5", null),
|
||||
// (1) Haven't yet decided on this one. It's a consistency issue. On the one
|
||||
// hand, `:` is considered a part of a javadoc tag's name (e.g. JDK-4750173);
|
||||
// on the other hand, snippet markup treats `:` (next-line modifier) as a value
|
||||
// terminator.
|
||||
// """
|
||||
// {@snippet id=foo:
|
||||
// Hello, Snippet!
|
||||
// }
|
||||
// """,
|
||||
"""
|
||||
{@snippet id="" :
|
||||
Hello, Snippet!
|
||||
}
|
||||
""",
|
||||
"""
|
||||
{@snippet id="":
|
||||
Hello, Snippet!
|
||||
}
|
||||
""",
|
||||
"""
|
||||
{@snippet id='':
|
||||
Hello, Snippet!
|
||||
}
|
||||
""",
|
||||
"""
|
||||
{@snippet id=:
|
||||
Hello, Snippet!
|
||||
}
|
||||
""",
|
||||
"""
|
||||
{@snippet lang="java" :
|
||||
Hello, Snippet!
|
||||
}
|
||||
""",
|
||||
"""
|
||||
{@snippet lang="java":
|
||||
Hello, Snippet!
|
||||
}
|
||||
""",
|
||||
"""
|
||||
{@snippet lang='java' :
|
||||
Hello, Snippet!
|
||||
}
|
||||
""",
|
||||
"""
|
||||
{@snippet lang='java':
|
||||
Hello, Snippet!
|
||||
}
|
||||
""",
|
||||
"""
|
||||
{@snippet lang=java :
|
||||
Hello, Snippet!
|
||||
}
|
||||
""",
|
||||
"""
|
||||
{@snippet lang="properties" :
|
||||
Hello, Snippet!
|
||||
}
|
||||
""",
|
||||
"""
|
||||
{@snippet lang="text" :
|
||||
Hello, Snippet!
|
||||
}
|
||||
""",
|
||||
"""
|
||||
{@snippet lang="" :
|
||||
Hello, Snippet!
|
||||
}
|
||||
""",
|
||||
"""
|
||||
{@snippet lang="foo" id="bar" :
|
||||
Hello, Snippet!
|
||||
}
|
||||
"""
|
||||
// new SnippetAttributes("""
|
||||
// {@snippet id=foo6:
|
||||
// Hello, Snippet!
|
||||
// }
|
||||
// """, "foo6", null),
|
||||
|
||||
new SnippetAttributes("""
|
||||
{@snippet id="" :
|
||||
Hello, Snippet!
|
||||
}
|
||||
""", null, null),
|
||||
new SnippetAttributes("""
|
||||
{@snippet id="":
|
||||
Hello, Snippet!
|
||||
}
|
||||
""", null, null),
|
||||
new SnippetAttributes("""
|
||||
{@snippet id='':
|
||||
Hello, Snippet!
|
||||
}
|
||||
""", null, null),
|
||||
new SnippetAttributes("""
|
||||
{@snippet id=:
|
||||
Hello, Snippet!
|
||||
}
|
||||
""", null, null),
|
||||
new SnippetAttributes("""
|
||||
{@snippet lang="java" :
|
||||
Hello, Snippet!
|
||||
}
|
||||
""", null, "java"),
|
||||
new SnippetAttributes("""
|
||||
{@snippet lang="java":
|
||||
Hello, Snippet!
|
||||
}
|
||||
""", null, "java"),
|
||||
new SnippetAttributes("""
|
||||
{@snippet lang='java' :
|
||||
Hello, Snippet!
|
||||
}
|
||||
""", null, "java"),
|
||||
new SnippetAttributes("""
|
||||
{@snippet lang='java':
|
||||
Hello, Snippet!
|
||||
}
|
||||
""", null, "java"),
|
||||
new SnippetAttributes("""
|
||||
{@snippet lang=java :
|
||||
Hello, Snippet!
|
||||
}
|
||||
""", null, "java"),
|
||||
new SnippetAttributes("""
|
||||
{@snippet lang="properties" :
|
||||
Hello, Snippet!
|
||||
}
|
||||
""", null, "properties"),
|
||||
new SnippetAttributes("""
|
||||
{@snippet lang="text" :
|
||||
Hello, Snippet!
|
||||
}
|
||||
""", null, "text"),
|
||||
new SnippetAttributes("""
|
||||
{@snippet lang="" :
|
||||
Hello, Snippet!
|
||||
}
|
||||
""", null, null),
|
||||
new SnippetAttributes("""
|
||||
{@snippet lang="foo" id="bar" :
|
||||
Hello, Snippet!
|
||||
}
|
||||
""", "bar", "foo")
|
||||
);
|
||||
ClassBuilder classBuilder = new ClassBuilder(tb, "pkg.A")
|
||||
.setModifiers("public", "class");
|
||||
forEachNumbered(snippets, (s, i) -> {
|
||||
classBuilder.addMembers(
|
||||
MethodBuilder.parse("public void case%s() { }".formatted(i))
|
||||
.setComments(s));
|
||||
.setComments("A method.", s.content()));
|
||||
});
|
||||
classBuilder.write(srcDir);
|
||||
javadoc("-d", outDir.toString(),
|
||||
"-sourcepath", srcDir.toString(),
|
||||
"pkg");
|
||||
checkExit(Exit.OK);
|
||||
checkLinks();
|
||||
for (int j = 0; j < snippets.size(); j++) {
|
||||
SnippetAttributes snippet = snippets.get(j);
|
||||
checkOutput("pkg/A.html", true,
|
||||
"""
|
||||
<span class="element-name">case%s</span>()</div>
|
||||
<div class="block">
|
||||
<div class="block">A method.
|
||||
\s
|
||||
<div class="snippet-container"><a href="#" class="snippet-copy" onclick="cop\
|
||||
ySnippet(this)" aria-label="Copy" data-copied="Copied!"><img src="../copy.sv\
|
||||
g" alt="Copy"></a>
|
||||
<pre class="snippet">
|
||||
Hello, Snippet!
|
||||
</pre>
|
||||
<pre class="snippet"%s><code%s> Hello, Snippet!
|
||||
</code></pre>
|
||||
</div>
|
||||
""".formatted(j));
|
||||
""".formatted(j, snippet.idAttribute(), snippet.langAttribute()));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure the lang attribute is derived correctly from the snippet source file
|
||||
* for external snippets when it is not defined in the snippet. Defining the lang
|
||||
* attribute in the snippet should always override this mechanism.
|
||||
*/
|
||||
@Test
|
||||
public void testExternalImplicitAttributes(Path base) throws IOException {
|
||||
Path srcDir = base.resolve("src");
|
||||
Path outDir = base.resolve("out");
|
||||
|
||||
ClassBuilder classBuilder = new ClassBuilder(tb, "com.example.Cls")
|
||||
.setModifiers("public", "class");
|
||||
classBuilder.setComments("""
|
||||
{@snippet class="Snippets" region="code" id="snippet1"}
|
||||
{@snippet file="Snippets.java" region="code" id="snippet2"}
|
||||
{@snippet class="Snippets" region="code" id="snippet3" lang="none"}
|
||||
{@snippet file="Snippets.java" region="code" id="snippet4" lang="none"}
|
||||
{@snippet class="Snippets" region="code" id="snippet5" lang=""}
|
||||
{@snippet file="Snippets.java" region="code" id="snippet6" lang=""}
|
||||
{@snippet file="user.properties" id="snippet7"}
|
||||
{@snippet file="user.properties" id="snippet8" lang="none"}
|
||||
{@snippet file="user.properties" id="snippet9" lang=""}
|
||||
""");
|
||||
addSnippetFile(srcDir, "com.example", "Snippets.java", """
|
||||
public class Snippets {
|
||||
public static void printMessage(String msg) {
|
||||
// @start region="code"
|
||||
System.out.println(msg);
|
||||
// @end
|
||||
}
|
||||
}
|
||||
""");
|
||||
addSnippetFile(srcDir, "com.example", "user.properties", """
|
||||
user=jane
|
||||
home=/home/jane
|
||||
""");
|
||||
classBuilder.write(srcDir);
|
||||
javadoc("-d", outDir.toString(),
|
||||
"-sourcepath", srcDir.toString(),
|
||||
"com.example");
|
||||
checkExit(Exit.OK);
|
||||
checkLinks();
|
||||
checkOutput("com/example/Cls.html", true,
|
||||
"""
|
||||
<pre class="snippet" id="snippet1"><code class="language-java">
|
||||
System.out.println(msg);
|
||||
</code></pre>""",
|
||||
"""
|
||||
<pre class="snippet" id="snippet2"><code class="language-java">
|
||||
System.out.println(msg);
|
||||
</code></pre>""",
|
||||
"""
|
||||
<pre class="snippet" id="snippet3"><code class="language-none">
|
||||
System.out.println(msg);
|
||||
</code></pre>""",
|
||||
"""
|
||||
<pre class="snippet" id="snippet4"><code class="language-none">
|
||||
System.out.println(msg);
|
||||
</code></pre>""",
|
||||
"""
|
||||
<pre class="snippet" id="snippet5"><code>
|
||||
System.out.println(msg);
|
||||
</code></pre>""",
|
||||
"""
|
||||
<pre class="snippet" id="snippet6"><code>
|
||||
System.out.println(msg);
|
||||
</code></pre>""",
|
||||
"""
|
||||
<pre class="snippet" id="snippet7"><code class="language-properties">user=jane
|
||||
home=/home/jane
|
||||
</code></pre>""",
|
||||
"""
|
||||
<pre class="snippet" id="snippet8"><code class="language-none">user=jane
|
||||
home=/home/jane
|
||||
</code></pre>""",
|
||||
"""
|
||||
<pre class="snippet" id="snippet9"><code>user=jane
|
||||
home=/home/jane
|
||||
</code></pre>""");
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a convenience method to iterate through a list.
|
||||
* Unlike List.forEach, this method provides the consumer not only with an
|
||||
@ -858,8 +951,7 @@ public class TestSnippetTag extends JavadocTester {
|
||||
<div class="snippet-container"><a href="#" class="snippet-copy" onclick="cop\
|
||||
ySnippet(this)" aria-label="Copy" data-copied="Copied!"><img src="../copy.sv\
|
||||
g" alt="Copy"></a>
|
||||
<pre class="snippet">
|
||||
%s</pre>
|
||||
<pre class="snippet"><code>%s</code></pre>
|
||||
</div>""".formatted(id, t.expectedOutput()));
|
||||
});
|
||||
}
|
||||
@ -955,8 +1047,7 @@ public class TestSnippetTag extends JavadocTester {
|
||||
<div class="snippet-container"><a href="#" class="snippet-copy" onclick="cop\
|
||||
ySnippet(this)" aria-label="Copy" data-copied="Copied!"><img src="../copy.sv\
|
||||
g" alt="Copy"></a>
|
||||
<pre class="snippet">
|
||||
%s</pre>
|
||||
<pre class="snippet"><code>%s</code></pre>
|
||||
</div>""".formatted(index, expectedOutput));
|
||||
});
|
||||
}
|
||||
@ -1517,8 +1608,7 @@ public class TestSnippetTag extends JavadocTester {
|
||||
<div class="snippet-container"><a href="#" class="snippet-copy" onclick="cop\
|
||||
ySnippet(this)" aria-label="Copy" data-copied="Copied!"><img src="../copy.sv\
|
||||
g" alt="Copy"></a>
|
||||
<pre class="snippet">
|
||||
%s</pre>
|
||||
<pre class="snippet"><code>%s</code></pre>
|
||||
</div>""".formatted(index, t.expectedOutput()));
|
||||
});
|
||||
}
|
||||
@ -1635,8 +1725,7 @@ public class TestSnippetTag extends JavadocTester {
|
||||
<div class="snippet-container"><a href="#" class="snippet-copy" onclick="copySni\
|
||||
ppet(this)" aria-label="Copy" data-copied="Copied!"><img src="../copy.svg" alt="\
|
||||
Copy"></a>
|
||||
<pre class="snippet">
|
||||
</pre>
|
||||
<pre class="snippet"><code></code></pre>
|
||||
</div>""");
|
||||
checkOutput("pkg/A.html", true,
|
||||
"""
|
||||
@ -1645,8 +1734,7 @@ public class TestSnippetTag extends JavadocTester {
|
||||
<div class="snippet-container"><a href="#" class="snippet-copy" onclick="copySni\
|
||||
ppet(this)" aria-label="Copy" data-copied="Copied!"><img src="../copy.svg" alt="\
|
||||
Copy"></a>
|
||||
<pre class="snippet">
|
||||
</pre>
|
||||
<pre class="snippet"><code></code></pre>
|
||||
</div>""");
|
||||
}
|
||||
|
||||
@ -1747,8 +1835,7 @@ public class TestSnippetTag extends JavadocTester {
|
||||
<div class="snippet-container"><a href="#" class="snippet-copy" onclick="cop\
|
||||
ySnippet(this)" aria-label="Copy" data-copied="Copied!"><img src="../copy.sv\
|
||||
g" alt="Copy"></a>
|
||||
<pre class="snippet">
|
||||
2</pre>
|
||||
<pre class="snippet"><code>2</code></pre>
|
||||
</div>
|
||||
""".formatted(j));
|
||||
}
|
||||
@ -1832,8 +1919,7 @@ public class TestSnippetTag extends JavadocTester {
|
||||
<div class="snippet-container"><a href="#" class="snippet-copy" onclick="cop\
|
||||
ySnippet(this)" aria-label="Copy" data-copied="Copied!"><img src="../copy.sv\
|
||||
g" alt="Copy"></a>
|
||||
<pre class="snippet">
|
||||
%s</pre>
|
||||
<pre class="snippet"><code>%s</code></pre>
|
||||
</div>""".formatted(index, t.expectedOutput()));
|
||||
});
|
||||
}
|
||||
@ -2165,8 +2251,7 @@ public class TestSnippetTag extends JavadocTester {
|
||||
<div class="snippet-container"><a href="#" class="snippet-copy" onclick="cop\
|
||||
ySnippet(this)" aria-label="Copy" data-copied="Copied!"><img src="../copy.sv\
|
||||
g" alt="Copy"></a>
|
||||
<pre class="snippet">
|
||||
%s</pre>
|
||||
<pre class="snippet"><code>%s</code></pre>
|
||||
</div>""".formatted(index, t.expectedOutput()));
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user