8318106: Generated HTML for snippet does not always contain an id

Reviewed-by: liach
This commit is contained in:
Pavel Rappo 2024-07-12 23:11:04 +00:00
parent 5bc86f3329
commit 4166e53452
7 changed files with 65 additions and 19 deletions

View File

@ -36,6 +36,7 @@ import java.util.stream.Stream;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.ModuleElement;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
@ -595,4 +596,35 @@ public class HtmlIds {
}
return HtmlId.of(idValue);
}
/**
* Returns an id for a snippet.
*
* @param e the element in whose documentation the snippet appears
* @param snippetIds the set of snippet ids already generated
* @return a unique id for the snippet
*/
public HtmlId forSnippet(Element e, Set<String> snippetIds) {
String id = "snippet-";
ElementKind kind = e.getKind();
if (kind == ElementKind.PACKAGE) {
id += forPackage((PackageElement) e).name();
} else if (kind.isDeclaredType()) {
id += forClass((TypeElement) e).name();
} else if (kind.isExecutable()) {
id += forMember((ExecutableElement) e).getFirst().name();
} else if (kind.isField()) {
id += forMember((VariableElement) e).name();
} else if (kind == ElementKind.MODULE) {
id += ((ModuleElement) e).getQualifiedName();
} else {
// while utterly unexpected, we shouldn't fail
id += "unknown-element";
}
int counter = 1;
while (!snippetIds.add(id + counter)) {
counter++;
}
return HtmlId.of(id + counter);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2024, 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
@ -148,6 +148,8 @@ public class SnippetTaglet extends BaseTaglet {
var pre = new HtmlTree(TagName.PRE).setStyle(HtmlStyle.snippet);
if (id != null && !id.isBlank()) {
pre.put(HtmlAttr.ID, id);
} else {
pre.put(HtmlAttr.ID, config.htmlIds.forSnippet(element, ids).name());
}
var code = new HtmlTree(TagName.CODE)
.addUnchecked(Text.EMPTY); // Make sure the element is always rendered
@ -232,6 +234,8 @@ public class SnippetTaglet extends BaseTaglet {
return snippetContainer.add(pre.add(code));
}
private final Set<String> ids = new HashSet<>();
private static final class BadSnippetException extends Exception {
@java.io.Serial

View File

@ -121,7 +121,7 @@ public class TestMarkdownTaglets extends JavadocTester {
<div class="snippet-container"><button class="copy snippet-copy" aria-label="Copy snippet" \
onclick="copySnippet(this)"><span data-copied="Copied!">Copy</span>\
<img src="../resource-files/copy.svg" alt="Copy snippet"></button>
<pre class="snippet"><code> this is snippet_standalone
<pre class="snippet" id="snippet-snippet_standalone()1"><code> this is snippet_standalone
</code></pre>
</div>
@ -131,7 +131,7 @@ public class TestMarkdownTaglets extends JavadocTester {
<div class="block"><p>First sentence.</p>
<p>Before.</p>
<div class="snippet-container"><button class="copy snippet-copy" aria-label="Copy snippet" onclick="copySnippet(this)"><span data-copied="Copied!">Copy</span><img src="../resource-files/copy.svg" alt="Copy snippet"></button>
<pre class="snippet"><code> this is a snippet_wrapped
<pre class="snippet" id="snippet-snippet_wrapped()1"><code> this is a snippet_wrapped
</code></pre>
</div>

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2024, 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
@ -179,7 +179,7 @@ public class TestLangProperties extends SnippetTester {
<div class="block">
%s
</div>""".formatted(index, getSnippetHtmlRepresentation("A.html",
t.expectedOutput(), Optional.of("properties")));
t.expectedOutput(), Optional.of("properties"), Optional.of("snippet-case" + index + "()2")));
checkOutput("A.html", true, html);
});
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2024, 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
@ -52,6 +52,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.regex.MatchResult;
@ -365,7 +366,7 @@ First line // @highlight :
<span class="element-name">case%s</span>()</div>
<div class="block">
%s
</div>""".formatted(index, getSnippetHtmlRepresentation("A.html", t.expectedOutput()));
</div>""".formatted(index, getSnippetHtmlRepresentation("A.html", t.expectedOutput(), Optional.empty(), Optional.of("snippet-case" + index + "()2")));
checkOutput("A.html", true, html);
});
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2024, 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
@ -220,8 +220,9 @@ public class TestSnippetTag extends SnippetTester {
checkLinks();
for (int j = 0; j < snippets.size(); j++) {
var attr = snippets.get(j);
Optional<String> id = (attr.id() != null ? Optional.of(attr.id()) : Optional.of("snippet-case" + j + "()1"));
var snippetHtml = getSnippetHtmlRepresentation("pkg/A.html", " Hello, Snippet!\n",
Optional.ofNullable(attr.lang()), Optional.ofNullable(attr.id()));
Optional.ofNullable(attr.lang()), id);
checkOutput("pkg/A.html", true,
"""
<span class="element-name">case%s</span>()</div>
@ -879,7 +880,8 @@ public class TestSnippetTag extends SnippetTester {
"""
<span class="element-name">case%s</span>()</div>
<div class="block">
%s""".formatted(id, getSnippetHtmlRepresentation("pkg/A.html", t.expectedOutput())));
%s""".formatted(id, getSnippetHtmlRepresentation("pkg/A.html", t.expectedOutput(), Optional.empty(),
Optional.of("snippet-case" + id + "()2"))));
});
}
@ -971,7 +973,8 @@ public class TestSnippetTag extends SnippetTester {
"""
<span class="element-name">case%s</span>()</div>
<div class="block">
%s""".formatted(index, getSnippetHtmlRepresentation("pkg/A.html", expectedOutput)));
%s""".formatted(index, getSnippetHtmlRepresentation("pkg/A.html", expectedOutput, Optional.empty(),
Optional.of("snippet-case" + index + "()2"))));
});
}
@ -1549,7 +1552,8 @@ public class TestSnippetTag extends SnippetTester {
"""
<span class="element-name">case%s</span>()</div>
<div class="block">
%s""".formatted(index, getSnippetHtmlRepresentation("pkg/A.html", t.expectedOutput())));
%s""".formatted(index, getSnippetHtmlRepresentation("pkg/A.html", t.expectedOutput(), Optional.empty(),
Optional.of("snippet-case" + index + "()2"))));
});
}
@ -1662,12 +1666,14 @@ public class TestSnippetTag extends SnippetTester {
"""
<span class="element-name">case0</span>()</div>
<div class="block">
""" + getSnippetHtmlRepresentation("pkg/A.html", ""));
""" + getSnippetHtmlRepresentation("pkg/A.html", "", Optional.empty(),
Optional.of("snippet-case0()2")));
checkOutput("pkg/A.html", true,
"""
<span class="element-name">case1</span>()</div>
<div class="block">
""" + getSnippetHtmlRepresentation("pkg/A.html", ""));
""" + getSnippetHtmlRepresentation("pkg/A.html", "", Optional.empty(),
Optional.of("snippet-case1()2")));
}
@Test // TODO: use combinatorial methods
@ -1765,7 +1771,8 @@ public class TestSnippetTag extends SnippetTester {
<span class="element-name">case%s</span>()</div>
<div class="block">
%s
""".formatted(j, getSnippetHtmlRepresentation("pkg/A.html", "2")));
""".formatted(j, getSnippetHtmlRepresentation("pkg/A.html", "2", Optional.empty(),
Optional.of("snippet-case" + j + "()2"))));
}
}
@ -1844,7 +1851,8 @@ public class TestSnippetTag extends SnippetTester {
"""
<span class="element-name">case%s</span>()</div>
<div class="block">
%s""".formatted(index, getSnippetHtmlRepresentation("pkg/A.html", t.expectedOutput())));
%s""".formatted(index, getSnippetHtmlRepresentation("pkg/A.html", t.expectedOutput(), Optional.empty(),
Optional.of("snippet-case" + index + "()2"))));
});
}
@ -2302,7 +2310,8 @@ public class TestSnippetTag extends SnippetTester {
"""
<span class="element-name">case%s</span>()</div>
<div class="block">
%s""".formatted(index, getSnippetHtmlRepresentation("pkg/A.html", t.expectedOutput())));
%s""".formatted(index, getSnippetHtmlRepresentation("pkg/A.html", t.expectedOutput(), Optional.empty(),
Optional.of("snippet-case" + index + "()2"))));
});
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2024, 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
@ -91,6 +91,6 @@ public class TestSnippetUnnamedPackage extends SnippetTester {
%s
After.""".formatted(getSnippetHtmlRepresentation("C.html",
"public class S { }", Optional.of("java"))));
"public class S { }", Optional.of("java"), Optional.of("snippet-C1"))));
}
}