Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8266666: Implementation for snippets #4795

Closed
wants to merge 40 commits into from
Closed
Changes from 23 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
b800f57
Initial commit
pavelrappo Jul 15, 2021
5491387
Do not use snippets
pavelrappo Jul 15, 2021
dd172b5
Update copyright years
pavelrappo Jul 15, 2021
c4e5b79
Update @since tags
pavelrappo Jul 27, 2021
4300903
Improve comments
pavelrappo Jul 27, 2021
34b274c
Make CSS rules more specific
pavelrappo Jul 27, 2021
6022df0
Restructure ...toolkit.taglets.snippet.** packages
pavelrappo Jul 27, 2021
01afad6
Change AnnotatedText<T> to StyledText
pavelrappo Jul 28, 2021
ae81871
Update src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/too…
pavelrappo Jul 29, 2021
0a47e9c
Update src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/too…
pavelrappo Jul 29, 2021
22d1e07
Pass through FIXMEs and TODOs
pavelrappo Jul 30, 2021
e1dbd98
Merge branch 'master' into 8266666
pavelrappo Aug 23, 2021
942b1e9
Remove JEP link from SnippetTree
pavelrappo Aug 23, 2021
731a1a6
Remove superfluous editor-fold
pavelrappo Aug 23, 2021
0510287
Add @summary to tests
pavelrappo Aug 23, 2021
a7cd155
Fix import layout and typos
pavelrappo Aug 24, 2021
26de340
Clarify the attributes order comment
pavelrappo Aug 24, 2021
be7ffe5
Merge branch 'master' into 8266666
pavelrappo Aug 24, 2021
87e765b
Format code closer to the ambient style
pavelrappo Aug 25, 2021
0d9c53a
Recover from a sloppy merge
pavelrappo Aug 25, 2021
a473e9a
Exclude unrelated changes to resources
pavelrappo Aug 25, 2021
98fbaab
Clean up tag parsing
pavelrappo Aug 30, 2021
b2b604f
Merge branch 'master' into 8266666
pavelrappo Aug 30, 2021
1b61b37
Merge branch 'master' into 8266666
pavelrappo Aug 31, 2021
e6d9f29
Be more specific when testing for syntax errors
pavelrappo Aug 31, 2021
2966876
Merge branch 'master' into 8266666
pavelrappo Sep 3, 2021
0a3adfb
Improve snippet attributes parsing
pavelrappo Sep 6, 2021
3df2d28
Refactor SnippetTaglet
pavelrappo Sep 6, 2021
b276f3f
Merge branch 'master' into 8266666
pavelrappo Sep 6, 2021
1705c93
Merge branch 'master' into 8266666
pavelrappo Sep 9, 2021
a217316
Clean up code
pavelrappo Sep 9, 2021
3c654e6
Remove trailing whitespace to satisfy jcheck
pavelrappo Sep 9, 2021
4a22c09
Address feedback
pavelrappo Sep 14, 2021
219d467
Address trivial feedback
pavelrappo Sep 15, 2021
0b3ea61
Merge branch 'master' into 8266666
pavelrappo Sep 17, 2021
1e3e5d5
Improve markup error messages
pavelrappo Sep 20, 2021
edb7cf8
Merge branch 'master' into 8266666
pavelrappo Sep 20, 2021
884dcb0
Address feedback
pavelrappo Sep 21, 2021
1050d4f
Use bug id, not JEP id
pavelrappo Sep 21, 2021
a7299d8
Merge branch 'master' into 8266666
pavelrappo Sep 21, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -184,7 +184,12 @@ enum Location implements JavaFileManager.Location {
/**
* Location to search for taglets.
*/
TAGLET_PATH;
TAGLET_PATH,

/**
* Location to search for snippets.
*/
SNIPPET_PATH;

public String getName() { return name(); }

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,7 +29,7 @@
import javax.lang.model.element.Name;

/**
* A tree node for an attribute in an HTML element.
* A tree node for an attribute in an HTML element or tag.
*
* @since 1.8
*/
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -37,7 +37,7 @@ public interface DocTree {
enum Kind {
/**
* Used for instances of {@link AttributeTree}
* representing an HTML attribute.
* representing an attribute in an HTML element or tag.
*/
ATTRIBUTE,

@@ -204,6 +204,12 @@ enum Kind {
*/
SINCE("since"),

/**
* Used for instances of {@link SnippetTree}
* representing an {@code @snippet} tag.
*/
SNIPPET("snippet"),

/**
* Used for instances of {@link EndElementTree}
* representing the start of an HTML element.
@@ -287,6 +287,21 @@ default R visitProvides(ProvidesTree node, P p) {
*/
R visitSince(SinceTree node, P p);

/**
* Visits a {@code SnippetTree} node.
*
* @implSpec Visits the provided {@code SnippetTree} node
* by calling {@code visitOther(node, p)}.
*
* @param node the node being visited
* @param p a parameter value
* @return a result value
* @since 18
*/
default R visitSnippet(SnippetTree node, P p) {
return visitOther(node, p);
}

/**
* Visits a {@code StartElementTree} node.
* @param node the node being visited
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package com.sun.source.doctree;

import java.util.List;

/**
* A tree node for an {@code @snippet} inline tag.
*
* <pre>
* {&#064;snippet :
* body
* }
*
* {&#064;snippet attributes}
*
* {&#064;snippet attributes :
* body
* }
* </pre>
*
* @since 18
*/
public interface SnippetTree extends InlineTagTree {

/**
* Returns the list of the attributes of the {@code @snippet} tag.
*
* @return the list of the attributes
*/
List<? extends DocTree> getAttributes();

/**
* Returns the body of the {@code @snippet} tag, or {@code null} if there is no body.
*
* @apiNote
* An instance of {@code SnippetTree} with an empty body differs from an
* instance of {@code SnippetTree} with no body.
* If a tag has no body, then calling this method returns {@code null}.
* If a tag has an empty body, then this method returns a {@code TextTree}
* whose {@link TextTree#getBody()} returns an empty string.
*
* @return the body of the tag, or {@code null} if there is no body
*/
TextTree getBody();
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -58,6 +58,7 @@
import com.sun.source.doctree.SerialFieldTree;
import com.sun.source.doctree.SerialTree;
import com.sun.source.doctree.SinceTree;
import com.sun.source.doctree.SnippetTree;
import com.sun.source.doctree.StartElementTree;
import com.sun.source.doctree.SummaryTree;
import com.sun.source.doctree.SystemPropertyTree;
@@ -79,7 +80,7 @@
*/
public interface DocTreeFactory {
/**
* Creates a new {@code AttributeTree} object, to represent an HTML attribute in an HTML tag.
* Creates a new {@code AttributeTree} object, to represent an attribute in an HTML element or tag.
* @param name the name of the attribute
* @param vkind the kind of the attribute value
* @param value the value, if any, of the attribute
@@ -326,6 +327,15 @@ default ReturnTree newReturnTree(boolean isInline, List<? extends DocTree> descr
*/
SinceTree newSinceTree(List<? extends DocTree> text);

/**
* Creates a new {@code SnippetTree} object, to represent a {@code {@snippet }} tag.
* @param attributes the attributes of the tag
* @param text the body of the tag, or {@code null} if the tag has no body (not to be confused with an empty body)
* @return a {@code SnippetTree} object
* @since 18
*/
SnippetTree newSnippetTree(List<? extends DocTree> attributes, TextTree text);

/**
* Creates a new {@code StartElementTree} object, to represent the start of an HTML element.
* @param name the name of the HTML element
@@ -492,6 +492,23 @@ public R visitSince(SinceTree node, P p) {
return scan(node.getBody(), p);
}

/**
* {@inheritDoc}
*
* @implSpec This implementation scans the children in left to right order.
*
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of scanning
* @since 18
*/
@Override
public R visitSnippet(SnippetTree node, P p) {
R r = scan(node.getAttributes(), p);
r = scanAndReduce(node.getBody(), p, r);
return r;
}

/**
* {@inheritDoc}
*
@@ -448,6 +448,21 @@ public R visitSince(SinceTree node, P p) {
return defaultAction(node, p);
}

/**
* {@inheritDoc}
*
* @implSpec This implementation calls {@code defaultAction}.
*
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of {@code defaultAction}
* @since 18
*/
pavelrappo marked this conversation as resolved.
Show resolved Hide resolved
@Override
public R visitSnippet(SnippetTree node, P p) {
return defaultAction(node, p);
}

/**
* {@inheritDoc}
*
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1062,6 +1062,19 @@ protected boolean isWhitespace(char ch) {
return Character.isWhitespace(ch);
}

protected boolean isHorizontalWhitespace(char ch) {
// Should `\f` (FORM FEED) break a line? Relevant specifications have
// different opinions on this:
//
// No: https://docs.oracle.com/javase/specs/jls/se14/html/jls-3.html#jls-3.4
// Yes: https://www.unicode.org/versions/Unicode13.0.0/ch05.pdf, "5.8 Newline Guidelines"
// No: https://html.spec.whatwg.org/multipage/syntax.html#newlines
//
// This parser treats `\f` as a line break (see `nextChar`). To be
// consistent with that behaviour, this method does the same.
return ch == ' ' || ch == '\t' /* || ch == '\f'*/;
}

pavelrappo marked this conversation as resolved.
Show resolved Hide resolved
protected void skipWhitespace() {
while (bp < buflen && isWhitespace(ch)) {
nextChar();
@@ -1397,6 +1410,90 @@ public DCTree parse(int pos) {
}
},

// {@snippet attributes :
// body}
new TagParser(TagParser.Kind.INLINE, DCTree.Kind.SNIPPET) {
@Override
DCTree parse(int pos) throws ParseException {
skipWhitespace();
List<DCTree> attributes = tagAttrs();
// expect "}" or ":"
if (ch == '}') {
nextChar();
return m.at(pos).newSnippetTree(attributes, null);
} else if (ch == ':') {
newline = false;
// consume ':'
nextChar();
// better still reuse JavaTokenizer here
// expect optional whitespace followed by mandatory newline
while (bp < buflen && isHorizontalWhitespace(ch)) {
nextChar();
}
// check that we are looking at newline
if (!newline) {
throw new ParseException("dc.unexpected.content");
}
// consume newline
nextChar();
DCText text = inlineText(WhitespaceRetentionPolicy.RETAIN_ALL);
nextChar();
return m.at(pos).newSnippetTree(attributes, text);
} else {
throw new ParseException("dc.unexpected.content");
}
}

/*
* Reads a series of inline snippet tag attributes.
*
* Attributes are terminated by the first of ":" (colon) or
* an unmatched "}" (closing curly).
*/
private List<DCTree> tagAttrs() {
ListBuffer<DCTree> attrs = new ListBuffer<>();
skipWhitespace();
while (bp < buflen && isIdentifierStart(ch)) {
int namePos = bp;
Name name = readAttributeName();
skipWhitespace();
List<DCTree> value = null;
ValueKind vkind = ValueKind.EMPTY;
if (ch == '=') {
ListBuffer<DCTree> v = new ListBuffer<>();
nextChar();
skipWhitespace();
if (ch == '\'' || ch == '"') {
newline = false;
vkind = (ch == '\'') ? ValueKind.SINGLE : ValueKind.DOUBLE;
char quote = ch;
nextChar();
textStart = bp;
while (bp < buflen && ch != quote) {
nextChar();
}
addPendingText(v, bp - 1);
nextChar();
} else {
vkind = ValueKind.UNQUOTED;
textStart = bp;
// TODO: do balance {}!
// Stop on '}' and ':' for them to be re-consumed by non-attribute parts of tag
while (bp < buflen && (ch != '}' && ch != ':' && !isUnquotedAttrValueTerminator(ch))) {
nextChar();
}
addPendingText(v, bp - 1);
}
skipWhitespace();
value = v.toList();
}
DCAttribute attr = m.at(namePos).newAttributeTree(name, vkind, value);
attrs.add(attr);
}
return attrs.toList();
}
},

// {@summary summary-text}
new TagParser(TagParser.Kind.INLINE, DCTree.Kind.SUMMARY) {
@Override
@@ -857,6 +857,36 @@ public List<? extends DocTree> getBody() {
}
}

public static class DCSnippet extends DCInlineTag implements SnippetTree {
public final List<? extends DocTree> attributes;
public final DCText body;

public DCSnippet(List<DCTree> attributes, DCText body) {
this.body = body;
this.attributes = attributes;
}

@Override @DefinedBy(Api.COMPILER_TREE)
public Kind getKind() {
return Kind.SNIPPET;
}

@Override @DefinedBy(Api.COMPILER_TREE)
public <R, D> R accept(DocTreeVisitor<R, D> v, D d) {
return v.visitSnippet(this, d);
}

@Override @DefinedBy(Api.COMPILER_TREE)
public List<? extends DocTree> getAttributes() {
return attributes;
}

@Override @DefinedBy(Api.COMPILER_TREE)
public TextTree getBody() {
return body;
}
}

public static class DCStartElement extends DCEndPosTree<DCStartElement> implements StartElementTree {
public final Name name;
public final List<DCTree> attrs;