diff --git a/build.xml b/build.xml index c78b6eb108b8..3067cd1fc095 100644 --- a/build.xml +++ b/build.xml @@ -45,21 +45,7 @@ patches/6330.diff - patches/7491.diff patches/7610.diff - patches/7641.diff - patches/7654.diff - patches/7670.diff - patches/7699.diff - patches/7709.diff - patches/7722.diff - patches/7724.diff - patches/7733.diff - patches/7750.diff - patches/7910.diff - patches/7921.diff - patches/7923.diff - patches/7926.diff patches/8036-draft.diff patches/8038-draft.diff patches/mvn-sh.diff @@ -69,7 +55,6 @@ patches/remove-db.diff patches/nbjavac-not-required.diff patches/l10n-licence.diff - patches/no-security-manager-allow.diff patches/dev-dependency-licenses.diff diff --git a/netbeans b/netbeans index 2985f1fa57ce..0f82d968998b 160000 --- a/netbeans +++ b/netbeans @@ -1 +1 @@ -Subproject commit 2985f1fa57ce0c1877bad0f86d5e5504d65f9286 +Subproject commit 0f82d968998b78ef4d323537d1149eb68e747d9a diff --git a/patches/6330.diff b/patches/6330.diff index dc9b931d6aa6..2ebede28f926 100644 --- a/patches/6330.diff +++ b/patches/6330.diff @@ -1,5 +1,5 @@ diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ConnectionSpec.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ConnectionSpec.java -index 9ab389564c..9b7c044c81 100644 +index bb8a4e8183..b9662fe6cd 100644 --- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ConnectionSpec.java +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ConnectionSpec.java @@ -25,7 +25,7 @@ import java.io.OutputStream; @@ -86,11 +86,10 @@ index 9ab389564c..9b7c044c81 100644 } catch (IOException ex) { if (isClosed(server)) { break; -@@ -129,26 +152,54 @@ final class ConnectionSpec implements Closeable { - } +@@ -130,25 +153,53 @@ final class ConnectionSpec implements Closeable { }; listeningThread.start(); -- out.write((prefix + " listening at port " + localPort).getBytes()); + out.write((prefix + " listening at port " + localPort + "\n").getBytes()); + StringBuilder message = new StringBuilder(); + message.append(prefix).append(" listening at port ").append(localPort); + if (hash) { @@ -128,7 +127,6 @@ index 9ab389564c..9b7c044c81 100644 + if (hashContent != null) { + for (char c : hashContent) { + byte b = (byte) in.read(); -+ + if (b != c) { + IOException toThrow = new IOException("Hash validation failed!"); + try { @@ -146,7 +144,7 @@ index 9ab389564c..9b7c044c81 100644 connectionObject.getRunningFuture().get(); } catch (IOException | InterruptedException | ExecutionException ex) { diff --git a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/ConnectionSpecTest.java b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/ConnectionSpecTest.java -index 1035cf9bc8..55c0488d03 100644 +index f538c953f6..e008bcda0d 100644 --- a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/ConnectionSpecTest.java +++ b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/ConnectionSpecTest.java @@ -118,7 +118,7 @@ public class ConnectionSpecTest { @@ -154,7 +152,7 @@ index 1035cf9bc8..55c0488d03 100644 ByteArrayOutputStream os = new ByteArrayOutputStream(); conn.prepare("Pipe server", in, os, new LspSession(), ConnectionSpecTest::setCopy, ConnectionSpecTest::copy); - String reply = os.toString("UTF-8"); -+ String reply = os.toString("UTF-8").replaceAll("\n$", ""); ++ String reply = os.toString("UTF-8".replaceAll("\n$", "")); String exp = "Pipe server listening at port "; assertTrue(reply, reply.startsWith(exp)); - int port = Integer.parseInt(reply.substring(exp.length())); + int port = Integer.parseInt(reply.substring(exp.length(), reply.indexOf('\n', exp.length()))); diff --git a/patches/7491.diff b/patches/7491.diff deleted file mode 100644 index a0a90593c788..000000000000 --- a/patches/7491.diff +++ /dev/null @@ -1,1907 +0,0 @@ -diff --git a/java/java.completion/src/org/netbeans/modules/java/completion/BaseTask.java b/java/java.completion/src/org/netbeans/modules/java/completion/BaseTask.java -index 78fdd4caa30d..95ae5235c67a 100644 ---- a/java/java.completion/src/org/netbeans/modules/java/completion/BaseTask.java -+++ b/java/java.completion/src/org/netbeans/modules/java/completion/BaseTask.java -@@ -176,6 +176,7 @@ TokenSequence nextNonWhitespaceToken(TokenSequence ts) - case LINE_COMMENT: - case BLOCK_COMMENT: - case JAVADOC_COMMENT: -+ case JAVADOC_COMMENT_LINE_RUN: - break; - default: - return ts; -@@ -206,6 +207,7 @@ TokenSequence previousNonWhitespaceToken(TokenSequence - case LINE_COMMENT: - case BLOCK_COMMENT: - case JAVADOC_COMMENT: -+ case JAVADOC_COMMENT_LINE_RUN: - break; - default: - return ts; -@@ -427,6 +429,7 @@ private Env getEnvImpl(CompilationController controller, TreePath orig, TreePath - case LINE_COMMENT: - case BLOCK_COMMENT: - case JAVADOC_COMMENT: -+ case JAVADOC_COMMENT_LINE_RUN: - break; - case ARROW: - scope = controller.getTrees().getScope(blockPath); -@@ -456,6 +459,7 @@ private Env getEnvImpl(CompilationController controller, TreePath orig, TreePath - case LINE_COMMENT: - case BLOCK_COMMENT: - case JAVADOC_COMMENT: -+ case JAVADOC_COMMENT_LINE_RUN: - break; - case ARROW: - return new Env(offset, prefix, controller, path, sourcePositions, scope); -diff --git a/java/java.completion/src/org/netbeans/modules/java/completion/JavaCompletionTask.java b/java/java.completion/src/org/netbeans/modules/java/completion/JavaCompletionTask.java -index 9ec7273f86f6..be001bce2358 100644 ---- a/java/java.completion/src/org/netbeans/modules/java/completion/JavaCompletionTask.java -+++ b/java/java.completion/src/org/netbeans/modules/java/completion/JavaCompletionTask.java -@@ -1608,6 +1608,7 @@ private void insideMemberSelect(Env env) throws IOException { - case LINE_COMMENT: - case BLOCK_COMMENT: - case JAVADOC_COMMENT: -+ case JAVADOC_COMMENT_LINE_RUN: - break; - default: - lastNonWhitespaceTokenId = ts.token().id(); -diff --git a/java/java.editor.base/nbproject/project.properties b/java/java.editor.base/nbproject/project.properties -index 1f620a92a638..808db111121e 100644 ---- a/java/java.editor.base/nbproject/project.properties -+++ b/java/java.editor.base/nbproject/project.properties -@@ -16,7 +16,7 @@ - # under the License. - spec.version.base=2.90.0 - is.autoload=true --javac.source=1.8 -+javac.release=17 - javac.compilerargs=-Xlint -Xlint:-serial - - test.config.semantic.includes=\ -diff --git a/java/java.editor.base/src/org/netbeans/modules/java/editor/base/javadoc/JavadocCompletionUtils.java b/java/java.editor.base/src/org/netbeans/modules/java/editor/base/javadoc/JavadocCompletionUtils.java -index a0682653d7f6..0bf0f69914db 100644 ---- a/java/java.editor.base/src/org/netbeans/modules/java/editor/base/javadoc/JavadocCompletionUtils.java -+++ b/java/java.editor.base/src/org/netbeans/modules/java/editor/base/javadoc/JavadocCompletionUtils.java -@@ -51,7 +51,7 @@ - */ - public final class JavadocCompletionUtils { - -- static final Pattern JAVADOC_LINE_BREAK = Pattern.compile("\\n[ \\t]*\\**[ \\t]*\\z"); // NOI18N -+ static final Pattern JAVADOC_LINE_BREAK = Pattern.compile("(\\n[ \\t]*\\**[ \\t]*\\z)|(\\n[ \\t]*///[ \\t]*\\z)"); // NOI18N - static final Pattern JAVADOC_WHITE_SPACE = Pattern.compile("[^ \\t]"); // NOI18N - /** - * javadoc parser considers whatever number of spaces or standalone newline -@@ -62,7 +62,7 @@ public final class JavadocCompletionUtils { - static final Pattern JAVADOC_EMPTY = Pattern.compile("(\\s*\\**\\s*\n)*\\s*\\**\\s*\\**"); // NOI18N - static final Pattern JAVADOC_FIRST_WHITE_SPACE = Pattern.compile("[ \\t]*\\**[ \\t]*"); // NOI18N - private static Set IGNORE_TOKES = EnumSet.of( -- JavaTokenId.WHITESPACE, JavaTokenId.BLOCK_COMMENT, JavaTokenId.LINE_COMMENT); -+ JavaTokenId.WHITESPACE, JavaTokenId.BLOCK_COMMENT, JavaTokenId.LINE_COMMENT, JavaTokenId.JAVADOC_COMMENT_LINE_RUN); - private static final Logger LOGGER = Logger.getLogger(JavadocCompletionUtils.class.getName()); - - /** -@@ -196,6 +196,7 @@ public static TokenSequence findJavadocTokenSequence(Compilation - break; - } - case JAVADOC_COMMENT: -+ case JAVADOC_COMMENT_LINE_RUN: - if (token.partType() == PartType.COMPLETE) { - return javac.getElements().getDocComment(e) == null - ? null : s.embedded(JavadocTokenId.language()); -@@ -246,36 +247,39 @@ static boolean isInsideIndent(Token token, int offset) { - - /** - * Is javadoc line break? -- * @param token token to test -+ * @param ts a token sequence positioned to the token to test - * @return {@code true} in case the token is something like {@code "\n\t*"} - */ -- public static boolean isLineBreak(Token token) { -- return isLineBreak(token, token.length()); -+ public static boolean isLineBreak(TokenSequence ts) { -+ return isLineBreak(ts, ts.token().length()); - } - - /** - * Tests if the token part before {@code pos} is a javadoc line break. -- * @param token a token to test -+ * @param ts a token sequence positioned to the token to test - * @param pos position in the token - * @return {@code true} in case the token is something like {@code "\n\t* |\n\t*"} - */ -- public static boolean isLineBreak(Token token, int pos) { -+ public static boolean isLineBreak(TokenSequence ts, int pos) { -+ Token token = ts.token(); -+ - if (token == null || token.id() != JavadocTokenId.OTHER_TEXT) { -- return false; -+ return ts.isEmpty() || ts.index() == 0; - } - try { - CharSequence text = token.text(); - if (pos < token.length()) - text = text.subSequence(0, pos); -- boolean result = pos > 0 -+ boolean result = (pos > 0 - && JAVADOC_LINE_BREAK.matcher(text).find() -- && (pos == token.length() || !isInsideIndent(token, pos)); -+ && (pos == token.length() || !isInsideIndent(token, pos)) -+ ); - return result; - } catch (IndexOutOfBoundsException e) { - throw (IndexOutOfBoundsException) new IndexOutOfBoundsException("pos: " + pos + ", token.length: " + token.length() + ", token text: " + token.text()).initCause(e); - } - } -- -+ - public static boolean isWhiteSpace(CharSequence text) { - return text != null && text.length() > 0 && !JAVADOC_WHITE_SPACE.matcher(text).find(); - } -@@ -437,7 +441,8 @@ private static boolean movedToJavadocToken(TokenSequence ts, int of - return false; - } - -- if (ts.token().id() != JavaTokenId.JAVADOC_COMMENT) { -+ if (ts.token().id() != JavaTokenId.JAVADOC_COMMENT && -+ ts.token().id() != JavaTokenId.JAVADOC_COMMENT_LINE_RUN) { - return false; - } - -@@ -456,6 +461,11 @@ private static boolean isEmptyJavadoc(Token token, int offset) { - // check special case /**|*/ - return offset == 3 && "/***/".contentEquals(text); //NOI18N - } -+ if (token != null && token.id() == JavaTokenId.JAVADOC_COMMENT_LINE_RUN) { -+ CharSequence text = token.text(); -+ // check special case ///|\n -+ return offset == 3 && "///\n".contentEquals(text); //NOI18N -+ } - return false; - } - -diff --git a/java/java.editor.base/src/org/netbeans/modules/java/editor/base/javadoc/JavadocImports.java b/java/java.editor.base/src/org/netbeans/modules/java/editor/base/javadoc/JavadocImports.java -index 07ccd639c0e0..2d91d7065f64 100644 ---- a/java/java.editor.base/src/org/netbeans/modules/java/editor/base/javadoc/JavadocImports.java -+++ b/java/java.editor.base/src/org/netbeans/modules/java/editor/base/javadoc/JavadocImports.java -@@ -641,7 +641,7 @@ public static boolean isInsideReference(TokenSequence jdts, int - } - case OTHER_TEXT: - isBeforeWS |= JavadocCompletionUtils.isWhiteSpace(jdt); -- isBeforeWS |= JavadocCompletionUtils.isLineBreak(jdt); -+ isBeforeWS |= JavadocCompletionUtils.isLineBreak(jdts); - if (isBeforeWS) { - continue; - } else { -@@ -690,7 +690,9 @@ private static TokenSequence getJavadocTS(CompilationInfo javac, - TokenSequence javadoc = null; - TokenSequence ts = SourceUtils.getJavaTokenSequence(javac.getTokenHierarchy(), start); - -- if (ts.moveNext() && ts.token().id() == JavaTokenId.JAVADOC_COMMENT) { -+ if (ts.moveNext() && -+ (ts.token().id() == JavaTokenId.JAVADOC_COMMENT || -+ ts.token().id() == JavaTokenId.JAVADOC_COMMENT_LINE_RUN)) { - javadoc = ts.embedded(JavadocTokenId.language()); - } - -@@ -893,14 +895,14 @@ private static void insideTag(DocTreePath tag, JavadocContext jdctx, int caretOf - cs = pos < cs.length() ? cs.subSequence(0, pos) : cs; - - if (JavadocCompletionUtils.isWhiteSpace(cs) -- || JavadocCompletionUtils.isLineBreak(jdts.token(), pos)) { -+ || JavadocCompletionUtils.isLineBreak(jdts, pos)) { - noPrefix = true; - } else { - // broken syntax - return; - } - } else if (!(JavadocCompletionUtils.isWhiteSpace(jdts.token()) -- || JavadocCompletionUtils.isLineBreak(jdts.token()))) { -+ || JavadocCompletionUtils.isLineBreak(jdts))) { - // not java reference - return; - } else if (jdts.moveNext()) { -diff --git a/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/javadoc/JavadocCompletionUtilsTest.java b/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/javadoc/JavadocCompletionUtilsTest.java -index add1ca7dc20d..33926dc0b405 100644 ---- a/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/javadoc/JavadocCompletionUtilsTest.java -+++ b/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/javadoc/JavadocCompletionUtilsTest.java -@@ -270,32 +270,32 @@ public void testIsLineBreak() throws Exception { - TokenSequence jdts = JavadocCompletionUtils.findJavadocTokenSequence(info, offset); - assertTrue(jdts.moveNext()); - assertTrue(insertPointer(code, offset), -- JavadocCompletionUtils.isLineBreak(jdts.token())); -+ JavadocCompletionUtils.isLineBreak(jdts)); - offset += 1; - jdts = JavadocCompletionUtils.findJavadocTokenSequence(info, offset); - assertTrue(jdts.moveNext()); - // token is INDENT - assertFalse(insertPointer(code, offset), -- JavadocCompletionUtils.isLineBreak(jdts.token())); -+ JavadocCompletionUtils.isLineBreak(jdts)); - - what = " \n"; - offset = code.indexOf(what); - jdts = JavadocCompletionUtils.findJavadocTokenSequence(info, offset); - assertTrue(jdts.moveNext()); - assertTrue(insertPointer(code, offset), -- JavadocCompletionUtils.isLineBreak(jdts.token(), offset - jdts.offset())); -+ JavadocCompletionUtils.isLineBreak(jdts, offset - jdts.offset())); - - what = " * {*i"; - offset = code.indexOf(what) + what.length() - 3; - jdts = JavadocCompletionUtils.findJavadocTokenSequence(info, offset); - assertTrue(jdts.moveNext()); - assertFalse(insertPointer(code, offset), -- JavadocCompletionUtils.isLineBreak(jdts.token())); -+ JavadocCompletionUtils.isLineBreak(jdts)); - assertTrue(insertPointer(code, offset), -- JavadocCompletionUtils.isLineBreak(jdts.token(), offset - jdts.offset())); -+ JavadocCompletionUtils.isLineBreak(jdts, offset - jdts.offset())); - offset = code.indexOf(what); - assertFalse(insertPointer(code, offset), -- JavadocCompletionUtils.isLineBreak(jdts.token(), offset - jdts.offset())); -+ JavadocCompletionUtils.isLineBreak(jdts, offset - jdts.offset())); - } - - public void testIsLineBreak2() throws Exception { -@@ -319,10 +319,10 @@ public void testIsLineBreak2() throws Exception { - assertTrue(jdts.moveNext()); - assertTrue(jdts.token().id() == JavadocTokenId.OTHER_TEXT); - assertFalse(insertPointer(code, jdts.offset() + jdts.token().length()), -- JavadocCompletionUtils.isLineBreak(jdts.token())); -+ JavadocCompletionUtils.isLineBreak(jdts)); - // test OTHER_TEXT(' * |{') - assertTrue(insertPointer(code, jdts.offset() + jdts.token().length() - 1), -- JavadocCompletionUtils.isLineBreak(jdts.token(), jdts.token().length() - 1)); -+ JavadocCompletionUtils.isLineBreak(jdts, jdts.token().length() - 1)); - } - - public void testIsWhiteSpace() throws Exception { -diff --git a/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/javadoc/JavadocImportsTest.java b/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/javadoc/JavadocImportsTest.java -index e64ec4698b52..a880930c5850 100644 ---- a/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/javadoc/JavadocImportsTest.java -+++ b/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/javadoc/JavadocImportsTest.java -@@ -207,6 +207,124 @@ public void testComputeReferencedElements() throws Exception { - assertEquals(exp, sortedResult); - } - -+ public void testComputeReferencedElementsMarkdown() throws Exception { -+ String code = -+ """ -+ package p; -+ import java.io.IOException; -+ import java.util.Collections; -+ import java.util.List; -+ class C { -+ ///link1 {@link Runnable} -+ ///link3 {@linkplain Collections#binarySearch(java.util.List, Object) search} -+ ///{@link java. uncomplete reference} -+ ///unclosed link {@value Math#PI} -+ ///@see List -+ ///@throws IOException -+ void m() throws java.io.IOException { -+ } -+ /// -+ ///{@link Collections} -+ /// -+ int field; -+ /// {@link IOException -+ interface InnerInterface {} -+ /// {@link Collections} -+ @interface InnerAnnotationType {} -+ } -+ /// {@link Collections} -+ enum TopLevelEnum { -+ /** {@link Collections} */ E1 -+ } -+ """; -+ //TODO: does not work: -+ //unclosed link {@value Math#PI\n -+ prepareTest(code); -+ -+ // C.m() -+ TreePath member = findPath(code, "m() throws"); -+ assertNotNull(member); -+ List exp = Arrays.asList( -+ info.getElements().getTypeElement("java.lang.Runnable"), -+ info.getElements().getTypeElement("java.lang.Math"), -+ info.getElements().getTypeElement("java.lang.Object"), -+ info.getElements().getTypeElement("java.util.Collections"), -+ info.getElements().getTypeElement("java.util.List"), -+ info.getElements().getTypeElement("java.io.IOException") -+ ); -+ Collections.sort(exp, new ElementComparator()); -+ Set result = JavadocImports.computeReferencedElements(info, member); -+ assertNotNull(result); -+ List sortedResult = new ArrayList(result); -+ sortedResult.sort(new ElementComparator()); -+ assertEquals(exp, sortedResult); -+ -+ // C.field -+ member = findPath(code, "field;"); -+ assertNotNull(member); -+ exp = Arrays.asList( -+ info.getElements().getTypeElement("java.util.Collections") -+ ); -+ Collections.sort(exp, new ElementComparator()); -+ result = JavadocImports.computeReferencedElements(info, member); -+ assertNotNull(result); -+ sortedResult = new ArrayList(result); -+ sortedResult.sort(new ElementComparator()); -+ assertEquals(exp, sortedResult); -+ -+ // C.InnerInterface -+ member = findPath(code, "InnerInterface {"); -+ assertNotNull(member); -+ exp = Arrays.asList( -+ info.getElements().getTypeElement("java.io.IOException") -+ ); -+ Collections.sort(exp, new ElementComparator()); -+ result = JavadocImports.computeReferencedElements(info, member); -+ assertNotNull(result); -+ sortedResult = new ArrayList(result); -+ sortedResult.sort(new ElementComparator()); -+ assertEquals(exp, sortedResult); -+ -+ // C.InnerAnnotationType -+ member = findPath(code, "InnerAnnotationType {"); -+ assertNotNull(member); -+ exp = Arrays.asList( -+ info.getElements().getTypeElement("java.util.Collections") -+ ); -+ Collections.sort(exp, new ElementComparator()); -+ result = JavadocImports.computeReferencedElements(info, member); -+ assertNotNull(result); -+ sortedResult = new ArrayList(result); -+ sortedResult.sort(new ElementComparator()); -+ assertEquals(exp, sortedResult); -+ -+ // TopLevelEnum -+ member = findPath(code, "TopLevelEnum {"); -+ assertNotNull(member); -+ exp = Arrays.asList( -+ info.getElements().getTypeElement("java.util.Collections") -+ ); -+ Collections.sort(exp, new ElementComparator()); -+ result = JavadocImports.computeReferencedElements(info, member); -+ assertNotNull(result); -+ sortedResult = new ArrayList(result); -+ sortedResult.sort(new ElementComparator()); -+ assertEquals(exp, sortedResult); -+ -+ // TopLevelEnum.E1 -+ member = findPath(code, "E1\n"); -+ assertNotNull(member); -+ exp = Arrays.asList( -+ info.getElements().getTypeElement("java.util.Collections") -+ ); -+ Collections.sort(exp, new ElementComparator()); -+ result = JavadocImports.computeReferencedElements(info, member); -+ assertNotNull(result); -+ sortedResult = new ArrayList(result); -+ sortedResult.sort(new ElementComparator()); -+ assertEquals(exp, sortedResult); -+ } -+ - public void testComputeTokensOfReferencedElements() throws Exception { - String code = - "package p;\n" + -@@ -286,6 +404,87 @@ public void testComputeTokensOfReferencedElements() throws Exception { - // assertEquals(toFind.toString(), exp, tokens); - } - -+ public void testComputeTokensOfReferencedElementsMarkdown() throws Exception { -+ String code = -+ """ -+ package p; -+ import java.util.Collections; -+ class C { -+ ///link1 {@link Runnable} -+ ///link2 {@link Collections#binarySearch(java.util.List, java.lang.Object) search} -+ ///{@link java. uncomplete reference} ///unclosed link {@value Math#PI} -+ ///@see java.util.Collections -+ ///@throws ThrowsUnresolved -+ /// -+ void m() throws java.io.IOException { -+ Collections.binarySearch(Collections.emptyList(), ""); -+ double pi = Math.PI; -+ } -+ } -+ """; -+ //TODO: does not work: -+ //unclosed link {@value Math#PI\n -+ prepareTest(code); -+ -+ TreePath where = findPath(code, "m() throws"); -+ assertNotNull(where); -+ TokenSequence jdts = JavadocCompletionUtils.findJavadocTokenSequence(info, null, info.getTrees().getElement(where)); -+ assertNotNull(jdts); -+ List exp; -+ -+ // toFind java.lang.Runnable -+ Element toFind = info.getElements().getTypeElement("java.lang.Runnable"); -+ assertNotNull(toFind); -+ List tokens = JavadocImports.computeTokensOfReferencedElements(info, where, toFind); -+ assertNotNull(toFind.toString(), tokens); -+ jdts.move(code.indexOf("Runnable", code.indexOf("link1"))); -+ assertTrue(jdts.moveNext()); -+ exp = Arrays.asList(jdts.token()); -+ assertEquals(toFind.toString(), exp, tokens); -+ -+ // toFind java.util.Collections -+ toFind = info.getElements().getTypeElement("java.util.Collections"); -+ assertNotNull(toFind); -+ tokens = JavadocImports.computeTokensOfReferencedElements(info, where, toFind); -+ assertNotNull(toFind.toString(), tokens); -+ exp = new ArrayList(); -+ jdts.move(code.indexOf("Collections", code.indexOf("link2"))); -+ assertTrue(jdts.moveNext()); -+ exp.add(jdts.token()); -+ jdts.move(code.indexOf("Collections", code.indexOf("///@see"))); -+ assertTrue(jdts.moveNext()); -+ exp.add(jdts.token()); -+ System.err.println("exp:"); -+ for (Token e : exp) { -+ System.err.println(e.text()); -+ } -+ System.err.println("tokens:"); -+ for (Token e : tokens) { -+ System.err.println(e.text()); -+ } -+ assertEquals(toFind.toString(), exp, tokens); -+ -+ // toFind Math#PI -+ toFind = findElement(code, "PI;\n"); -+ assertNotNull(toFind); -+ tokens = JavadocImports.computeTokensOfReferencedElements(info, where, toFind); -+ assertNotNull(toFind.toString(), tokens); -+ jdts.move(code.indexOf("PI", code.indexOf("unclosed link"))); -+ assertTrue(jdts.moveNext()); -+ exp = Arrays.asList(jdts.token()); -+ assertEquals(toFind.toString(), exp, tokens); -+ -+ // toFind Collections#binarySearch -+ toFind = findElement(code, "binarySearch(Collections.emptyList()"); -+ assertNotNull(toFind); -+ tokens = JavadocImports.computeTokensOfReferencedElements(info, where, toFind); -+ assertNotNull(toFind.toString(), tokens); -+ jdts.move(code.indexOf("binarySearch", code.indexOf("link2"))); -+ assertTrue(jdts.moveNext()); -+ exp = Arrays.asList(jdts.token()); -+// assertEquals(toFind.toString(), exp, tokens); -+ } -+ - public void testComputeTokensOfReferencedElementsForParams() throws Exception { - String code = - "package p;\n" + -diff --git a/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/javadoc/JavadocTestSupport.java b/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/javadoc/JavadocTestSupport.java -index 7c153124956a..8770a3906721 100644 ---- a/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/javadoc/JavadocTestSupport.java -+++ b/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/javadoc/JavadocTestSupport.java -@@ -20,7 +20,9 @@ - package org.netbeans.modules.java.editor.base.javadoc; - - import java.io.File; -+import java.util.ArrayList; - import java.util.Enumeration; -+import java.util.List; - import javax.swing.text.StyledDocument; - import org.netbeans.api.editor.mimelookup.MimePath; - import org.netbeans.api.editor.mimelookup.test.MockMimeLookup; -@@ -62,10 +64,10 @@ protected void setUp() throws Exception { - super.setUp(); - - MockMimeLookup.setInstances(MimePath.parse("text/x-java"), new JavaKit()); -- SourceUtilsTestUtil.prepareTest(new String[0], new Object[] { -- new Pool(), -- new MockMimeLookup(), -- }); -+ List services = new ArrayList<>(); -+ services.add(new Pool()); -+ services.add(new MockMimeLookup()); -+ SourceUtilsTestUtil.prepareTest(new String[0], services.toArray()); - FileUtil.setMIMEType("java", "text/x-java"); - - if (cache == null) { -@@ -115,7 +117,11 @@ protected void prepareTest(String code) throws Exception { - assertNotNull(info); - assertTrue(info.getDiagnostics().toString(), info.getDiagnostics().isEmpty()); - } -- -+ -+ protected Object[] additionalServices() { -+ return new Object[0]; -+ } -+ - /** - * Inserts a marker '|' to string {@code s} on position {@code pos}. Useful - * for assert's debug messages -diff --git a/java/java.editor/nbproject/project.properties b/java/java.editor/nbproject/project.properties -index 914e09646b80..9c667f21de38 100644 ---- a/java/java.editor/nbproject/project.properties -+++ b/java/java.editor/nbproject/project.properties -@@ -19,7 +19,7 @@ javadoc.title=Java Editor - - spec.version.base=2.93.0 - test.qa-functional.cp.extra=${editor.dir}/modules/org-netbeans-modules-editor-fold.jar --javac.source=1.8 -+javac.release=17 - #test.unit.cp.extra= - #test.unit.run.cp.extra=${o.n.core.dir}/core/core.jar:${o.n.core.dir}/lib/boot.jar:${libs.xerces.dir}/modules/ext/xerces-2.6.2.jar:${libs.xerces.dir}/modules/ext/xml-commons-dom-ranges-1.0.b2.jar:${retouche/javacimpl.dir}/modules/ext/javac-impl.jar - -diff --git a/java/java.editor/src/org/netbeans/modules/editor/java/GoToSupport.java b/java/java.editor/src/org/netbeans/modules/editor/java/GoToSupport.java -index 8a60126a7266..3e8b08e4e7a2 100644 ---- a/java/java.editor/src/org/netbeans/modules/editor/java/GoToSupport.java -+++ b/java/java.editor/src/org/netbeans/modules/editor/java/GoToSupport.java -@@ -408,7 +408,7 @@ public static Context resolveContext(CompilationInfo controller, Document doc, i - boolean insideImportStmt = false; - TreePath path = controller.getTreeUtilities().pathFor(exactOffset); - -- if (token[0] != null && token[0].id() == JavaTokenId.JAVADOC_COMMENT) { -+ if (token[0] != null && (token[0].id() == JavaTokenId.JAVADOC_COMMENT || token[0].id() == JavaTokenId.JAVADOC_COMMENT_LINE_RUN)) { - el = JavadocImports.findReferencedElement(controller, offset); - } else { - path = adjustPathForModuleName(path); -@@ -662,7 +662,7 @@ public void run() { - - Token t = ts.token(); - -- if (JavaTokenId.JAVADOC_COMMENT == t.id()) { -+ if (JavaTokenId.JAVADOC_COMMENT == t.id() || JavaTokenId.JAVADOC_COMMENT_LINE_RUN == t.id()) { - // javadoc hyperlinking (references + param names) - TokenSequence jdts = ts.embedded(JavadocTokenId.language()); - if (JavadocImports.isInsideReference(jdts, offset) || JavadocImports.isInsideParamName(jdts, offset)) { -diff --git a/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionCollector.java b/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionCollector.java -index cd61d9094104..a68dc91894fa 100644 ---- a/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionCollector.java -+++ b/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionCollector.java -@@ -1319,6 +1319,7 @@ private static TokenSequence findLastNonWhitespaceToken(TokenSequen - case LINE_COMMENT: - case BLOCK_COMMENT: - case JAVADOC_COMMENT: -+ case JAVADOC_COMMENT_LINE_RUN: - break; - default: - return ts; -diff --git a/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionItem.java b/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionItem.java -index 733ba9ad3226..47fbd2803f85 100644 ---- a/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionItem.java -+++ b/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionItem.java -@@ -4617,6 +4617,7 @@ private static TokenSequence findLastNonWhitespaceToken(TokenSequen - case LINE_COMMENT: - case BLOCK_COMMENT: - case JAVADOC_COMMENT: -+ case JAVADOC_COMMENT_LINE_RUN: - break; - default: - return ts; -diff --git a/java/java.editor/src/org/netbeans/modules/editor/java/JavaKit.java b/java/java.editor/src/org/netbeans/modules/editor/java/JavaKit.java -index 5ac389c224c5..adefaee33d02 100644 ---- a/java/java.editor/src/org/netbeans/modules/editor/java/JavaKit.java -+++ b/java/java.editor/src/org/netbeans/modules/editor/java/JavaKit.java -@@ -458,6 +458,7 @@ public void insert(MutableContext context) throws BadLocationException { - if (isJavadocTouched) { - blockCommentComplete(doc, dotPos, context); - } -+ TypingCompletion.javadocLineRunCompletion(context); - } - } - -diff --git a/java/java.editor/src/org/netbeans/modules/editor/java/TypingCompletion.java b/java/java.editor/src/org/netbeans/modules/editor/java/TypingCompletion.java -index 5f2172b49d88..0682f52f5f6f 100644 ---- a/java/java.editor/src/org/netbeans/modules/editor/java/TypingCompletion.java -+++ b/java/java.editor/src/org/netbeans/modules/editor/java/TypingCompletion.java -@@ -563,6 +563,21 @@ private static boolean isClosedBlockComment(CharSequence txt, int pos) { - return false; - } - -+ static boolean javadocLineRunCompletion(TypedBreakInterceptor.MutableContext context) { -+ TokenSequence ts = javaTokenSequence(context, false); -+ if (ts == null) { -+ return false; -+ } -+ int dotPosition = context.getCaretOffset(); -+ ts.move(dotPosition); -+ if (!((ts.moveNext() || ts.movePrevious()) && ts.token().id() == JavaTokenId.JAVADOC_COMMENT_LINE_RUN)) { -+ return false; -+ } -+ context.setText("\n///", -1, 4, 0, 4); -+ -+ return false; -+ } -+ - private static boolean isAtRowEnd(CharSequence txt, int pos) { - int length = txt.length(); - for (int i = pos; i < length; i++) { -diff --git a/java/java.editor/src/org/netbeans/modules/editor/java/Utilities.java b/java/java.editor/src/org/netbeans/modules/editor/java/Utilities.java -index da31e094c582..6ccb51a51e3a 100644 ---- a/java/java.editor/src/org/netbeans/modules/editor/java/Utilities.java -+++ b/java/java.editor/src/org/netbeans/modules/editor/java/Utilities.java -@@ -288,6 +288,7 @@ public static boolean isJavaContext(final Document doc, final int offset, final - case INVALID_COMMENT_END: - case JAVADOC_COMMENT: - case LINE_COMMENT: -+ case JAVADOC_COMMENT_LINE_RUN: - case BLOCK_COMMENT: - return false; - case STRING_LITERAL: -diff --git a/java/java.editor/src/org/netbeans/modules/java/editor/javadoc/JavadocCompletionTask.java b/java/java.editor/src/org/netbeans/modules/java/editor/javadoc/JavadocCompletionTask.java -index eac6a5607c25..c328d4ae418d 100644 ---- a/java/java.editor/src/org/netbeans/modules/java/editor/javadoc/JavadocCompletionTask.java -+++ b/java/java.editor/src/org/netbeans/modules/java/editor/javadoc/JavadocCompletionTask.java -@@ -64,6 +64,7 @@ - import javax.swing.text.Document; - import org.netbeans.api.annotations.common.NonNull; - import org.netbeans.api.annotations.common.NullAllowed; -+import org.netbeans.api.java.lexer.JavaTokenId; - import org.netbeans.api.java.lexer.JavadocTokenId; - import org.netbeans.api.java.source.ClassIndex; - import org.netbeans.api.java.source.ClasspathInfo; -@@ -84,6 +85,7 @@ - import org.netbeans.modules.parsing.api.Source; - import org.netbeans.modules.parsing.api.UserTask; - import org.netbeans.modules.parsing.spi.Parser; -+import org.openide.util.Pair; - - public class JavadocCompletionTask extends UserTask { - -@@ -188,16 +190,20 @@ private void analyzeContext(JavadocContext jdctx) { - return; - } - jdts.move(this.caretOffset); -+ JavadocTokenId javadocId; - if (!jdts.moveNext() && !jdts.movePrevious()) { - // XXX solve /***/ - // provide block tags, inline tags, html -- return; -- } -- if (this.caretOffset - jdts.offset() == 0) { -- // if position in token == 0 resolve CC according to previous token -- jdts.movePrevious(); -+ // XXX: for Markdown, continuing -+ javadocId = JavadocTokenId.OTHER_TEXT; -+ } else { -+ if (this.caretOffset - jdts.offset() == 0) { -+ // if position in token == 0 resolve CC according to previous token -+ jdts.movePrevious(); -+ } -+ javadocId = jdts.token().id(); - } -- switch (jdts.token().id()) { -+ switch (javadocId) { - case TAG: - resolveTagToken(jdctx); - break; -@@ -265,7 +271,9 @@ void resolveInlineTag(DocTreePath tag, JavadocContext jdctx) { - - private int skipWhitespacesBackwards(final JavadocContext jdctx, final int offset) { - if (jdctx.jdts.move(offset) == 0 || !jdctx.jdts.moveNext()) { -- jdctx.jdts.movePrevious(); -+ if (!jdctx.jdts.movePrevious()) { -+ return offset; -+ } - } - do { - Token t = jdctx.jdts.token(); -@@ -415,13 +423,13 @@ private void insideSeeTag(DocTreePath tag, JavadocContext jdctx) { - int pos = caretOffset - jdts.offset(); - CharSequence cs = jdts.token().text(); - cs = pos < cs.length() ? cs.subSequence(0, pos) : cs; -- if (JavadocCompletionUtils.isWhiteSpace(cs) || JavadocCompletionUtils.isLineBreak(jdts.token(), pos)) { -+ if (JavadocCompletionUtils.isWhiteSpace(cs) || JavadocCompletionUtils.isLineBreak(jdts, pos)) { - noPrefix = true; - } else { - // broken syntax - return; - } -- } else if (!(JavadocCompletionUtils.isWhiteSpace(jdts.token()) || JavadocCompletionUtils.isLineBreak(jdts.token()))) { -+ } else if (!(JavadocCompletionUtils.isWhiteSpace(jdts.token()) || JavadocCompletionUtils.isLineBreak(jdts))) { - // not java reference - return; - } else if (jdts.moveNext()) { -@@ -519,7 +527,7 @@ private void insideParamTag(DocTreePath tag, JavadocContext jdctx) { - int pos = caretOffset - jdts.offset(); - CharSequence cs = jdts.token().text(); - cs = pos < cs.length() ? cs.subSequence(0, pos) : cs; -- if (JavadocCompletionUtils.isWhiteSpace(cs) || JavadocCompletionUtils.isLineBreak(jdts.token(), pos)) { -+ if (JavadocCompletionUtils.isWhiteSpace(cs) || JavadocCompletionUtils.isLineBreak(jdts, pos)) { - // none prefix - anchorOffset = caretOffset; - completeParamName(tag, "", caretOffset, jdctx); // NOI18N -@@ -1226,11 +1234,10 @@ private boolean startsWith(String theString, String prefix) { - - void resolveOtherText(JavadocContext jdctx, TokenSequence jdts) { - Token token = jdts.token(); -- assert token != null; -- assert token.id() == JavadocTokenId.OTHER_TEXT; -- CharSequence text = token.text(); -- int pos = caretOffset - jdts.offset(); -- DocTreePath tag = getTag(jdctx, caretOffset); -+ assert token == null || token.id() == JavadocTokenId.OTHER_TEXT; -+ CharSequence text = token == null ? "" : token.text(); -+ int pos = token == null ? 0 : caretOffset - jdts.offset(); -+ DocTreePath tag = token == null ? null : getTag(jdctx, caretOffset); - - if (pos > 0 && pos <= text.length() && text.charAt(pos - 1) == '{') { - if (tag != null && !JavadocCompletionUtils.isBlockTag(tag)) { -@@ -1244,10 +1251,10 @@ void resolveOtherText(JavadocContext jdctx, TokenSequence jdts) - } - if (tag != null) { - insideTag(tag, jdctx); -- if (JavadocCompletionUtils.isBlockTag(tag) && JavadocCompletionUtils.isLineBreak(token, pos)) { -+ if (JavadocCompletionUtils.isBlockTag(tag) && JavadocCompletionUtils.isLineBreak(jdts, pos)) { - resolveBlockTag(null, jdctx); - } -- } else if (JavadocCompletionUtils.isLineBreak(token, pos)) { -+ } else if (JavadocCompletionUtils.isLineBreak(jdts, pos)) { - resolveBlockTag(null, jdctx); - } - } -@@ -1336,6 +1343,7 @@ private static class JavadocContext { - private DocCommentTree comment; - private DocSourcePositions positions; - private TokenSequence jdts; -+ private TokenSequence javats; - private Document doc; - private ReferencesCount count; - private TreePath javadocFor; -diff --git a/java/java.editor/test/unit/src/org/netbeans/modules/editor/java/GoToSupportTest.java b/java/java.editor/test/unit/src/org/netbeans/modules/editor/java/GoToSupportTest.java -index 8deac6817b8f..a506190feb74 100644 ---- a/java/java.editor/test/unit/src/org/netbeans/modules/editor/java/GoToSupportTest.java -+++ b/java/java.editor/test/unit/src/org/netbeans/modules/editor/java/GoToSupportTest.java -@@ -1145,6 +1145,71 @@ public void testBindingVarToolTip() throws Exception { - assertEquals("java.lang.String str", tooltip); - } - -+ public void testJavadoc() throws Exception { -+ final boolean[] wasCalled = new boolean[1]; -+ final String code = """ -+ package test; -+ /** -+ * @see Obj|ect -+ */ -+ public class Test { -+ } -+ """; -+ -+ performTest(code, new UiUtilsCaller() { -+ @Override public boolean open(FileObject fo, int pos) { -+ fail("Should not be called."); -+ return true; -+ } -+ -+ @Override public void beep(boolean goToSource, boolean goToJavadoc) { -+ fail("Should not be called."); -+ } -+ @Override public boolean open(ClasspathInfo info, ElementHandle el, String fileName) { -+ assertEquals("java.lang.Object", el.getBinaryName()); -+ wasCalled[0] = true; -+ return true; -+ } -+ @Override public void warnCannotOpen(String displayName) { -+ fail("Should not be called."); -+ } -+ }, false, false); -+ -+ assertTrue(wasCalled[0]); -+ } -+ -+ public void testMarkdownJavadoc() throws Exception { -+ final boolean[] wasCalled = new boolean[1]; -+ this.sourceLevel = "23"; -+ final String code = """ -+ package test; -+ ///@see Obj|ect -+ public class Test { -+ } -+ """; -+ -+ performTest(code, new UiUtilsCaller() { -+ @Override public boolean open(FileObject fo, int pos) { -+ fail("Should not be called."); -+ return true; -+ } -+ -+ @Override public void beep(boolean goToSource, boolean goToJavadoc) { -+ fail("Should not be called."); -+ } -+ @Override public boolean open(ClasspathInfo info, ElementHandle el, String fileName) { -+ assertEquals("java.lang.Object", el.getBinaryName()); -+ wasCalled[0] = true; -+ return true; -+ } -+ @Override public void warnCannotOpen(String displayName) { -+ fail("Should not be called."); -+ } -+ }, false, false); -+ -+ assertTrue(wasCalled[0]); -+ } -+ - private String sourceLevel = "1.5"; - private FileObject source; - -diff --git a/java/java.editor/test/unit/src/org/netbeans/modules/editor/java/TypingCompletionUnitTest.java b/java/java.editor/test/unit/src/org/netbeans/modules/editor/java/TypingCompletionUnitTest.java -index a0713d608f6c..99efb7f02484 100644 ---- a/java/java.editor/test/unit/src/org/netbeans/modules/editor/java/TypingCompletionUnitTest.java -+++ b/java/java.editor/test/unit/src/org/netbeans/modules/editor/java/TypingCompletionUnitTest.java -@@ -1419,6 +1419,22 @@ public void testX() throws Exception { - ctx.assertDocumentTextEquals("{"); - } - -+ public void testJavadocLineRun() { -+ Context ctx = new Context(new JavaKit(), -+ """ -+ class Test { -+ ///| -+ } -+ """); -+ ctx.typeChar('\n'); -+ ctx.assertDocumentTextEquals(""" -+ class Test { -+ /// -+ ///| -+ } -+ """); -+ } -+ - private boolean isInsideString(String code) throws BadLocationException { - int pos = code.indexOf('|'); - -diff --git a/java/java.editor/test/unit/src/org/netbeans/modules/java/editor/javadoc/JavadocCompletionQueryTest.java b/java/java.editor/test/unit/src/org/netbeans/modules/java/editor/javadoc/JavadocCompletionQueryTest.java -index 8bf90d619fab..48f00daca355 100644 ---- a/java/java.editor/test/unit/src/org/netbeans/modules/java/editor/javadoc/JavadocCompletionQueryTest.java -+++ b/java/java.editor/test/unit/src/org/netbeans/modules/java/editor/javadoc/JavadocCompletionQueryTest.java -@@ -23,10 +23,14 @@ - import java.util.Arrays; - import java.util.Collections; - import java.util.List; -+import javax.swing.event.ChangeListener; - import org.netbeans.junit.NbTestSuite; - import org.netbeans.spi.editor.completion.CompletionItem; - import org.netbeans.spi.editor.completion.CompletionProvider; - import org.netbeans.modules.java.editor.base.javadoc.JavadocTestSupport; -+import org.netbeans.spi.java.queries.SourceLevelQueryImplementation2; -+import org.openide.filesystems.FileObject; -+import org.openide.util.lookup.ServiceProvider; - - /** - * -@@ -492,7 +496,7 @@ public void testValue2() throws Exception { - "package p;\n" + - "class Clazz {\n" + - " /**\n" + -- " * {@value Mat|\n" + -+ " * {@value Math|\n" + - " */\n" + - " Clazz() {\n" + - " }\n" + -@@ -601,8 +605,244 @@ public void testSummaryCompletionForMethod() throws Exception { - "}\n"; - performCompletionTest(code, "@summary:"); - } -- -- -+ -+ public void testBlockTagsCompletionInMarkdown() throws Exception { -+ String code = -+ """ -+ package p; -+ class Clazz { -+ /// -+ /// | -+ /// -+ void method(int p1, int p2) { -+ } -+ } -+ """; -+ -+ performCompletionTest(code, "@deprecated:", "@exception:", "@hidden:", "@param:", "@return:", "@see:", "@serialData:", "@since:", "@throws:"); -+ } -+ -+ public void testBlockTagsCompletionInMarkdown2() throws Exception { -+ String code = -+ """ -+ package p; -+ class Clazz { -+ /// -+ ///| -+ /// -+ void method(int p1, int p2) { -+ } -+ } -+ """; -+ -+ performCompletionTest(code, "@deprecated:", "@exception:", "@hidden:", "@param:", "@return:", "@see:", "@serialData:", "@since:", "@throws:"); -+ } -+ -+ public void testBlockTagsCompletionInMarkdown3() throws Exception { -+ String code = -+ """ -+ package p; -+ class Clazz { -+ /// -+ ///|\s -+ /// -+ void method(int p1, int p2) { -+ } -+ } -+ """; -+ -+ performCompletionTest(code, "@deprecated:", "@exception:", "@hidden:", "@param:", "@return:", "@see:", "@serialData:", "@since:", "@throws:"); -+ } -+ -+ public void testBlockTagsCompletionInMarkdownStart() throws Exception { -+ String code = -+ """ -+ package p; -+ class Clazz { -+ ///| -+ void method(int p1, int p2) { -+ } -+ } -+ """; -+ -+ performCompletionTest(code, "@deprecated:", "@exception:", "@hidden:", "@param:", "@return:", "@see:", "@serialData:", "@since:", "@throws:"); -+ } -+ -+ public void testSeeMarkdown1() throws Exception { -+ String code = -+ """ -+ package p; -+ class Clazz { -+ /// -+ /// @see CharSequence#le| -+ /// -+ Clazz() { -+ } -+ } -+ """; -+ -+ performCompletionTest(code, "public abstract int length()"); -+ } -+ -+ public void testSeeMarkdown2() throws Exception { -+ String code = -+ """ -+ package p; -+ class Clazz { -+ /// -+ /// @see | -+ /// -+ Clazz() { -+ } -+ } -+ """; -+ -+ performCompletionTest(code, null, "String", "Clazz"); -+ } -+ -+ public void testSeeMarkdown3() throws Exception { -+ String code = -+ """ -+ package p; -+ class Clazz { -+ ///@param i i -+ ///@see | -+ /// -+ Clazz(int i) { -+ } -+ } -+ """; -+ -+ performCompletionTest(code, null, "String", "Clazz"); -+ } -+ -+ public void testParamMarkdown() throws Exception { -+ String code = -+ """ -+ package p; -+ class Clazz { -+ /// -+ /// @param | -+ /// -+ void method(int p1, int p2) { -+ } -+ } -+ """; -+ -+ performCompletionTest(code, "p1:", "p2:"); -+ } -+ -+ public void testJavadocOldStart1() throws Exception { -+ String code = -+ """ -+ package p; -+ class Clazz { -+ /**| */ -+ void method(int p1, int p2) { -+ } -+ } -+ """; -+ -+ performCompletionTest(code, "@deprecated:", "@exception:", "@hidden:", "@param:", "@return:", "@see:", "@serialData:", "@since:", "@throws:"); -+ } -+ -+ public void testJavadocOldStart2() throws Exception { -+ String code = -+ """ -+ package p; -+ class Clazz { -+ /**@s| */ -+ void method(int p1, int p2) { -+ } -+ } -+ """; -+ -+ performCompletionTest(code, "@see:", "@serialData:", "@since:"); -+ } -+ -+ public void testJavadocOldStart3() throws Exception { -+ String code = -+ """ -+ package p; -+ class Clazz { -+ /**@param | */ -+ void method(int p1, int p2) { -+ } -+ } -+ """; -+ -+ performCompletionTest(code, "p1:", "p2:"); -+ } -+ -+ public void testJavadocOldStart4() throws Exception { -+ String code = -+ """ -+ package p; -+ class Clazz { -+ /**@see | */ -+ void method(int p1, int p2) { -+ } -+ } -+ """; -+ -+ performCompletionTest(code, null, "String", "Clazz"); -+ } -+ -+ public void testJavadocMarkdownStart1() throws Exception { -+ String code = -+ """ -+ package p; -+ class Clazz { -+ ///| -+ void method(int p1, int p2) { -+ } -+ } -+ """; -+ -+ performCompletionTest(code, "@deprecated:", "@exception:", "@hidden:", "@param:", "@return:", "@see:", "@serialData:", "@since:", "@throws:"); -+ } -+ -+ public void testJavadocMarkdownStart2() throws Exception { -+ String code = -+ """ -+ package p; -+ class Clazz { -+ ///@s| -+ void method(int p1, int p2) { -+ } -+ } -+ """; -+ -+ performCompletionTest(code, "@see:", "@serialData:", "@since:"); -+ } -+ -+ public void testJavadocMarkdownStart3() throws Exception { -+ String code = -+ """ -+ package p; -+ class Clazz { -+ ///@param | -+ void method(int p1, int p2) { -+ } -+ } -+ """; -+ -+ performCompletionTest(code, "p1:", "p2:"); -+ } -+ -+ public void testJavadocMarkdownStart4() throws Exception { -+ String code = -+ """ -+ package p; -+ class Clazz { -+ ///@see | -+ void method(int p1, int p2) { -+ } -+ } -+ """; -+ -+ performCompletionTest(code, null, "String", "Clazz"); -+ } - - private static String stripHTML(String from) { - StringBuilder result = new StringBuilder(); -@@ -647,4 +887,30 @@ private void performCompletionTest(String code, String... golden) throws Excepti - assertEquals(goldenList, resultStrings); - } - } -+ -+ @Override -+ protected Object[] additionalServices() { -+ return new Object[] { -+ new SourceLevelQueryImplementation2() { -+ -+ @Override -+ public Result getSourceLevel(FileObject javaFile) { -+ return new Result() { -+ @Override -+ public String getSourceLevel() { -+ return "23"; -+ } -+ -+ @Override -+ public void addChangeListener(ChangeListener listener) { -+ } -+ -+ @Override -+ public void removeChangeListener(ChangeListener listener) { -+ } -+ }; -+ } -+ } -+ }; -+ } - } -diff --git a/java/java.lexer/nbproject/project.properties b/java/java.lexer/nbproject/project.properties -index 4ea9ce2e2612..36e48fd27c57 100644 ---- a/java/java.lexer/nbproject/project.properties -+++ b/java/java.lexer/nbproject/project.properties -@@ -17,7 +17,7 @@ - - is.autoload=true - javac.compilerargs=-Xlint:unchecked --javac.source=1.8 -+javac.release=17 - javadoc.title=Java Lexer API - javadoc.apichanges=${basedir}/apichanges.xml - -diff --git a/java/java.lexer/src/org/netbeans/api/java/lexer/JavaTokenId.java b/java/java.lexer/src/org/netbeans/api/java/lexer/JavaTokenId.java -index 17602a5e400a..25738a8da1ae 100644 ---- a/java/java.lexer/src/org/netbeans/api/java/lexer/JavaTokenId.java -+++ b/java/java.lexer/src/org/netbeans/api/java/lexer/JavaTokenId.java -@@ -193,6 +193,7 @@ public enum JavaTokenId implements TokenId { - LINE_COMMENT(null, "comment"), // Token includes ending new-line - BLOCK_COMMENT(null, "comment"), - JAVADOC_COMMENT(null, "comment"), -+ JAVADOC_COMMENT_LINE_RUN(null, "comment"), // A run of "markdown" javadoc comments, includes ending new-line - - // Errors - INVALID_COMMENT_END("*/", "error"), -@@ -262,6 +263,9 @@ protected LanguageEmbedding embedding( - case JAVADOC_COMMENT: - return LanguageEmbedding.create(JavadocTokenId.language(), 3, - (token.partType() == PartType.COMPLETE) ? 2 : 0); -+ case JAVADOC_COMMENT_LINE_RUN: -+ return LanguageEmbedding.create(JavadocTokenId.language(), 3, -+ (token.partType() == PartType.COMPLETE) ? 1 : 0); - case STRING_LITERAL: - return LanguageEmbedding.create(JavaStringTokenId.language(), 1, - (token.partType() == PartType.COMPLETE) ? 1 : 0); -diff --git a/java/java.lexer/src/org/netbeans/lib/java/lexer/JavaLexer.java b/java/java.lexer/src/org/netbeans/lib/java/lexer/JavaLexer.java -index 49c32619b467..438b8b84ffb3 100644 ---- a/java/java.lexer/src/org/netbeans/lib/java/lexer/JavaLexer.java -+++ b/java/java.lexer/src/org/netbeans/lib/java/lexer/JavaLexer.java -@@ -321,6 +321,13 @@ public Token nextToken() { - case '/': - switch (nextChar()) { - case '/': // in single-line comment -+ switch (nextChar()) { -+ case '/': return finishJavadocLineRun(); -+ case '\r': consumeNewline(); -+ case '\n': -+ case EOF: -+ return token(JavaTokenId.LINE_COMMENT); -+ } - while (true) - switch (nextChar()) { - case '\r': consumeNewline(); -@@ -1428,6 +1435,34 @@ private Token finishFloatExponent() { - return token(JavaTokenId.DOUBLE_LITERAL); - } - } -+ -+ private Token finishJavadocLineRun() { -+ while (true) { -+ //finish current line: -+ LINE: while (true) { -+ switch (nextChar()) { -+ case '\r': consumeNewline(); -+ case '\n': break LINE; -+ case EOF: -+ return token(JavaTokenId.JAVADOC_COMMENT_LINE_RUN); -+ } -+ } -+ -+ //at the next line, if it starts with "///", include it in the run, -+ //otherwise finish the run: -+ int mark = input.readLength(); -+ int c; -+ -+ while (Character.isWhitespace(c = nextChar()) && c != '\r' && c != '\n' && c != EOF) -+ ; -+ -+ if (c != '/' || nextChar() != '/' || nextChar() != '/') { -+ input.backup(input.readLengthEOF()- mark); -+ -+ return token(JavaTokenId.JAVADOC_COMMENT_LINE_RUN); -+ } -+ } -+ } - - private Token token(JavaTokenId id) { - return token(id, PartType.COMPLETE); -diff --git a/java/java.lexer/src/org/netbeans/lib/java/lexer/JavadocLexer.java b/java/java.lexer/src/org/netbeans/lib/java/lexer/JavadocLexer.java -index fa7189b48331..c6ab507fee3f 100644 ---- a/java/java.lexer/src/org/netbeans/lib/java/lexer/JavadocLexer.java -+++ b/java/java.lexer/src/org/netbeans/lib/java/lexer/JavadocLexer.java -@@ -194,6 +194,20 @@ private Token otherText(int ch) { - leftbr = false; - newline = false; - break; -+ case '/': -+ //TODO: check comment type? -+ if (newline) { -+ if (input.read() == '/') { -+ if (input.read() == '/') { -+ break; -+ } else { -+ input.backup(1); -+ } -+ } else { -+ input.backup(1); -+ } -+ newline = false; //for fall-through: -+ } - case '*': - if (newline) { - break; -diff --git a/java/java.lexer/test/unit/src/org/netbeans/lib/java/lexer/JavaLexerBatchTest.java b/java/java.lexer/test/unit/src/org/netbeans/lib/java/lexer/JavaLexerBatchTest.java -index 6402fc5c348f..bf4e0b71a8c2 100644 ---- a/java/java.lexer/test/unit/src/org/netbeans/lib/java/lexer/JavaLexerBatchTest.java -+++ b/java/java.lexer/test/unit/src/org/netbeans/lib/java/lexer/JavaLexerBatchTest.java -@@ -812,4 +812,83 @@ public void testTemplates2() { - assertFalse(ts.moveNext()); - } - -+ public void testMarkdown1() { -+ String text = """ -+ ///test -+ ///@see second line -+ ///third -+ -+ ///another run -+ ///another line -+ -+ """; -+ InputAttributes attr = new InputAttributes(); -+ TokenHierarchy hi = TokenHierarchy.create(text, false, JavaTokenId.language(), EnumSet.noneOf(JavaTokenId.class), attr); -+ TokenSequence ts = hi.tokenSequence(); -+ -+ LexerTestUtilities.assertNextTokenEquals(ts, -+ JavaTokenId.JAVADOC_COMMENT_LINE_RUN, -+ "///test\n" + -+ "///@see second line\n" + -+ "///third\n"); -+ LexerTestUtilities.assertNextTokenEquals(ts, JavaTokenId.WHITESPACE, "\n"); -+ LexerTestUtilities.assertNextTokenEquals(ts, -+ JavaTokenId.JAVADOC_COMMENT_LINE_RUN, -+ "///another run\n" + -+ "///another line\n"); -+ LexerTestUtilities.assertNextTokenEquals(ts, JavaTokenId.WHITESPACE, "\n"); -+ assertFalse(ts.moveNext()); -+ } -+ -+ public void testMarkdown2() { -+ String text = """ -+ ///test -+ ///@see second line -+ ///third -+ -+ ///another run -+ ///another line -+ """; -+ InputAttributes attr = new InputAttributes(); -+ TokenHierarchy hi = TokenHierarchy.create(text, false, JavaTokenId.language(), EnumSet.noneOf(JavaTokenId.class), attr); -+ TokenSequence ts = hi.tokenSequence(); -+ -+ LexerTestUtilities.assertNextTokenEquals(ts, -+ JavaTokenId.JAVADOC_COMMENT_LINE_RUN, -+ "///test\n" + -+ "///@see second line\n" + -+ "///third\n"); -+ LexerTestUtilities.assertNextTokenEquals(ts, JavaTokenId.WHITESPACE, "\n"); -+ LexerTestUtilities.assertNextTokenEquals(ts, -+ JavaTokenId.JAVADOC_COMMENT_LINE_RUN, -+ "///another run\n" + -+ "///another line\n"); -+ assertFalse(ts.moveNext()); -+ } -+ -+ public void testMarkdown3() { -+ String text = """ -+ ///test -+ ///@see second line -+ ///third -+ -+ ///another run -+ ///another line"""; -+ InputAttributes attr = new InputAttributes(); -+ TokenHierarchy hi = TokenHierarchy.create(text, false, JavaTokenId.language(), EnumSet.noneOf(JavaTokenId.class), attr); -+ TokenSequence ts = hi.tokenSequence(); -+ -+ LexerTestUtilities.assertNextTokenEquals(ts, -+ JavaTokenId.JAVADOC_COMMENT_LINE_RUN, -+ "///test\n" + -+ "///@see second line\n" + -+ "///third\n"); -+ LexerTestUtilities.assertNextTokenEquals(ts, JavaTokenId.WHITESPACE, "\n"); -+ LexerTestUtilities.assertNextTokenEquals(ts, -+ JavaTokenId.JAVADOC_COMMENT_LINE_RUN, -+ "///another run\n" + -+ "///another line"); -+ assertFalse(ts.moveNext()); -+ } -+ - } -diff --git a/java/java.lexer/test/unit/src/org/netbeans/lib/java/lexer/JavadocLexerTest.java b/java/java.lexer/test/unit/src/org/netbeans/lib/java/lexer/JavadocLexerTest.java -index 72668f24a651..c0859ba2f826 100644 ---- a/java/java.lexer/test/unit/src/org/netbeans/lib/java/lexer/JavadocLexerTest.java -+++ b/java/java.lexer/test/unit/src/org/netbeans/lib/java/lexer/JavadocLexerTest.java -@@ -143,6 +143,20 @@ public void test233097b() { - LexerTestUtilities.assertNextTokenEquals(ts, JavadocTokenId.OTHER_TEXT, "}"); - } - -+ public void testMarkdown() { -+ String text = "///@see\n/// @see"; -+ -+ TokenHierarchy hi = TokenHierarchy.create(text, JavadocTokenId.language()); -+ TokenSequence ts = hi.tokenSequence(); -+ -+ LexerTestUtilities.assertNextTokenEquals(ts, JavadocTokenId.OTHER_TEXT, "///"); -+ LexerTestUtilities.assertNextTokenEquals(ts, JavadocTokenId.TAG, "@see"); -+ LexerTestUtilities.assertNextTokenEquals(ts, JavadocTokenId.OTHER_TEXT, "\n/// "); -+ LexerTestUtilities.assertNextTokenEquals(ts, JavadocTokenId.TAG, "@see"); -+ -+ assertFalse(ts.moveNext()); -+ } -+ - // public void testModification1() throws Exception { - // PlainDocument doc = new PlainDocument(); - // doc.putProperty(Language.class, JavadocTokenId.language()); -diff --git a/java/java.lsp.server/licenseinfo.xml b/java/java.lsp.server/licenseinfo.xml -index fc390b64bbc9..001c83187ebd 100644 ---- a/java/java.lsp.server/licenseinfo.xml -+++ b/java/java.lsp.server/licenseinfo.xml -@@ -50,4 +50,9 @@ - - - -+ -+ vscode/language-configuration.json -+ -+ -+ - -diff --git a/java/java.lsp.server/vscode/language-configuration.json b/java/java.lsp.server/vscode/language-configuration.json -new file mode 100644 -index 000000000000..c6b69e53360e ---- /dev/null -+++ b/java/java.lsp.server/vscode/language-configuration.json -@@ -0,0 +1,111 @@ -+{ -+ "comments": { -+ "lineComment": "//", -+ "blockComment": [ "/*", "*/" ] -+ }, -+ "brackets": [ -+ ["{", "}"], -+ ["[", "]"], -+ ["(", ")"] -+ ], -+ "autoClosingPairs": [ -+ ["{", "}"], -+ ["[", "]"], -+ ["(", ")"], -+ { "open": "\"", "close": "\"", "notIn": ["string"] }, -+ { "open": "'", "close": "'", "notIn": ["string"] }, -+ { "open": "/**", "close": " */", "notIn": ["string"] } -+ ], -+ "surroundingPairs": [ -+ ["{", "}"], -+ ["[", "]"], -+ ["(", ")"], -+ ["\"", "\""], -+ ["'", "'"], -+ ["<", ">"] -+ ], -+ "folding": { -+ "markers": { -+ "start": "^\\s*//\\s*(?:(?:#?region\\b)|(?:))" -+ } -+ }, -+ "onEnterRules": [ -+ { -+ // e.g. /** | */ -+ "beforeText": { -+ "pattern": "^\\s*/\\*\\*(?!/)([^\\*]|\\*(?!/))*$" -+ }, -+ "afterText": { -+ "pattern": "^\\s*\\*/$" -+ }, -+ "action": { -+ "indent": "indentOutdent", -+ "appendText": " * " -+ } -+ }, -+ { -+ // e.g. /** ...| -+ "beforeText": { -+ "pattern": "^\\s*/\\*\\*(?!/)([^\\*]|\\*(?!/))*$" -+ }, -+ "action": { -+ "indent": "none", -+ "appendText": " * " -+ } -+ }, -+ { -+ // e.g. * ...| -+ "beforeText": { -+ "pattern": "^(\\t|[ ])*[ ]\\*([ ]([^\\*]|\\*(?!/))*)?$" -+ }, -+ "previousLineText": { -+ "pattern": "(?=^(\\s*(/\\*\\*|\\*)).*)(?=(?!(\\s*\\*/)))" -+ }, -+ "action": { -+ "indent": "none", -+ "appendText": "* " -+ } -+ }, -+ { -+ // e.g. */| -+ "beforeText": { -+ "pattern": "^(\\t|[ ])*[ ]\\*/\\s*$" -+ }, -+ "action": { -+ "indent": "none", -+ "removeText": 1 -+ } -+ }, -+ { -+ // e.g. *-----*/| -+ "beforeText": { -+ "pattern": "^(\\t|[ ])*[ ]\\*[^/]*\\*/\\s*$" -+ }, -+ "action": { -+ "indent": "none", -+ "removeText": 1 -+ } -+ }, -+ { -+ "beforeText": { -+ "pattern": "^\\s*(\\bcase\\s.+:|\\bdefault:)$" -+ }, -+ "afterText": { -+ "pattern": "^(?!\\s*(\\bcase\\b|\\bdefault\\b))" -+ }, -+ "action": { -+ "indent": "indent" -+ } -+ }, -+ { -+ "beforeText": { -+ "pattern": "^\\s*///.*$" -+ }, -+ "action": { -+ "indent": "none", -+ "appendText": "///" -+ } -+ } -+ ] -+} -diff --git a/java/java.lsp.server/vscode/package.json b/java/java.lsp.server/vscode/package.json -index cad97381ef73..60795163b16a 100644 ---- a/java/java.lsp.server/vscode/package.json -+++ b/java/java.lsp.server/vscode/package.json -@@ -35,6 +35,18 @@ - "main": "./out/extension.js", - "contributes": { - "languages": [ -+ { -+ "id": "java", -+ "extensions": [ -+ ".java", -+ ".jav" -+ ], -+ "aliases": [ -+ "Java", -+ "java" -+ ], -+ "configuration": "./language-configuration.json" -+ }, - { - "id": "javascript", - "mimetypes": [ -diff --git a/java/java.lsp.server/vscode/syntaxes/java.tmLanguage.json b/java/java.lsp.server/vscode/syntaxes/java.tmLanguage.json -index 1357aff68d5f..d990bf88fd93 100644 ---- a/java/java.lsp.server/vscode/syntaxes/java.tmLanguage.json -+++ b/java/java.lsp.server/vscode/syntaxes/java.tmLanguage.json -@@ -655,6 +655,63 @@ - } - } - ] -+ }, -+ { -+ "begin": "^\\s*(///)", -+ "beginCaptures": { -+ "1": { -+ "name": "punctuation.definition.comment.java" -+ } -+ }, -+ "end": "\n", -+ "endCaptures": { -+ "0": { -+ "name": "punctuation.definition.comment.java" -+ } -+ }, -+ "name": "comment.block.javadoc.markdown.java", -+ "patterns": [ -+ { -+ "match": "@(author|deprecated|return|see|serial|since|version)\\b", -+ "name": "keyword.other.documentation.javadoc.java" -+ }, -+ { -+ "match": "(@param)\\s+(\\S+)", -+ "captures": { -+ "1": { -+ "name": "keyword.other.documentation.javadoc.java" -+ }, -+ "2": { -+ "name": "variable.parameter.java" -+ } -+ } -+ }, -+ { -+ "match": "(@(?:exception|throws))\\s+(\\S+)", -+ "captures": { -+ "1": { -+ "name": "keyword.other.documentation.javadoc.java" -+ }, -+ "2": { -+ "name": "entity.name.type.class.java" -+ } -+ } -+ }, -+ { -+ "match": "{(@link)\\s+(\\S+)?#([\\w$]+\\s*\\([^\\(\\)]*\\)).*?}", -+ "captures": { -+ "1": { -+ "name": "keyword.other.documentation.javadoc.java" -+ }, -+ "2": { -+ "name": "entity.name.type.class.java" -+ }, -+ "3": { -+ "name": "variable.parameter.java" -+ } -+ } -+ } -+ ] - } - ] - }, -diff --git a/java/java.source.base/src/org/netbeans/api/java/source/TreeUtilities.java b/java/java.source.base/src/org/netbeans/api/java/source/TreeUtilities.java -index a7453c44778e..bca4cbd46ae0 100644 ---- a/java/java.source.base/src/org/netbeans/api/java/source/TreeUtilities.java -+++ b/java/java.source.base/src/org/netbeans/api/java/source/TreeUtilities.java -@@ -1302,7 +1302,11 @@ public int[] findNameSpan(DocCommentTree docTree, ReferenceTree ref) { - - tokenSequence.move(pos); - -- if (!tokenSequence.moveNext() || tokenSequence.token().id() != JavaTokenId.JAVADOC_COMMENT) return null; -+ if (!tokenSequence.moveNext() || -+ (tokenSequence.token().id() != JavaTokenId.JAVADOC_COMMENT && -+ tokenSequence.token().id() != JavaTokenId.JAVADOC_COMMENT_LINE_RUN)) { -+ return null; -+ } - - TokenSequence jdocTS = tokenSequence.embedded(JavadocTokenId.language()); - -diff --git a/java/java.sourceui/nbproject/project.xml b/java/java.sourceui/nbproject/project.xml -index 9ac80a3c0929..f1bb6375971b 100644 ---- a/java/java.sourceui/nbproject/project.xml -+++ b/java/java.sourceui/nbproject/project.xml -@@ -86,6 +86,14 @@ - - - -+ -+ org.netbeans.libs.flexmark -+ -+ -+ -+ 1.18 -+ -+ - - org.netbeans.libs.javacapi - -diff --git a/java/java.sourceui/src/org/netbeans/api/java/source/ui/ElementJavadoc.java b/java/java.sourceui/src/org/netbeans/api/java/source/ui/ElementJavadoc.java -index fdabe504448e..832e599852c2 100644 ---- a/java/java.sourceui/src/org/netbeans/api/java/source/ui/ElementJavadoc.java -+++ b/java/java.sourceui/src/org/netbeans/api/java/source/ui/ElementJavadoc.java -@@ -32,6 +32,7 @@ - import com.sun.source.doctree.LinkTree; - import com.sun.source.doctree.LiteralTree; - import com.sun.source.doctree.ParamTree; -+import com.sun.source.doctree.RawTextTree; - import com.sun.source.doctree.ReferenceTree; - import com.sun.source.doctree.ReturnTree; - import com.sun.source.doctree.SeeTree; -@@ -119,6 +120,11 @@ - import com.sun.source.tree.ImportTree; - import com.sun.source.tree.Tree; - import com.sun.source.util.JavacTask; -+import com.vladsch.flexmark.ext.tables.TablesExtension; -+import com.vladsch.flexmark.html.HtmlRenderer; -+import com.vladsch.flexmark.parser.Parser; -+import com.vladsch.flexmark.util.data.DataHolder; -+import com.vladsch.flexmark.util.data.MutableDataSet; - import javax.lang.model.element.RecordComponentElement; - import org.netbeans.api.java.queries.SourceLevelQuery; - import org.netbeans.api.java.queries.SourceLevelQuery.Profile; -@@ -1275,7 +1281,7 @@ private String noJavadocFound() { - private StringBuilder inlineTags(List tags, TreePath docPath, DocCommentTree doc, DocTrees trees, CharSequence inherited) { - StringBuilder sb = new StringBuilder(); - Integer snippetCount=0; -- for (DocTree tag : tags) { -+ for (DocTree tag : resolveMarkdown(trees, tags)) { - switch (tag.getKind()) { - case REFERENCE: - ReferenceTree refTag = (ReferenceTree)tag; -@@ -1287,6 +1293,9 @@ private StringBuilder inlineTags(List tags, TreePath docPath, - break; - case LINK: - linkTag = (LinkTree)tag; -+ if (linkTag.getReference() == null) { -+ break; -+ } - sb.append(""); //NOI18N - appendReference(sb, linkTag.getReference(), linkTag.getLabel(), docPath, doc, trees); - sb.append(""); //NOI18N -@@ -1395,6 +1404,53 @@ private StringBuilder inlineTags(List tags, TreePath docPath, - return sb; - } - -+ private static final char REPLACEMENT = '\uFFFD'; -+ private List resolveMarkdown(DocTrees trees, List tags) { -+ if (tags.stream().noneMatch(t -> t.getKind() == DocTree.Kind.MARKDOWN)) { -+ return tags; -+ } -+ -+ StringBuilder markdownSource = new StringBuilder(); -+ List replacements = new ArrayList<>(); -+ -+ for (DocTree t : tags) { -+ if (t.getKind() == DocTree.Kind.MARKDOWN) { -+ markdownSource.append(((RawTextTree) t).getContent()); -+ } else { -+ markdownSource.append(REPLACEMENT); -+ replacements.add(t); -+ } -+ } -+ -+ TablesExtension tablesExtension = TablesExtension.create(); -+ -+ Parser.Builder parserBuilder = Parser.builder(); -+ tablesExtension.extend(parserBuilder); -+ -+ HtmlRenderer.Builder rendererBuilder = HtmlRenderer.builder(); -+ tablesExtension.extend(rendererBuilder, "HTML"); -+ -+ String html = rendererBuilder.build() -+ .render(parserBuilder.build() -+ .parse(markdownSource.toString())); -+ -+ if (html.startsWith("

")) { -+ html = html.substring("

".length()); -+ } -+ html = html.replace("

", ""); -+ List result = new ArrayList<>(); -+ String[] parts = html.split(Pattern.quote("" + REPLACEMENT)); -+ -+ result.add(trees.getDocTreeFactory().newTextTree(parts[0])); -+ -+ for (int i = 1; i < parts.length; i++) { -+ result.add(replacements.get(i - 1)); -+ result.add(trees.getDocTreeFactory().newTextTree(parts[i])); -+ } -+ -+ return result; -+ } -+ - private void processDocSnippet(StringBuilder sb, SnippetTree javadocSnippet, Integer snippetCount, TreePath docPath,DocCommentTree doc, DocTrees trees) { - sb.append("
"); //NOI18N - sb.append("
" //NOI18N -diff --git a/java/java.sourceui/test/unit/src/org/netbeans/api/java/source/ui/ElementJavadocTest.java b/java/java.sourceui/test/unit/src/org/netbeans/api/java/source/ui/ElementJavadocTest.java -new file mode 100644 -index 000000000000..13bee62039d3 ---- /dev/null -+++ b/java/java.sourceui/test/unit/src/org/netbeans/api/java/source/ui/ElementJavadocTest.java -@@ -0,0 +1,164 @@ -+/* -+ * Licensed to the Apache Software Foundation (ASF) under one -+ * or more contributor license agreements. See the NOTICE file -+ * distributed with this work for additional information -+ * regarding copyright ownership. The ASF licenses this file -+ * to you under the Apache License, Version 2.0 (the -+ * "License"); you may not use this file except in compliance -+ * with the License. You may obtain a copy of the License at -+ * -+ * http://www.apache.org/licenses/LICENSE-2.0 -+ * -+ * Unless required by applicable law or agreed to in writing, -+ * software distributed under the License is distributed on an -+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -+ * KIND, either express or implied. See the License for the -+ * specific language governing permissions and limitations -+ * under the License. -+ */ -+package org.netbeans.api.java.source.ui; -+ -+import com.sun.source.util.TreePath; -+import java.io.File; -+import java.util.ArrayList; -+import java.util.List; -+import javax.lang.model.element.Element; -+import javax.swing.text.Document; -+import org.netbeans.api.java.lexer.JavaTokenId; -+import org.netbeans.api.java.source.CompilationInfo; -+import org.netbeans.api.java.source.JavaSource; -+import org.netbeans.api.java.source.JavaSource.Phase; -+import org.netbeans.api.java.source.SourceUtilsTestUtil; -+import org.netbeans.api.java.source.TestUtilities; -+import org.netbeans.api.lexer.Language; -+import org.netbeans.junit.NbTestCase; -+import org.netbeans.modules.java.JavaDataLoader; -+import org.openide.cookies.EditorCookie; -+import org.openide.filesystems.FileObject; -+import org.openide.filesystems.FileUtil; -+import org.openide.loaders.DataObject; -+ -+public class ElementJavadocTest extends NbTestCase { -+ -+ private static final String CARET_MARK = ""; -+ -+ public ElementJavadocTest(String testName) { -+ super(testName); -+ } -+ -+ private void prepareTest(String fileName, String code) throws Exception { -+ int pos = code.indexOf(CARET_MARK); -+ -+ if (pos == (-1)) { -+ throw new AssertionError("Does not have caret position!"); -+ } -+ -+ code = code.substring(0, pos) + code.substring(pos + CARET_MARK.length()); -+ -+ List extras = new ArrayList<>(); -+ extras.add(JavaDataLoader.class); -+ SourceUtilsTestUtil.prepareTest(new String[] { -+ "org/netbeans/modules/java/platform/resources/layer.xml", -+ "org/netbeans/modules/java/j2seplatform/resources/layer.xml" -+ }, -+ extras.toArray(new Object[0]) -+ ); -+ -+ clearWorkDir(); -+ -+ FileUtil.refreshAll(); -+ -+ FileObject workFO = FileUtil.toFileObject(getWorkDir()); -+ -+ assertNotNull(workFO); -+ -+ sourceRoot = workFO.createFolder("src"); -+ -+ FileObject buildRoot = workFO.createFolder("build"); -+ FileObject cache = workFO.createFolder("cache"); -+ -+ FileObject data = FileUtil.createData(sourceRoot, fileName); -+ File dataFile = FileUtil.toFile(data); -+ -+ assertNotNull(dataFile); -+ -+ TestUtilities.copyStringToFile(dataFile, code); -+ -+ SourceUtilsTestUtil.prepareTest(sourceRoot, buildRoot, cache, new FileObject[0]); -+ -+ DataObject od = DataObject.find(data); -+ EditorCookie ec = od.getCookie(EditorCookie.class); -+ -+ assertNotNull(ec); -+ -+ doc = ec.openDocument(); -+ doc.putProperty(Language.class, JavaTokenId.language()); -+ -+ JavaSource js = JavaSource.forFileObject(data); -+ -+ assertNotNull(js); -+ -+ info = SourceUtilsTestUtil.getCompilationInfo(js, Phase.RESOLVED); -+ -+ assertNotNull(info); -+ -+ selectedPath = info.getTreeUtilities().pathFor(pos); -+ selectedElement = info.getTrees().getElement(selectedPath); -+ -+ assertNotNull(selectedElement); -+ } -+ -+ private FileObject sourceRoot; -+ private CompilationInfo info; -+ private Document doc; -+ private TreePath selectedPath; -+ private Element selectedElement; -+ -+ protected void performTest(String fileName, String code, int pos, String format, String golden) throws Exception { -+ prepareTest(fileName, code); -+ -+ TreePath path = info.getTreeUtilities().pathFor(pos); -+ -+ assertEquals(golden, ElementHeaders.getHeader(path, info, format)); -+ } -+ -+ public void testMarkdownTables() throws Exception { -+ prepareTest("test/Test.java", -+ "///| header1 | header2 |\n" + -+ "///|---------|---------|\n" + -+ "///| cr11 | cr12 |\n" + -+ "///| cr21 | cr22 |\n" + -+ "public class Test {\n" + -+ "}\n"); -+ -+ String actualJavadoc = ElementJavadoc.create(info, selectedElement).getText(); -+ String expectedJavadoc = "
public class Test
extends Object

\n" + -+ "\n" + -+ "\n" + -+ "\n" + -+ "\n" + -+ "\n" + -+ "\n" + -+ "\n" + -+ "
header1header2
cr11cr12
cr21cr22
\n" + -+ "

"; -+ -+ assertEquals(expectedJavadoc, actualJavadoc); -+ } -+ -+ public void testLinkNoRef() throws Exception { -+ prepareTest("test/Test.java", -+ "///Hello!\n" + -+ "///{@link }\n" + -+ "public class Test {\n" + -+ "}\n"); -+ -+ String actualJavadoc = ElementJavadoc.create(info, selectedElement).getText(); -+ String expectedJavadoc = "

public class Test
extends Object

Hello!\n" + -+ "\n" + -+ "

"; -+ -+ assertEquals(expectedJavadoc, actualJavadoc); -+ } -+ -+} diff --git a/patches/7641.diff b/patches/7641.diff deleted file mode 100644 index 676c13277efe..000000000000 --- a/patches/7641.diff +++ /dev/null @@ -1,492 +0,0 @@ -diff --git a/java/java.source.base/src/org/netbeans/modules/java/source/save/Reformatter.java b/java/java.source.base/src/org/netbeans/modules/java/source/save/Reformatter.java -index f1239136427f..0727cbe743be 100644 ---- a/java/java.source.base/src/org/netbeans/modules/java/source/save/Reformatter.java -+++ b/java/java.source.base/src/org/netbeans/modules/java/source/save/Reformatter.java -@@ -4776,6 +4776,28 @@ public class Reformatter implements ReformatTask { - } - } - -+ private enum JavadocReformattingState { -+ INITIAL_TEXT, -+ AFTER_PARAM_TAG, -+ PARAM_DESCRIPTION, -+ RETURN_DESCRIPTION, -+ AFTER_THROWS_TAG, -+ EXCEPTION_DESCRIPTION, -+ AFTER_PRE_TAG, -+ AFTER_OTHER_TAG -+ } -+ -+ private enum JavadocReformattingActionType { -+ NONE, -+ ADD_BLANK_LINE, -+ ADD_NEW_LINE, -+ ALIGN_PARAMS, -+ ALIGN_RETURN, -+ ALIGN_EXCEPTIONS, -+ NO_FORMAT, -+ FORMAT -+ } -+ - private void reformatComment() { - if (tokens.token().id() != BLOCK_COMMENT && tokens.token().id() != JAVADOC_COMMENT) - return; -@@ -4788,15 +4810,14 @@ public class Reformatter implements ReformatTask { - return; - } - } -- String text = tokens.token().text().toString(); -- int offset = tokens.offset(); -- LinkedList> marks = new LinkedList>(); -+ final String text = tokens.token().text().toString(); -+ final int offset = tokens.offset(); -+ LinkedList> marks = new LinkedList<>(); - int maxParamNameLength = 0; - int maxExcNameLength = 0; - int initTextEndOffset = Integer.MAX_VALUE; - if (javadocTokens != null) { -- int state = 0; // 0 - initial text, 1 - after param tag, 2 - param description, 3 - return description, -- // 4 - after throws tag, 5 - exception description, 6 - after pre tag, 7 - after other tag -+ JavadocReformattingState state = JavadocReformattingState.INITIAL_TEXT; - int currWSOffset = -1; - int lastWSOffset = -1; - int identStart = -1; -@@ -4806,105 +4827,97 @@ public class Reformatter implements ReformatTask { - boolean insideTag = false; - int nestedParenCnt = 0; - StringBuilder cseq = null; -- Pair toAdd = null; -- Pair nlAdd = null; -+ Pair marker = null; -+ Pair nlAdd = null; - while (javadocTokens.moveNext()) { - switch (javadocTokens.token().id()) { - case TAG: -- toAdd = null; -+ marker = null; - nlAdd = null; - String tokenText = javadocTokens.token().text().toString(); -- int newState; -- if (JDOC_PARAM_TAG.equalsIgnoreCase(tokenText)) { -- newState = 1; -- } else if (JDOC_RETURN_TAG.equalsIgnoreCase(tokenText)) { -- newState = 3; -- } else if (JDOC_THROWS_TAG.equalsIgnoreCase(tokenText) -- || JDOC_EXCEPTION_TAG.equalsIgnoreCase(tokenText)) { -- newState = 4; -- } else if (JDOC_LINK_TAG.equalsIgnoreCase(tokenText) -- || JDOC_LINKPLAIN_TAG.equalsIgnoreCase(tokenText) -- || JDOC_CODE_TAG.equalsIgnoreCase(tokenText) -- || JDOC_SNIPPET_TAG.equalsIgnoreCase(tokenText) -- || JDOC_DOCROOT_TAG.equalsIgnoreCase(tokenText) -- || JDOC_INHERITDOC_TAG.equalsIgnoreCase(tokenText) -- || JDOC_VALUE_TAG.equalsIgnoreCase(tokenText) -- || JDOC_SUMMARY_TAG.equalsIgnoreCase(tokenText) -- || JDOC_LITERAL_TAG.equalsIgnoreCase(tokenText)) { -+ JavadocReformattingState newState; -+ if (hasInlineTagPrefix(text, tokenText, javadocTokens.offset() - offset)) { - insideTag = true; -- addMark(Pair.of(currWSOffset >= 0 ? currWSOffset : javadocTokens.offset() - offset, 5), marks, state); -+ addMark(Pair.of(currWSOffset, JavadocReformattingActionType.NO_FORMAT), marks, state); - lastWSOffset = currWSOffset = -1; - break; -+ } else if (JDOC_PARAM_TAG.equalsIgnoreCase(tokenText)) { -+ newState = JavadocReformattingState.AFTER_PARAM_TAG; -+ } else if (JDOC_RETURN_TAG.equalsIgnoreCase(tokenText)) { -+ newState = JavadocReformattingState.RETURN_DESCRIPTION; -+ } else if (JDOC_THROWS_TAG.equalsIgnoreCase(tokenText) -+ || JDOC_EXCEPTION_TAG.equalsIgnoreCase(tokenText)) { -+ newState = JavadocReformattingState.AFTER_THROWS_TAG; - } else { - if (insideTag) - break; -- newState = 7; -+ newState = JavadocReformattingState.AFTER_OTHER_TAG; - } -- if (lastWSOffset < initTextEndOffset && newState > 0) { -+ if (lastWSOffset < initTextEndOffset && (newState != JavadocReformattingState.INITIAL_TEXT)) { - initTextEndOffset = lastWSOffset; - } - if (currWSOffset >= 0 && afterText) { -- addMark(Pair.of(currWSOffset, state == 0 && cs.blankLineAfterJavadocDescription() -- || state == 2 && newState != 1 && cs.blankLineAfterJavadocParameterDescriptions() -- || state == 3 && cs.blankLineAfterJavadocReturnTag() ? 0 : 1), marks, state); -+ addMark(Pair.of(currWSOffset, state == JavadocReformattingState.INITIAL_TEXT && cs.blankLineAfterJavadocDescription() -+ || state == JavadocReformattingState.PARAM_DESCRIPTION && newState != JavadocReformattingState.AFTER_PARAM_TAG && cs.blankLineAfterJavadocParameterDescriptions() -+ || state == JavadocReformattingState.RETURN_DESCRIPTION && cs.blankLineAfterJavadocReturnTag() ? JavadocReformattingActionType.ADD_BLANK_LINE : JavadocReformattingActionType.ADD_NEW_LINE), marks, state); - } - state = newState; -- if (state == 3 && cs.alignJavadocReturnDescription()) { -- toAdd = Pair.of(javadocTokens.offset() + javadocTokens.token().length() - offset, 3); -+ if (state == JavadocReformattingState.RETURN_DESCRIPTION && cs.alignJavadocReturnDescription()) { -+ marker = Pair.of(javadocTokens.offset() + javadocTokens.token().length() - offset, JavadocReformattingActionType.ALIGN_RETURN); - } - lastWSOffset = currWSOffset = -1; - break; - case IDENT: -- if (toAdd != null) { -- addMark(toAdd, marks, state); -- toAdd = null; -+ if (marker != null) { -+ addMark(marker, marks, state); -+ marker = null; - } - nlAdd = null; -- if (identStart < 0 && (state == 1 || state == 4)) -+ if (identStart < 0 && (state == JavadocReformattingState.AFTER_PARAM_TAG || state == JavadocReformattingState.AFTER_THROWS_TAG)) - identStart = javadocTokens.offset() - offset; - lastWSOffset = currWSOffset = -1; - afterText = true; - break; - case HTML_TAG: -- if (toAdd != null) { -- addMark(toAdd, marks, state); -+ if (marker != null) { -+ addMark(marker, marks, state); - } - nlAdd = null; - tokenText = javadocTokens.token().text().toString(); - if (tokenText.endsWith(">")) { //NOI18N - if (P_TAG.equalsIgnoreCase(tokenText) || END_P_TAG.equalsIgnoreCase(tokenText)) { -- if (currWSOffset >= 0 && currWSOffset > lastAddedNLOffset && (toAdd == null || toAdd.first() < currWSOffset)) { -- addMark(Pair.of(currWSOffset, 1), marks, state); -+ if (currWSOffset >= 0 && currWSOffset > lastAddedNLOffset && (marker == null || marker.first() < currWSOffset)) { -+ addMark(Pair.of(currWSOffset, JavadocReformattingActionType.ADD_NEW_LINE), marks, state); - } - lastAddedNLOffset = javadocTokens.offset() + javadocTokens.token().length() - offset; -- addMark(Pair.of(lastAddedNLOffset, 1), marks, state); -+ addMark(Pair.of(lastAddedNLOffset, JavadocReformattingActionType.ADD_NEW_LINE), marks, state); - afterText = false; - } else if (PRE_TAG.equalsIgnoreCase(tokenText)) { -- if (currWSOffset >= 0 && state == 0 && (toAdd == null || toAdd.first() < currWSOffset)) { -- addMark(Pair.of(currWSOffset, 1), marks, state); -+ if (currWSOffset >= 0 && state == JavadocReformattingState.INITIAL_TEXT && (marker == null || marker.first() < currWSOffset)) { -+ addMark(Pair.of(currWSOffset, JavadocReformattingActionType.ADD_NEW_LINE), marks, state); - } -- addMark(Pair.of(javadocTokens.offset() - offset, 5), marks, state); -- state = 6; -+ addMark(Pair.of(javadocTokens.offset() - offset, JavadocReformattingActionType.NO_FORMAT), marks, state); -+ state = JavadocReformattingState.AFTER_PRE_TAG; - } else if (CODE_TAG.equalsIgnoreCase(tokenText)) { -- addMark(Pair.of(javadocTokens.offset() - offset, 5), marks, state); -+ addMark(Pair.of(javadocTokens.offset() - offset, JavadocReformattingActionType.NO_FORMAT), marks, state); - } else if (PRE_END_TAG.equalsIgnoreCase(tokenText)) { -- state = 0; -- addMark(Pair.of(currWSOffset >= 0 ? currWSOffset : javadocTokens.offset() - offset, 6), marks, state); -+ state = JavadocReformattingState.INITIAL_TEXT; -+ addMark(Pair.of(currWSOffset >= 0 ? currWSOffset : javadocTokens.offset() - offset, JavadocReformattingActionType.FORMAT), marks, state); - } else if (CODE_END_TAG.equalsIgnoreCase(tokenText)) { -- addMark(Pair.of(currWSOffset >= 0 ? currWSOffset : javadocTokens.offset() - offset, 6), marks, state); -+ addMark(Pair.of(currWSOffset >= 0 ? currWSOffset : javadocTokens.offset() - offset, JavadocReformattingActionType.FORMAT), marks, state); - } else { - if (currWSOffset >= 0 && lastNLOffset >= currWSOffset -- && lastAddedNLOffset < currWSOffset && (toAdd == null || toAdd.first() < currWSOffset)) { -- addMark(Pair.of(currWSOffset, 1), marks, state); -+ && lastAddedNLOffset < currWSOffset && (marker == null || marker.first() < currWSOffset)) { -+ addMark(Pair.of(currWSOffset, JavadocReformattingActionType.ADD_NEW_LINE), marks, state); - } -- addMark(Pair.of(javadocTokens.offset() - offset, 5), marks, state); -- addMark(Pair.of(javadocTokens.offset() + javadocTokens.token().length() - offset - 1, 6), marks, state); -- nlAdd = Pair.of(javadocTokens.offset() + javadocTokens.token().length() - offset, 1); -+ addMark(Pair.of(javadocTokens.offset() - offset, JavadocReformattingActionType.NO_FORMAT), marks, state); -+ addMark(Pair.of(javadocTokens.offset() + javadocTokens.token().length() - offset - 1, JavadocReformattingActionType.FORMAT), marks, state); -+ nlAdd = Pair.of(javadocTokens.offset() + javadocTokens.token().length() - offset, JavadocReformattingActionType.ADD_NEW_LINE); - } - } else { - cseq = new StringBuilder(tokenText); - } -- toAdd = null; -+ marker = null; - lastWSOffset = currWSOffset = -1; - break; - case OTHER_TEXT: -@@ -4937,9 +4950,9 @@ public class Reformatter implements ReformatTask { - } else { - nlFollows = false; - if (c != '*') { -- if (toAdd != null) { -- addMark(toAdd, marks, state); -- toAdd = null; -+ if (marker != null) { -+ addMark(marker, marks, state); -+ marker = null; - } else { - addNow = true; - } -@@ -4965,7 +4978,7 @@ public class Reformatter implements ReformatTask { - } - } - if (nlFollows && nlAdd != null) { -- toAdd = nlAdd; -+ marker = nlAdd; - } - nlAdd = null; - if (identStart >= 0) { -@@ -4976,52 +4989,52 @@ public class Reformatter implements ReformatTask { - break; - } - } -- if (state == 1) { -+ if (state == JavadocReformattingState.AFTER_PARAM_TAG) { - if (len > maxParamNameLength) - maxParamNameLength = len; - if (cs.alignJavadocParameterDescriptions()) -- toAdd = Pair.of(identStart + len, 2); -- state = 2; -- } else if (state == 4) { -+ marker = Pair.of(identStart + len, JavadocReformattingActionType.ALIGN_PARAMS); -+ state = JavadocReformattingState.PARAM_DESCRIPTION; -+ } else if (state == JavadocReformattingState.AFTER_THROWS_TAG) { - if (len > maxExcNameLength) - maxExcNameLength = len; - if (cs.alignJavadocExceptionDescriptions()) -- toAdd = Pair.of(identStart + len, 4); -- state = 5; -+ marker = Pair.of(identStart + len, JavadocReformattingActionType.ALIGN_EXCEPTIONS); -+ state = JavadocReformattingState.EXCEPTION_DESCRIPTION; - } -- if (addNow && toAdd != null) { -- addMark(toAdd, marks, state); -- toAdd = null; -+ if (addNow && marker != null) { -+ addMark(marker, marks, state); -+ marker = null; - } - identStart = -1; - } - if (insideTagEndOffset >= 0) { -- addMark(Pair.of(insideTagEndOffset, 6), marks, state); -+ addMark(Pair.of(insideTagEndOffset, JavadocReformattingActionType.FORMAT), marks, state); - } - cseq = null; - break; - default: -- if (toAdd != null) { -- addMark(toAdd, marks, state); -- toAdd = null; -+ if (marker != null) { -+ addMark(marker, marks, state); -+ marker = null; - } - nlAdd = null; - } - } - } -- int checkOffset, actionType; // 0 - add blank line, 1 - add newline, 2 - align params, 3 - align return, -- // 4 - align exceptions, 5 - no format, 6 - format -- Iterator> it = marks.iterator(); -- if (it.hasNext()) { -- Pair next = it.next(); -- checkOffset = next.first(); -+ int markedOffset; -+ JavadocReformattingActionType actionType; -+ Iterator> marksIterator = marks.iterator(); -+ if (marksIterator.hasNext()) { -+ Pair next = marksIterator.next(); -+ markedOffset = next.first(); - actionType = next.second(); - } else { -- checkOffset = Integer.MAX_VALUE; -- actionType = -1; -+ markedOffset = Integer.MAX_VALUE; -+ actionType = JavadocReformattingActionType.NONE; - } -- String indentString = getIndent(); -- String lineStartString = cs.addLeadingStarInComment() ? indentString + SPACE + LEADING_STAR + SPACE : indentString + SPACE; -+ final String indentString = getIndent(); -+ final String lineStartString = cs.addLeadingStarInComment() ? indentString + SPACE + LEADING_STAR + SPACE : indentString + SPACE; - String blankLineString; - int currNWSPos = -1; - int lastNWSPos = -1; -@@ -5100,16 +5113,16 @@ public class Reformatter implements ReformatTask { - } - firstLine = false; - } -- if (i >= checkOffset && actionType == 5) { -+ if (i >= markedOffset && actionType == JavadocReformattingActionType.NO_FORMAT) { - noFormat = true; - align = -1; -- if (it.hasNext()) { -- Pair next = it.next(); -- checkOffset = next.first(); -+ if (marksIterator.hasNext()) { -+ Pair next = marksIterator.next(); -+ markedOffset = next.first(); - actionType = next.second(); - } else { -- checkOffset = Integer.MAX_VALUE; -- actionType = -1; -+ markedOffset = Integer.MAX_VALUE; -+ actionType = JavadocReformattingActionType.NONE; - } - } - } else { -@@ -5123,23 +5136,23 @@ public class Reformatter implements ReformatTask { - if (currNWSPos < 0) { - currNWSPos = i; - } -- if (i >= checkOffset) { -+ if (i >= markedOffset) { - noFormat = false; - switch (actionType) { -- case 0: -+ case ADD_BLANK_LINE: - pendingDiff = new Diff(currWSPos >= 0 ? offset + currWSPos : offset + i, offset + i, NEWLINE + blankLineString + NEWLINE); - lastNewLinePos = i - 1; - preserveNewLines = true; - align = -1; - break; -- case 1: -+ case ADD_NEW_LINE: - pendingDiff = new Diff(currWSPos >= 0 ? offset + currWSPos : offset + i, offset + i, NEWLINE); - lastNewLinePos = i - 1; - preserveNewLines = true; - align = -1; - break; -- case 2: -- col += (maxParamNameLength + lastNWSPos- currWSPos); -+ case ALIGN_PARAMS: -+ col += (maxParamNameLength + lastNWSPos - currWSPos); - align = col; - currWSPos = -1; - if (lastNewLinePos < 0) { -@@ -5151,11 +5164,11 @@ public class Reformatter implements ReformatTask { - } - } - break; -- case 3: -+ case ALIGN_RETURN: - align = col; - break; -- case 4: -- col += (maxExcNameLength + lastNWSPos- currWSPos); -+ case ALIGN_EXCEPTIONS: -+ col += (maxExcNameLength + lastNWSPos - currWSPos); - align = col; - currWSPos = -1; - if (lastNewLinePos < 0) { -@@ -5167,22 +5180,22 @@ public class Reformatter implements ReformatTask { - } - } - break; -- case 5: -+ case NO_FORMAT: - noFormat = true; - if (currWSPos > 0) - lastWSPos = currWSPos; - break; -- case 6: -+ case FORMAT: - preserveNewLines = true; - break; - } -- if (it.hasNext()) { -- Pair next = it.next(); -- checkOffset = next.first(); -+ if (marksIterator.hasNext()) { -+ Pair next = marksIterator.next(); -+ markedOffset = next.first(); - actionType = next.second(); - } else { -- checkOffset = Integer.MAX_VALUE; -- actionType = -1; -+ markedOffset = Integer.MAX_VALUE; -+ actionType = JavadocReformattingActionType.NONE; - } - } - } -@@ -5287,16 +5300,16 @@ public class Reformatter implements ReformatTask { - lastNewLinePos = -1; - break; - } else { -- if (i >= checkOffset && actionType == 6) { -+ if (i >= markedOffset && actionType == JavadocReformattingActionType.FORMAT) { - noFormat = false; - preserveNewLines = true; -- if (it.hasNext()) { -- Pair next = it.next(); -- checkOffset = next.first(); -+ if (marksIterator.hasNext()) { -+ Pair next = marksIterator.next(); -+ markedOffset = next.first(); - actionType = next.second(); - } else { -- checkOffset = Integer.MAX_VALUE; -- actionType = -1; -+ markedOffset = Integer.MAX_VALUE; -+ actionType = JavadocReformattingActionType.NONE; - } - } - if (!cs.addLeadingStarInComment()) { -@@ -5411,8 +5424,17 @@ public class Reformatter implements ReformatTask { - } - } - -- private void addMark(Pair mark, List> marks, int state) { -- if (state != 6) { -+ /** -+ * -+ * @see for more info on inline tags check documentation here. -+ * @return returns true if has inline tag prefix like "{+@tagname" -+ */ -+ private static boolean hasInlineTagPrefix(String commentsText, String tokenText ,int tagTokenStartOffset) { -+ return commentsText.startsWith("{"+tokenText, tagTokenStartOffset-1); -+ } -+ -+ private void addMark(Pair mark, List> marks, JavadocReformattingState state) { -+ if (state != JavadocReformattingState.AFTER_PRE_TAG) { - marks.add(mark); - } - } -diff --git a/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/save/FormatingTest.java b/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/save/FormatingTest.java -index c11be84ffc76..be95039cc4d9 100644 ---- a/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/save/FormatingTest.java -+++ b/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/save/FormatingTest.java -@@ -5201,6 +5201,47 @@ public class FormatingTest extends NbTestCase { - + "}\n"; - reformat(doc, content, golden); - -+ content = "package hierbas.del.litoral;\n" + -+ "\n" + -+ "public class Test{\n" + -+ "/**{@return foo bar method} */ String bar() { \n" + -+ " return null; \n" + -+ " }\n" + -+ "}"; -+ golden ="package hierbas.del.litoral;\n" + -+ "\n" + -+ "public class Test {\n" + -+ "\n" + -+ " /**\n" + -+ " * {@return foo bar method}\n" + -+ " */\n" + -+ " String bar() {\n" + -+ " return null;\n" + -+ " }\n" + -+ "}\n"; -+ reformat(doc, content, golden); -+ -+ content ="package hierbas.del.litoral;\n" + -+ "\n" + -+ "public class Test{\n" + -+ "/** bar method description {@return foo bar method} */ String bar() { \n" + -+ " return null; \n" + -+ " }\n" + -+ "}"; -+ golden ="package hierbas.del.litoral;\n" + -+ "\n" + -+ "public class Test {\n" + -+ "\n" + -+ " /**\n" + -+ " * bar method description\n" + -+ " * {@return foo bar method}\n" + -+ " */\n" + -+ " String bar() {\n" + -+ " return null;\n" + -+ " }\n" + -+ "}\n"; -+ reformat(doc, content, golden); -+ - content = - "package hierbas.del.litoral;\n" - + "\n" diff --git a/patches/7654.diff b/patches/7654.diff deleted file mode 100644 index dee812c8dac7..000000000000 --- a/patches/7654.diff +++ /dev/null @@ -1,379 +0,0 @@ -diff --git a/java/java.completion/src/org/netbeans/modules/java/completion/BaseTask.java b/java/java.completion/src/org/netbeans/modules/java/completion/BaseTask.java -index 78fdd4caa3..f8869882aa 100644 ---- a/java/java.completion/src/org/netbeans/modules/java/completion/BaseTask.java -+++ b/java/java.completion/src/org/netbeans/modules/java/completion/BaseTask.java -@@ -260,6 +260,8 @@ abstract class BaseTask extends UserTask { - && (ts.token().id() == JavaTokenId.IDENTIFIER - || ts.token().id().primaryCategory().startsWith("keyword") || //NOI18N - ts.token().id().primaryCategory().startsWith("string") || //NOI18N -+ ts.token().id().primaryCategory().equals("number") || //NOI18N -+ ts.token().id().primaryCategory().equals("character") || //NOI18N - ts.token().id().primaryCategory().equals("literal"))) { //NOI18N - offset++; - } -@@ -279,6 +281,10 @@ abstract class BaseTask extends UserTask { - treePath = treePath.getParentPath(); - } - } else { -+ TreePath newClassPath = findNewClassForConstructorName(path); -+ if (newClassPath != null) { -+ path = newClassPath; -+ } - if (JavaSource.Phase.RESOLVED.compareTo(controller.getPhase()) > 0) { - LinkedList reversePath = new LinkedList<>(); - TreePath treePath = path; -@@ -299,6 +305,34 @@ abstract class BaseTask extends UserTask { - return new Env(offset, prefix, controller, path, controller.getTrees().getSourcePositions(), null); - } - -+ private TreePath findNewClassForConstructorName(TreePath tp) { -+ if (tp == null) { -+ return null; -+ } -+ -+ TreePath parentPath = tp.getParentPath(); -+ -+ while (parentPath != null) { -+ boolean goUp = false; -+ goUp = goUp || (parentPath.getLeaf().getKind() == Kind.PARAMETERIZED_TYPE && -+ ((ParameterizedTypeTree) parentPath.getLeaf()).getType() == tp.getLeaf()); -+ goUp = goUp || (parentPath.getLeaf().getKind() == Kind.ANNOTATED_TYPE && -+ ((AnnotatedTypeTree) parentPath.getLeaf()).getUnderlyingType() == tp.getLeaf()); -+ if (goUp) { -+ tp = parentPath; -+ parentPath = parentPath.getParentPath(); -+ } else { -+ break; -+ } -+ } -+ -+ if (parentPath != null && parentPath.getLeaf().getKind() == Kind.NEW_CLASS && ((NewClassTree) parentPath.getLeaf()).getIdentifier() == tp.getLeaf()) { -+ return parentPath; -+ } -+ -+ return null; -+ } -+ - private Env getEnvImpl(CompilationController controller, TreePath orig, TreePath path, TreePath pPath, TreePath gpPath, int offset, String prefix, boolean upToOffset) throws IOException { - Tree tree = path != null ? path.getLeaf() : null; - Tree parent = pPath != null ? pPath.getLeaf() : null; -diff --git a/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaDocumentationTaskTest.java b/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaDocumentationTaskTest.java -new file mode 100644 -index 0000000000..2d85997e34 ---- /dev/null -+++ b/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaDocumentationTaskTest.java -@@ -0,0 +1,247 @@ -+/* -+ * Licensed to the Apache Software Foundation (ASF) under one -+ * or more contributor license agreements. See the NOTICE file -+ * distributed with this work for additional information -+ * regarding copyright ownership. The ASF licenses this file -+ * to you under the Apache License, Version 2.0 (the -+ * "License"); you may not use this file except in compliance -+ * with the License. You may obtain a copy of the License at -+ * -+ * http://www.apache.org/licenses/LICENSE-2.0 -+ * -+ * Unless required by applicable law or agreed to in writing, -+ * software distributed under the License is distributed on an -+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -+ * KIND, either express or implied. See the License for the -+ * specific language governing permissions and limitations -+ * under the License. -+ */ -+ -+package org.netbeans.modules.java.completion; -+ -+import java.io.File; -+import java.io.FileWriter; -+import java.io.IOException; -+import java.io.Writer; -+import java.util.*; -+import java.util.concurrent.Callable; -+ -+import javax.lang.model.element.*; -+import javax.lang.model.type.ArrayType; -+import javax.lang.model.type.DeclaredType; -+import javax.lang.model.type.ExecutableType; -+import javax.lang.model.type.TypeKind; -+import javax.lang.model.type.TypeMirror; -+import javax.lang.model.util.Elements; -+import javax.swing.text.Document; -+ -+import org.netbeans.api.java.lexer.JavaTokenId; -+import org.netbeans.api.java.source.*; -+import org.netbeans.api.java.source.support.ReferencesCount; -+import org.netbeans.api.lexer.Language; -+import org.netbeans.modules.parsing.api.ParserManager; -+import org.netbeans.modules.parsing.api.Source; -+import org.openide.LifecycleManager; -+import org.openide.cookies.EditorCookie; -+import org.openide.filesystems.FileObject; -+import org.openide.filesystems.FileUtil; -+import org.openide.loaders.DataObject; -+import org.openide.util.lookup.ServiceProvider; -+import org.openide.xml.EntityCatalog; -+import org.xml.sax.InputSource; -+import org.xml.sax.SAXException; -+ -+/** -+ * -+ * @author Dusan Balek, Jan Lahoda -+ */ -+public class JavaDocumentationTaskTest extends CompletionTestBaseBase { -+ -+ public JavaDocumentationTaskTest(String testName) { -+ super(testName, "org/netbeans/modules/java/completion/JavaDocumentationTaskTest"); -+ } -+ -+ public void testConstructor() throws Exception { -+ performTest("import java.util.*;\n" + -+ "public class Test {\n" + -+ " List l = new Array|List();\n" + -+ "}\n", -+ "", -+ "11", -+ "[java.util.ArrayList, , ()V]"); -+ } -+ -+ public void testConstructorDiamond() throws Exception { -+ performTest("import java.util.*;\n" + -+ "public class Test {\n" + -+ " List l = new Array|List<>();\n" + -+ "}\n", -+ "", -+ "11", -+ "[java.util.ArrayList, , ()V]"); -+ } -+ -+ public void testConstructorTypeParams1() throws Exception { -+ performTest("import java.util.*;\n"+ -+ "public class Test {\n"+ -+ " List l = new Array|List();\n"+ -+ "}\n", -+ "", -+ "11", -+ "[java.util.ArrayList, , ()V]"); -+ } -+ -+ public void testConstructorTypeParams2() throws Exception { -+ performTest("import java.util.*;\n"+ -+ "public class Test {\n"+ -+ " List l = new ArrayList();\n"+ -+ "}\n", -+ "", -+ "11", -+ "[java.lang.String]"); -+ } -+ -+ public void testConstructorAnnotation1() throws Exception { -+ performTest("import java.lang.annotation.ElementType;\n"+ -+ "import java.lang.annotation.Target;\n"+ -+ "import java.util.*;\n"+ -+ "public class Test {\n"+ -+ " List l = new @Ann Array|List();\n"+ -+ "}\n"+ -+ "@Target(ElementType.TYPE_USE)\n"+ -+ "@interface Ann {}\n", -+ "", -+ "11", -+ "[java.util.ArrayList, , ()V]"); -+ } -+ -+ public void testConstructorAnnotation2() throws Exception { -+ performTest("import java.lang.annotation.ElementType;\n"+ -+ "import java.lang.annotation.Target;\n"+ -+ "import java.util.*;\n"+ -+ "public class Test {\n"+ -+ " List l = new @An|n ArrayList();\n"+ -+ "}\n"+ -+ "@Target(ElementType.TYPE_USE)\n"+ -+ "@interface Ann {}\n", -+ "", -+ "11", -+ "[Ann]"); -+ } -+ -+ public void testConstructorAnnotationTypeParams1() throws Exception { -+ performTest("import java.lang.annotation.ElementType;\n"+ -+ "import java.lang.annotation.Target;\n"+ -+ "import java.util.*;\n"+ -+ "public class Test {\n"+ -+ " List l = new @Ann Array|List();\n"+ -+ "}\n"+ -+ "@Target(ElementType.TYPE_USE)\n"+ -+ "@interface Ann {}\n", -+ "", -+ "11", -+ "[java.util.ArrayList, , ()V]"); -+ } -+ -+ public void testConstructorAnnotationTypeParams2() throws Exception { -+ performTest("import java.lang.annotation.ElementType;\n"+ -+ "import java.lang.annotation.Target;\n"+ -+ "import java.util.*;\n"+ -+ "public class Test {\n"+ -+ " List l = new @An|n ArrayList();\n"+ -+ "}\n"+ -+ "@Target(ElementType.TYPE_USE)\n"+ -+ "@interface Ann {}\n", -+ "", -+ "11", -+ "[Ann]"); -+ } -+ -+ public void testConstructorAnnotationTypeParams3() throws Exception { -+ performTest("import java.lang.annotation.ElementType;\n"+ -+ "import java.lang.annotation.Target;\n"+ -+ "import java.util.*;\n"+ -+ "public class Test {\n"+ -+ " List l = new @Ann ArrayList();\n"+ -+ "}\n"+ -+ "@Target(ElementType.TYPE_USE)\n"+ -+ "@interface Ann {}\n", -+ "", -+ "11", -+ "[java.lang.String]"); -+ } -+ -+ public void testConstructorIntegerArgument() throws Exception { -+ performTest("public class Test {\n" + -+ "/**\n"+ -+ "This is constructor level Javadoc\n"+ -+ "**/\n"+ -+ "Test(int i){}\n"+ -+ " public static void main(String[] args) {\n"+ -+ " Test t = new Test(|10000);\n" + -+ " }\n" + -+ "}\n", -+ "", -+ "11", -+ null); -+ } -+ -+ public void testConstructorCharacterArgument() throws Exception { -+ performTest("public class Test {\n" + -+ "/**\n"+ -+ "This is constructor level Javadoc\n"+ -+ "**/\n"+ -+ "Test(char c){}\n"+ -+ " public static void main(String[] args) {\n"+ -+ " Test t = new Test(|'x');\n" + -+ " }\n" + -+ "}\n", -+ "", -+ "11", -+ null); -+ } -+ -+ protected void performTest(String source, String textToInsert, String sourceLevel, String expected) throws Exception { -+ this.sourceLevel.set(sourceLevel); -+ int caretPos = source.indexOf("|"); -+ assertTrue(caretPos != (-1)); -+ String code = source.substring(0, caretPos) + source.substring(caretPos + 1); -+ File testSource = new File(getWorkDir(), "test/Test.java"); -+ testSource.getParentFile().mkdirs(); -+ try (Writer w = new FileWriter(testSource)) { -+ w.write(code); -+ } -+ FileObject testSourceFO = FileUtil.toFileObject(testSource); -+ assertNotNull(testSourceFO); -+ DataObject testSourceDO = DataObject.find(testSourceFO); -+ assertNotNull(testSourceDO); -+ EditorCookie ec = (EditorCookie) testSourceDO.getCookie(EditorCookie.class); -+ assertNotNull(ec); -+ final Document doc = ec.openDocument(); -+ assertNotNull(doc); -+ doc.putProperty(Language.class, JavaTokenId.language()); -+ doc.putProperty("mimeType", "text/x-java"); -+ int textToInsertLength = textToInsert != null ? textToInsert.length() : 0; -+ if (textToInsertLength > 0) { -+ doc.insertString(caretPos, textToInsert, null); -+ } -+ Source s = Source.create(doc); -+ JavaDocumentationTask task = JavaDocumentationTask.create(caretPos + textToInsertLength, null, new StringFactory(), null); -+ ParserManager.parse(Collections.singletonList(s), task); -+ String documentation = task.getDocumentation(); -+ -+ assertEquals(expected, documentation); -+ -+ LifecycleManager.getDefault().saveAll(); -+ } -+ -+ private static class StringFactory implements JavaDocumentationTask.DocumentationFactory { -+ -+ @Override -+ public String create(CompilationInfo compilationInfo, Element element, Callable cancel) { -+ return Arrays.toString(SourceUtils.getJVMSignature(ElementHandle.create(element))); -+ } -+ -+ } -+ -+} -diff --git a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java -index 4fce71a094..684f5db0a2 100644 ---- a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java -+++ b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java -@@ -1362,11 +1362,15 @@ public class ServerTest extends NbTestCase { - File src = new File(getWorkDir(), "Test.java"); - src.getParentFile().mkdirs(); - String code = "/**\n" + -- " * This is a test class with Javadoc.\n" + -+ " * This is a class level Javadoc.\n" + - " */\n" + - "public class Test {\n" + -- " public static void main(String[] args) {\n" + -- " Test t = new Test();\n" + -+ "/**\n"+ -+ "This is constructor level Javadoc\n"+ -+ "**/\n"+ -+ "Test(int i){}\n"+ -+ " public static void main(String[] args) {\n"+ -+ " Test t = new Test(10000);\n" + - " }\n" + - "}\n"; - try (Writer w = new FileWriter(src)) { -@@ -1401,18 +1405,35 @@ public class ServerTest extends NbTestCase { - InitializeResult result = server.initialize(new InitializeParams()).get(); - assertTrue(result.getCapabilities().getHoverProvider().isLeft() && result.getCapabilities().getHoverProvider().getLeft()); - server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(toURI(src), "java", 0, code))); -- Hover hover = server.getTextDocumentService().hover(new HoverParams(new TextDocumentIdentifier(toURI(src)), new Position(5, 10))).get(); -- assertNotNull(hover); -- assertTrue(hover.getContents().isRight()); -- MarkupContent content = hover.getContents().getRight(); -- assertNotNull(content); -- assertEquals(content.getKind(), "markdown"); -- assertEquals(content.getValue(), "```\n" + -+ Hover hoverClass = server.getTextDocumentService().hover(new HoverParams(new TextDocumentIdentifier(toURI(src)), new Position(9, 10))).get(); -+ Hover hoverConstructor = server.getTextDocumentService().hover(new HoverParams(new TextDocumentIdentifier(toURI(src)), new Position(9, 23))).get(); -+ Hover hoverIntegerArgument = server.getTextDocumentService().hover(new HoverParams(new TextDocumentIdentifier(toURI(src)), new Position(9, 26))).get(); -+ assertNotNull(hoverClass); -+ assertNotNull(hoverConstructor); -+ assertNull(hoverIntegerArgument); -+ assertTrue(hoverConstructor.getContents().isRight()); -+ assertTrue(hoverClass.getContents().isRight()); -+ MarkupContent classContent = hoverClass.getContents().getRight(); -+ MarkupContent constructorContent = hoverConstructor.getContents().getRight(); -+ assertNotNull(classContent); -+ assertNotNull(constructorContent); -+ assertEquals(classContent.getKind(), "markdown"); -+ assertEquals(constructorContent.getKind(), "markdown"); -+ assertEquals(classContent.getValue(), "```\n" + - "public class Test\n" + - "extends Object\n" + - "```\n" + - "\n" + -- "This is a test class with Javadoc.\n" + -+ "This is a class level Javadoc.\n" + -+ "\n"); -+ assertEquals(constructorContent.getValue(), -+ "**Test**\n"+ -+ "\n"+ -+ "```\n" + -+ "Test(int i)\n" + -+ "```\n" + -+ "\n" + -+ "This is constructor level Javadoc\n" + - "\n"); - } - diff --git a/patches/7670.diff b/patches/7670.diff deleted file mode 100644 index 486c231ce04f..000000000000 --- a/patches/7670.diff +++ /dev/null @@ -1,2037 +0,0 @@ -diff --git a/java/java.editor.base/src/org/netbeans/modules/java/editor/base/semantic/Utilities.java b/java/java.editor.base/src/org/netbeans/modules/java/editor/base/semantic/Utilities.java -index 0901d96e3fd8..60255382e069 100644 ---- a/java/java.editor.base/src/org/netbeans/modules/java/editor/base/semantic/Utilities.java -+++ b/java/java.editor.base/src/org/netbeans/modules/java/editor/base/semantic/Utilities.java -@@ -268,16 +268,24 @@ private static Token findIdentifierSpanImpl(CompilationInfo info, T - - if (class2Kind.get(MethodTree.class).contains(leaf.getKind())) { - MethodTree method = (MethodTree) leaf; -+ TreePath parentPath = decl.getParentPath(); - List rightTrees = new ArrayList(); - -- rightTrees.addAll(method.getParameters()); -+ boolean ignoreParameters = parentPath.getLeaf().getKind() == Kind.RECORD && -+ !method.getParameters().isEmpty() && -+ info.getTreeUtilities().isSynthetic(new TreePath(decl, method.getParameters().get(0))); -+ -+ if (!ignoreParameters) { -+ rightTrees.addAll(method.getParameters()); -+ } -+ - rightTrees.addAll(method.getThrows()); - rightTrees.add(method.getBody()); - - Name name = method.getName(); - - if (method.getReturnType() == null) -- name = ((ClassTree) decl.getParentPath().getLeaf()).getSimpleName(); -+ name = ((ClassTree) parentPath.getLeaf()).getSimpleName(); - - return findIdentifierSpanImpl(info, leaf, method.getReturnType(), rightTrees, name.toString(), info.getCompilationUnit(), info.getTrees().getSourcePositions()); - } -diff --git a/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/MarkOccDetTest.java b/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/MarkOccDetTest.java -index 29b188e84bab..205680824678 100644 ---- a/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/MarkOccDetTest.java -+++ b/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/MarkOccDetTest.java -@@ -349,6 +349,36 @@ public void testMatchBindings() throws Exception { - "[MARK_OCCURRENCES], 3:30-3:33"); - } - -+ public void testRecordCompactConstructors1() throws Exception { -+ performTest("MatchBindings.java", -+ "public record MatchBindings(String compact) {\n" + -+ " public MatchBindings {\n" + -+ " }\n" + -+ " private static MatchBindings create() {\n" + -+ " return new MatchBindings(null);\n" + -+ " }\n" + -+ "}\n", -+ 1, -+ 20, -+ "[MARK_OCCURRENCES], 1:11-1:24", -+ "[MARK_OCCURRENCES], 4:19-4:32"); -+ } -+ -+ public void testRecordCompactConstructors2() throws Exception { -+ performTest("MatchBindings.java", -+ "public record MatchBindings(String compact) {\n" + -+ " public MatchBindings {\n" + -+ " }\n" + -+ " private static MatchBindings create() {\n" + -+ " return new MatchBindings(null);\n" + -+ " }\n" + -+ "}\n", -+ 4, -+ 20, -+ "[MARK_OCCURRENCES], 1:11-1:24", -+ "[MARK_OCCURRENCES], 4:19-4:32"); -+ } -+ - //Support for exotic identifiers has been removed 6999438 - public void REMOVEDtestExoticIdentifiers1() throws Exception { - performTest("ExoticIdentifier", 3, 43); -diff --git a/java/java.editor/src/org/netbeans/modules/java/editor/rename/InstantRenamePerformer.java b/java/java.editor/src/org/netbeans/modules/java/editor/rename/InstantRenamePerformer.java -index 5e775e157aea..2622a86c9a33 100644 ---- a/java/java.editor/src/org/netbeans/modules/java/editor/rename/InstantRenamePerformer.java -+++ b/java/java.editor/src/org/netbeans/modules/java/editor/rename/InstantRenamePerformer.java -@@ -451,6 +451,9 @@ private static boolean allowInstantRename(CompilationInfo info, Element e, Eleme - return false; - } - } -+ if (info.getElementUtilities().getLinkedRecordElements(e).size() > 1) { -+ return false; -+ } - if (org.netbeans.modules.java.editor.base.semantic.Utilities.isPrivateElement(e)) { - return true; - } -diff --git a/java/java.editor/test/unit/src/org/netbeans/modules/java/editor/rename/InstantRenameActionTest.java b/java/java.editor/test/unit/src/org/netbeans/modules/java/editor/rename/InstantRenameActionTest.java -index fa1439d916c7..ebe1fcedad9e 100644 ---- a/java/java.editor/test/unit/src/org/netbeans/modules/java/editor/rename/InstantRenameActionTest.java -+++ b/java/java.editor/test/unit/src/org/netbeans/modules/java/editor/rename/InstantRenameActionTest.java -@@ -297,6 +297,16 @@ public void testIsInaccessibleOutsideOuterClassForAnnTypeMethodOfPrivateNestedCl - validateChangePoints(changePoints, 87, 93); - } - -+ public void testNoInstanceRenameForRecordComponents() throws Exception { -+ sourceLevel = "17"; -+ -+ boolean[] wasResolved = new boolean[1]; -+ Collection changePoints = performTest("package test; public class Test { private record Rec(String component) { } }", 120 - 55, wasResolved); -+ -+ assertNull(changePoints); -+ assertTrue(wasResolved[0]); -+ } -+ - private void validateChangePoints(Collection changePoints, int... origs) { - Set awaited = new HashSet(); - -@@ -345,6 +355,7 @@ public String toString() { - } - - private FileObject source; -+ private String sourceLevel; - - private Collection performTest(String sourceCode, final int offset, boolean[] wasResolved) throws Exception { - FileObject root = makeScratchDir(this); -@@ -358,7 +369,11 @@ private Collection performTest(String sourceCode, final int offset, boole - writeIntoFile(source, sourceCode); - - SourceUtilsTestUtil.prepareTest(sourceDir, buildDir, cacheDir, new FileObject[0]); -- -+ -+ if (sourceLevel != null) { -+ SourceUtilsTestUtil.setSourceLevel(sourceDir, sourceLevel); -+ } -+ - DataObject od = DataObject.find(source); - EditorCookie ec = od.getCookie(EditorCookie.class); - Document doc = ec.openDocument(); -diff --git a/java/java.editor/test/unit/src/org/netbeans/modules/java/editor/rename/InstantRenamePerformerTest.java b/java/java.editor/test/unit/src/org/netbeans/modules/java/editor/rename/InstantRenamePerformerTest.java -index 4930bfaa9194..cf7697002067 100644 ---- a/java/java.editor/test/unit/src/org/netbeans/modules/java/editor/rename/InstantRenamePerformerTest.java -+++ b/java/java.editor/test/unit/src/org/netbeans/modules/java/editor/rename/InstantRenamePerformerTest.java -@@ -56,7 +56,9 @@ - * @author lahvac - */ - public class InstantRenamePerformerTest extends NbTestCase { -- -+ -+ private String sourceLevel; -+ - public InstantRenamePerformerTest(String testName) { - super(testName); - } -@@ -193,6 +195,13 @@ public void testPatternBinding() throws Exception { - performTest("package test; public class Test { public void test(Object o) {boolean b = o instanceof String s|tr && str.isEmpty(); } }", 117 - 22, ke, "package test; public class Test { public void test(Object o) {boolean b = o instanceof String satr && satr.isEmpty(); } }", true); - } - -+ public void testRecordWithCompactConstructor1() throws Exception { -+ sourceLevel = "17"; -+ -+ KeyEvent ke = new KeyEvent(new JFrame(), KeyEvent.KEY_TYPED, 0, 0, KeyEvent.VK_UNDEFINED, 'e'); -+ performTest("package test; public class Test { private record R|c(String str) { public Rc {} } }", 72 - 22, ke, - 1, "package test; public class Test { private record Rec(String str) { public Rec {} } }", true); -+ } -+ - private void performTest(String sourceCode, int offset, KeyEvent ke, String golden, boolean stillInRename) throws Exception { - performTest(sourceCode, offset, ke, -1, golden, stillInRename); - } -@@ -217,6 +226,9 @@ private void performTest(String sourceCode, int offset, KeyEvent[] kes, int sele - TestUtilities.copyStringToFile(source, sourceCode.replaceFirst(Pattern.quote("|"), "")); - - SourceUtilsTestUtil.prepareTest(sourceDir, buildDir, cacheDir, new FileObject[0]); -+ if (sourceLevel != null) { -+ SourceUtilsTestUtil.setSourceLevel(sourceDir, sourceLevel); -+ } - SourceUtilsTestUtil.compileRecursively(sourceDir); - - DataObject od = DataObject.find(source); -diff --git a/java/java.source.base/apichanges.xml b/java/java.source.base/apichanges.xml -index 8b5551495cf2..820ac01a8ab9 100644 ---- a/java/java.source.base/apichanges.xml -+++ b/java/java.source.base/apichanges.xml -@@ -25,6 +25,18 @@ - Java Source API - - -+ -+ -+

Adding TreeMaker.RecordComponent -+ -+ -+ -+ -+ -+ Adding TreeMaker.RecordComponent and ElementUtilities.getLinkedRecordElements. -+ -+ -+ - - - Adding TreeMaker.StringTemplate -diff --git a/java/java.source.base/nbproject/project.properties b/java/java.source.base/nbproject/project.properties -index 170637154f..7d9fff6702 100644 ---- a/java/java.source.base/nbproject/project.properties -+++ b/java/java.source.base/nbproject/project.properties -@@ -18,12 +18,12 @@ - #javac.compilerargs=-Xlint:unchecked - nbroot=../.. - is.autoload=true --javac.source=1.8 -+javac.release=17 - javadoc.name=Java Source Base - javadoc.title=Java Source Base - javadoc.arch=${basedir}/arch.xml - javadoc.apichanges=${basedir}/apichanges.xml --spec.version.base=2.68.0 -+spec.version.base=2.70.0 - test.qa-functional.cp.extra=${refactoring.java.dir}/modules/ext/nb-javac-api.jar - test.unit.run.cp.extra=${o.n.core.dir}/core/core.jar:\ - ${o.n.core.dir}/lib/boot.jar:\ -diff --git a/java/java.source.base/src/org/netbeans/api/java/source/ElementHandle.java b/java/java.source.base/src/org/netbeans/api/java/source/ElementHandle.java -index c17c623259a9..076e41c446ae 100644 ---- a/java/java.source.base/src/org/netbeans/api/java/source/ElementHandle.java -+++ b/java/java.source.base/src/org/netbeans/api/java/source/ElementHandle.java -@@ -235,7 +235,7 @@ private T resolveImpl (final ModuleElement module, final JavacTaskImpl jt) { - } - case PARAMETER: - { -- assert signatures.length == 3; -+ assert signatures.length == 4; - final Element type = getTypeElementByBinaryName (module, signatures[0], jt); - if (type instanceof TypeElement) { - final List members = type.getEnclosedElements(); -@@ -526,7 +526,12 @@ public static ElementHandle createModuleElementHandle( - ElementKind eek = ee.getKind(); - if (eek == ElementKind.METHOD || eek == ElementKind.CONSTRUCTOR) { - assert ee instanceof ExecutableElement; -- String[] _sigs = ClassFileUtil.createExecutableDescriptor((ExecutableElement)ee); -+ ExecutableElement eel = (ExecutableElement)ee; -+ if (!eel.getParameters().contains(element)) { -+ //may be e.g. a lambda parameter: -+ throw new IllegalArgumentException("Not a parameter for a method or a constructor."); -+ } -+ String[] _sigs = ClassFileUtil.createExecutableDescriptor(eel); - signatures = new String[_sigs.length + 1]; - System.arraycopy(_sigs, 0, signatures, 0, _sigs.length); - signatures[_sigs.length] = element.getSimpleName().toString(); -diff --git a/java/java.source.base/src/org/netbeans/api/java/source/ElementUtilities.java b/java/java.source.base/src/org/netbeans/api/java/source/ElementUtilities.java -index 11001a9037d6..2a178ca9507e 100644 ---- a/java/java.source.base/src/org/netbeans/api/java/source/ElementUtilities.java -+++ b/java/java.source.base/src/org/netbeans/api/java/source/ElementUtilities.java -@@ -60,6 +60,7 @@ - import java.util.ListIterator; - import java.util.Map; - import java.util.Set; -+import java.util.function.Supplier; - - import javax.lang.model.element.Element; - import javax.lang.model.element.ElementKind; -@@ -928,7 +929,143 @@ public ExecutableElement getDescriptorElement(TypeElement origin) { - } - return null; - } -- -+ -+ /** -+ * Find all elements that are linked record elements for the given input. Will -+ * return the record component element, field, accessor method, and canonical constructor -+ * parameters. -+ * -+ * This method can be called on any {@code Element}, and will return a collection -+ * with a single entry if the provided element is not a record element. -+ * -+ * @param forElement for which the linked elements should be found -+ * @return a collection containing the provided element, plus any additional elements -+ * that are linked to it by the Java record specification -+ * @since 2.70 -+ */ -+ public Collection getLinkedRecordElements(Element forElement) { -+ Parameters.notNull("forElement", forElement); -+ -+ TypeElement record = null; -+ Name componentName = null; -+ -+ switch (forElement.getKind()) { -+ case FIELD -> { -+ Element enclosing = forElement.getEnclosingElement(); -+ if (enclosing.getKind() == ElementKind.RECORD) { -+ record = (TypeElement) enclosing; -+ componentName = forElement.getSimpleName(); -+ } -+ } -+ case PARAMETER -> { -+ Element enclosing = forElement.getEnclosingElement(); -+ if (enclosing.getKind() == ElementKind.CONSTRUCTOR) { -+ Element enclosingType = enclosing.getEnclosingElement(); -+ if (enclosingType.getKind() == ElementKind.RECORD) { -+ TypeElement recordType = (TypeElement) enclosingType; -+ ExecutableElement constructor = recordCanonicalConstructor(recordType); -+ if (constructor != null && constructor.equals(enclosing)) { -+ int idx = constructor.getParameters().indexOf(forElement); -+ if (idx >= 0 && idx < recordType.getRecordComponents().size()) { -+ RecordComponentElement component = recordType.getRecordComponents().get(idx); -+ if (component.getSimpleName().equals(forElement.getSimpleName())) { -+ record = recordType; -+ componentName = component.getSimpleName(); -+ } -+ } -+ } -+ } -+ } -+ } -+ case METHOD -> { -+ Element enclosing = forElement.getEnclosingElement(); -+ ExecutableElement method = (ExecutableElement) forElement; -+ if (method.getParameters().isEmpty() && enclosing.getKind() == ElementKind.RECORD) { -+ TypeElement recordType = (TypeElement) enclosing; -+ for (RecordComponentElement component : recordType.getRecordComponents()) { -+ if (forElement.equals(component.getAccessor())) { -+ record = recordType; -+ componentName = component.getSimpleName(); -+ } -+ } -+ } -+ } -+ case RECORD_COMPONENT -> { -+ record = (TypeElement) forElement.getEnclosingElement(); -+ componentName = forElement.getSimpleName(); -+ } -+ } -+ -+ if (record == null) { -+ return Collections.singleton(forElement); -+ } -+ -+ RecordComponentElement component = null; -+ int componentIdx = 0; -+ -+ for (RecordComponentElement c : record.getRecordComponents()) { -+ if (c.getSimpleName().equals(componentName)) { -+ component = c; -+ break; -+ } -+ componentIdx++; -+ } -+ -+ if (component == null) { -+ //erroneous state(?), ignore: -+ return Collections.singleton(forElement); -+ } -+ -+ Set result = new HashSet<>(); -+ -+ result.add(component); -+ result.add(component.getAccessor()); -+ -+ for (Element el : record.getEnclosedElements()) { -+ if (el.getKind() == ElementKind.FIELD && el.getSimpleName().equals(componentName)) { -+ result.add(el); -+ break; -+ } -+ } -+ -+ ExecutableElement canonicalConstructor = recordCanonicalConstructor(record); -+ if (canonicalConstructor != null && componentIdx < canonicalConstructor.getParameters().size()) { -+ result.add(canonicalConstructor.getParameters().get(componentIdx)); -+ } -+ -+ return result; -+ } -+ -+ private ExecutableElement recordCanonicalConstructor(TypeElement recordType) { -+ Supplier fallback = -+ () -> { -+ List recordComponents = recordType.getRecordComponents(); -+ for (ExecutableElement c : ElementFilter.constructorsIn(recordType.getEnclosedElements())) { -+ if (recordComponents.size() == c.getParameters().size()) { -+ Iterator componentIt = recordComponents.iterator(); -+ Iterator parameterIt = c.getParameters().iterator(); -+ boolean componentMatches = true; -+ -+ while (componentIt.hasNext() && parameterIt.hasNext() && componentMatches) { -+ TypeMirror componentType = componentIt.next().asType(); -+ TypeMirror parameterType = parameterIt.next().asType(); -+ -+ componentMatches &= info.getTypes().isSameType(componentType, parameterType); -+ } -+ if (componentMatches) { -+ return c; -+ } -+ } -+ } -+ return null; -+ }; -+ return ElementFilter.constructorsIn(recordType.getEnclosedElements()) -+ .stream() -+ .filter(info.getElements()::isCanonicalConstructor) -+ .findAny() -+ .orElseGet(fallback); -+ } -+ - // private implementation -------------------------------------------------- - - private static final Set NOT_OVERRIDABLE = EnumSet.of(Modifier.STATIC, Modifier.FINAL, Modifier.PRIVATE); -diff --git a/java/java.source.base/src/org/netbeans/api/java/source/TreeMaker.java b/java/java.source.base/src/org/netbeans/api/java/source/TreeMaker.java -index 6b8b05e16e79..4f7d8beb50c9 100644 ---- a/java/java.source.base/src/org/netbeans/api/java/source/TreeMaker.java -+++ b/java/java.source.base/src/org/netbeans/api/java/source/TreeMaker.java -@@ -1277,6 +1277,21 @@ public VariableTree Variable(ModifiersTree modifiers, - return delegate.Variable(modifiers, name, type, initializer); - } - -+ /** -+ * Creates a new VariableTree for a record component. -+ * -+ * @param modifiers the modifiers of this record component. -+ * @param name the name of the record component. -+ * @param type the type of this record component. -+ * @see com.sun.source.tree.VariableTree -+ * @since 2.70 -+ */ -+ public VariableTree RecordComponent(ModifiersTree modifiers, -+ CharSequence name, -+ Tree type) { -+ return delegate.RecordComponent(modifiers, name, type); -+ } -+ - /** - * Creates a new BindingPatternTree. - * @deprecated -diff --git a/java/java.source.base/src/org/netbeans/api/java/source/TreePathHandle.java b/java/java.source.base/src/org/netbeans/api/java/source/TreePathHandle.java -index 286427f834cd..14b097f0afb8 100644 ---- a/java/java.source.base/src/org/netbeans/api/java/source/TreePathHandle.java -+++ b/java/java.source.base/src/org/netbeans/api/java/source/TreePathHandle.java -@@ -41,6 +41,7 @@ - - import javax.lang.model.element.Element; - import javax.lang.model.element.ElementKind; -+import javax.lang.model.element.ExecutableElement; - import javax.lang.model.element.Modifier; - import javax.lang.model.element.Name; - import javax.lang.model.element.TypeElement; -@@ -322,6 +323,14 @@ private static boolean isSupported(Element el) { - case RECORD: - //TODO: record component - return true; -+ case PARAMETER: -+ //only method and constructor parameters supported (not lambda): -+ if (el.getEnclosingElement().getKind() == ElementKind.METHOD || -+ el.getEnclosingElement().getKind() == ElementKind.CONSTRUCTOR) { -+ return ((ExecutableElement) el.getEnclosingElement()).getParameters().contains(el); -+ } else { -+ return false; -+ } - default: - return false; - } -diff --git a/java/java.source.base/src/org/netbeans/api/java/source/TreeUtilities.java b/java/java.source.base/src/org/netbeans/api/java/source/TreeUtilities.java -index a7453c44778e..737c3d6ad0d6 100644 ---- a/java/java.source.base/src/org/netbeans/api/java/source/TreeUtilities.java -+++ b/java/java.source.base/src/org/netbeans/api/java/source/TreeUtilities.java -@@ -185,8 +185,8 @@ public boolean isExpressionStatement(ExpressionTree tree) { - } - - /**Returns whether or not the given tree is synthetic - generated by the parser. -- * Please note that this method does not check trees transitively - a child of a syntetic tree -- * may be considered non-syntetic. -+ * Please note that this method does not check trees transitively - a child of a synthetic tree -+ * may be considered non-synthetic. - * - * @return true if the given tree is synthetic, false otherwise - * @throws NullPointerException if the given tree is null -@@ -210,6 +210,16 @@ public boolean isSynthetic(TreePath path) throws NullPointerException { - return true; - } - } -+ if (path.getLeaf().getKind() == Kind.VARIABLE && -+ path.getParentPath() != null && -+ path.getParentPath().getLeaf().getKind() == Kind.METHOD && -+ path.getParentPath().getParentPath() != null && -+ path.getParentPath().getParentPath().getLeaf().getKind() == Kind.RECORD) { -+ JCMethodDecl m = (JCMethodDecl) path.getParentPath().getLeaf(); -+ if ((m.mods.flags & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0 && m.getParameters().contains(path.getLeaf())) { -+ return true; -+ } -+ } - - path = path.getParentPath(); - } -diff --git a/java/java.source.base/src/org/netbeans/modules/java/source/builder/TreeFactory.java b/java/java.source.base/src/org/netbeans/modules/java/source/builder/TreeFactory.java -index aed7625d6e78..d28e43c61956 100644 ---- a/java/java.source.base/src/org/netbeans/modules/java/source/builder/TreeFactory.java -+++ b/java/java.source.base/src/org/netbeans/modules/java/source/builder/TreeFactory.java -@@ -925,6 +925,15 @@ public VariableTree Variable(ModifiersTree modifiers, - (JCExpression)type, (JCExpression)initializer); - } - -+ public VariableTree RecordComponent(ModifiersTree modifiers, -+ CharSequence name, -+ Tree type) { -+ JCModifiers augmentedModifiers = (JCModifiers) Modifiers(modifiers.getFlags(), modifiers.getAnnotations()); -+ -+ augmentedModifiers.flags |= Flags.RECORD; -+ -+ return Variable(augmentedModifiers, name, type, null); -+ } - public Tree BindingPattern(CharSequence name, - Tree type) { - try { -diff --git a/java/java.source.base/src/org/netbeans/modules/java/source/save/CasualDiff.java b/java/java.source.base/src/org/netbeans/modules/java/source/save/CasualDiff.java -index 08527547a62a..6a561506eaa0 100644 ---- a/java/java.source.base/src/org/netbeans/modules/java/source/save/CasualDiff.java -+++ b/java/java.source.base/src/org/netbeans/modules/java/source/save/CasualDiff.java -@@ -193,7 +193,6 @@ public class CasualDiff { - private final Names names; - private final TreeMaker make; - private static final Logger LOG = Logger.getLogger(CasualDiff.class.getName()); -- public static final int GENERATED_MEMBER = 1<<24; - - private Map diffInfo = new HashMap<>(); - private final Map tree2Tag; -@@ -1008,6 +1007,47 @@ protected int diffClassDef(JCClassDecl oldT, JCClassDecl newT, int[] bounds) { - // it can be > (GT) or >> (SHIFT) - insertHint = tokenSequence.offset() + tokenSequence.token().length(); - } -+ //TODO: class to record and vice versa! -+ if (oldT.getKind() == Kind.RECORD && newT.getKind() == Kind.RECORD) { -+ ComponentsAndOtherMembers oldParts = splitOutRecordComponents(filteredOldTDefs); -+ ComponentsAndOtherMembers newParts = splitOutRecordComponents(filteredNewTDefs); -+ int posHint; -+ if (oldParts.components().isEmpty()) { -+ // compute the position. Find the parameters closing ')', its -+ // start position is important for us. This is used when -+ // there was not any parameter in original tree. -+ int startOffset = oldT.pos; -+ -+ moveFwdToToken(tokenSequence, startOffset, JavaTokenId.RPAREN); -+ posHint = tokenSequence.offset(); -+ } else { -+ // take the position of the first old parameter -+ posHint = oldParts.components.iterator().next().getStartPosition(); -+ } -+ if (!listsMatch(oldParts.components, newParts.components)) { -+ copyTo(localPointer, posHint); -+ int old = printer.setPrec(TreeInfo.noPrec); -+ parameterPrint = true; -+ JCClassDecl oldEnclClass = printer.enclClass; -+ printer.enclClass = null; -+ localPointer = diffParameterList(oldParts.components, newParts.components, null, posHint, Measure.MEMBER); -+ printer.enclClass = oldEnclClass; -+ parameterPrint = false; -+ printer.setPrec(old); -+ } -+ //make sure the ')' is printed: -+ moveFwdToToken(tokenSequence, oldParts.components.isEmpty() ? posHint : endPos(oldParts.components.get(oldParts.components.size() - 1)), JavaTokenId.RPAREN); -+ tokenSequence.moveNext(); -+ posHint = tokenSequence.offset(); -+ if (localPointer < posHint) -+ copyTo(localPointer, localPointer = posHint); -+ filteredOldTDefs = oldParts.defs; -+ filteredNewTDefs = newParts.defs; -+ tokenSequence.move(localPointer); -+ moveToSrcRelevant(tokenSequence, Direction.FORWARD); -+ // it can be > (GT) or >> (SHIFT) -+ insertHint = tokenSequence.offset() + tokenSequence.token().length(); -+ } - switch (getChangeKind(oldT.extending, newT.extending)) { - case NOCHANGE: - insertHint = oldT.extending != null ? endPos(oldT.extending) : insertHint; -@@ -1119,6 +1159,25 @@ protected int diffClassDef(JCClassDecl oldT, JCClassDecl newT, int[] bounds) { - return bounds[1]; - } - -+ private ComponentsAndOtherMembers splitOutRecordComponents(List defs) { -+ ListBuffer components = new ListBuffer<>(); -+ ListBuffer filteredDefs = new ListBuffer<>(); -+ -+ for (JCTree t : defs) { -+ if (t.getKind() == Kind.VARIABLE && -+ (((JCVariableDecl) t).mods.flags & RECORD) != 0) { -+ components.add(t); -+ } else { -+ filteredDefs.add(t); -+ } -+ } -+ -+ return new ComponentsAndOtherMembers(components.toList(), -+ filteredDefs.toList()); -+ } -+ -+ record ComponentsAndOtherMembers(List components, List defs) {} -+ - /** - * When the enumeration contains just methods, it is necessary to preced them with single ;. If a constant is - * inserted, it must be inserted first; and the semicolon should be removed. This method will attempt to remove entire -@@ -4026,11 +4085,7 @@ else if (Kind.VARIABLE == tree.getKind()) { - // collect enum constants, make a field group from them - // and set the flag. - enumConstants.add(var); -- } // filter syntetic member variable, i.e. variable which are in -- // the tree, but not available in the source. -- else if ((var.mods.flags & GENERATED_MEMBER) != 0) -- continue; -- else { -+ } else { - if (!fieldGroup.isEmpty()) { - int oldPos = getOldPos(fieldGroup.get(0)); - -diff --git a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/ElementHandleTest.java b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/ElementHandleTest.java -index 92dcfdb519e4..f3c1cf42735c 100644 ---- a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/ElementHandleTest.java -+++ b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/ElementHandleTest.java -@@ -20,6 +20,7 @@ - package org.netbeans.api.java.source; - - import com.sun.source.tree.NewClassTree; -+import com.sun.source.tree.VariableTree; - import com.sun.source.util.TreePath; - import com.sun.source.util.TreePathScanner; - import com.sun.tools.javac.model.JavacElements; -@@ -39,6 +40,7 @@ - import java.util.Map; - import java.util.Set; - import java.util.concurrent.atomic.AtomicBoolean; -+import java.util.concurrent.atomic.AtomicInteger; - import javax.lang.model.element.Element; - import javax.lang.model.element.ElementKind; - import javax.lang.model.element.ExecutableElement; -@@ -678,6 +680,80 @@ public void testHandleClassBasedCompilations() throws Exception { - SourceLevelQueryImpl.getDefault().setSourceLevel(jlObject, null); - } - -+ public void testMethodParameter1() throws Exception { -+ try (PrintWriter out = new PrintWriter ( new OutputStreamWriter (data.getOutputStream()))) { -+ out.println(""" -+ public class Test { -+ public Test(int cParam) {} -+ public void test(int mParam) { -+ FI fi = lParam -> {}; -+ } -+ interface FI { -+ public void test(int x) {} -+ } -+ } -+ """); -+ } -+ final JavaSource js = JavaSource.create(ClasspathInfo.create(ClassPathProviderImpl.getDefault().findClassPath(data,ClassPath.BOOT), ClassPathProviderImpl.getDefault().findClassPath(data, ClassPath.COMPILE), null), data); -+ assertNotNull(js); -+ AtomicInteger testCount = new AtomicInteger(); -+ -+ js.runUserActionTask(new Task() { -+ public void run(CompilationController parameter) throws Exception { -+ parameter.toPhase(JavaSource.Phase.RESOLVED); -+ new TreePathScanner() { -+ @Override -+ public Void visitVariable(VariableTree node, Void p) { -+ if (node.getName().toString().endsWith("Param")) { -+ Element el = parameter.getTrees().getElement(getCurrentPath()); -+ if (el.getSimpleName().contentEquals("lParam")) { -+ try { -+ ElementHandle.create(el); -+ fail("Expected exception didn't happen!"); -+ } catch (IllegalArgumentException ex) { -+ //OK -+ } -+ } else { -+ assertEquals(el, ElementHandle.create(el).resolve(parameter)); -+ } -+ testCount.incrementAndGet(); -+ } -+ return super.visitVariable(node, p); -+ } -+ }.scan(parameter.getCompilationUnit(), null); -+ } -+ }, true); -+ -+ assertEquals(3, testCount.get()); -+ } -+ -+ public void testMethodParameter2() throws Exception { -+ ClassPath systemClasses = BootClassPathUtil.getModuleBootPath(); -+ ClassPath bcp = BootClassPathUtil.getBootClassPath(); -+ FileObject jlObject = bcp.findResource("java/lang/String.class"); -+ assertNotNull(jlObject); -+ ClasspathInfo cpInfo = new ClasspathInfo.Builder(bcp) -+ .setModuleBootPath(systemClasses) -+ .build(); -+ JavaSource js = JavaSource.create(cpInfo, jlObject); -+ assertNotNull(js); -+ SourceLevelQueryImpl.getDefault().setSourceLevel(jlObject, "11"); -+ js.runUserActionTask(cc -> { -+ cc.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED); -+ TypeElement tl = cc.getTopLevelElements().get(0); -+ for (Element el : tl.getEnclosedElements()) { -+ if (el.getKind() != ElementKind.METHOD && -+ el.getKind() != ElementKind.CONSTRUCTOR) { -+ continue; -+ } -+ for (VariableElement parameter : ((ExecutableElement) el).getParameters()) { -+ assertEquals(parameter, ElementHandle.create(parameter).resolve(cc)); -+ } -+ } -+ }, true); -+ SourceLevelQueryImpl.getDefault().setSourceLevel(jlObject, null); -+ } -+ - private Element[] getStringElements (Element stringElement) { - List members = ((TypeElement)stringElement).getEnclosedElements(); - Element[] result = new Element[3]; -diff --git a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/ElementUtilitiesTest.java b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/ElementUtilitiesTest.java -index 40205da17ee9..745d01003d17 100644 ---- a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/ElementUtilitiesTest.java -+++ b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/ElementUtilitiesTest.java -@@ -31,12 +31,15 @@ - import java.util.HashSet; - import java.util.List; - import java.util.Set; -+import java.util.TreeSet; -+import java.util.stream.Collectors; - import javax.lang.model.element.Element; - import javax.lang.model.element.ElementKind; - import javax.lang.model.element.ExecutableElement; - import javax.lang.model.element.TypeElement; - import javax.lang.model.type.TypeKind; - import javax.lang.model.type.TypeMirror; -+import javax.lang.model.util.ElementFilter; - import org.netbeans.api.java.source.JavaSourceTest.SourceLevelQueryImpl; - import org.netbeans.junit.NbTestCase; - import org.openide.filesystems.FileObject; -@@ -599,4 +602,206 @@ public void testGetGlobalTypes() throws Exception { - }, true); - } - -+ public void testGetLinkedRecordElements1() throws Exception { -+ prepareTest(); -+ SourceUtilsTestUtil.setSourceLevel(testFO, "17"); -+ TestUtilities.copyStringToFile(FileUtil.toFile(testFO), -+ """ -+ package test; -+ public record R(String component) {} -+ """ -+ ); -+ SourceLevelQueryImpl.sourceLevel = "17"; -+ -+ JavaSource javaSource = JavaSource.forFileObject(testFO); -+ javaSource.runUserActionTask((CompilationController controller) -> { -+ controller.toPhase(JavaSource.Phase.RESOLVED); -+ -+ TypeElement record = controller.getTopLevelElements().get(0); -+ ElementUtilities utils = controller.getElementUtilities(); -+ Collection linked = utils.getLinkedRecordElements(record.getRecordComponents().get(0)); -+ Set linkedEncoded = linked.stream() -+ .map(Element::getKind) -+ .map(ElementKind::name) -+ .collect(Collectors.toCollection(TreeSet::new)); -+ assertEquals(new TreeSet<>(Arrays.asList("FIELD", "METHOD", "PARAMETER", "RECORD_COMPONENT")), -+ linkedEncoded); -+ -+ for (Element linkedElement : linked) { -+ if (!linked.equals(utils.getLinkedRecordElements(linkedElement))) { -+ utils.getLinkedRecordElements(linkedElement); -+ } -+ assertEquals(linked, utils.getLinkedRecordElements(linkedElement)); -+ } -+ }, true); -+ } -+ -+ public void testGetLinkedRecordElements2() throws Exception { -+ prepareTest(); -+ SourceUtilsTestUtil.setSourceLevel(testFO, "17"); -+ TestUtilities.copyStringToFile(FileUtil.toFile(testFO), -+ """ -+ package test; -+ public record R(String component) { -+ public R { -+ this.component = component; -+ } -+ public String component() { -+ return component; -+ } -+ } -+ """ -+ ); -+ SourceLevelQueryImpl.sourceLevel = "17"; -+ -+ JavaSource javaSource = JavaSource.forFileObject(testFO); -+ javaSource.runUserActionTask((CompilationController controller) -> { -+ controller.toPhase(JavaSource.Phase.RESOLVED); -+ -+ TypeElement record = controller.getTopLevelElements().get(0); -+ ElementUtilities utils = controller.getElementUtilities(); -+ Collection linked = utils.getLinkedRecordElements(record.getRecordComponents().get(0)); -+ Set linkedEncoded = linked.stream() -+ .map(Element::getKind) -+ .map(ElementKind::name) -+ .collect(Collectors.toCollection(TreeSet::new)); -+ assertEquals(new TreeSet<>(Arrays.asList("FIELD", "METHOD", "PARAMETER", "RECORD_COMPONENT")), -+ linkedEncoded); -+ -+ for (Element linkedElement : linked) { -+ if (!linked.equals(utils.getLinkedRecordElements(linkedElement))) { -+ utils.getLinkedRecordElements(linkedElement); -+ } -+ assertEquals(linked, utils.getLinkedRecordElements(linkedElement)); -+ } -+ }, true); -+ } -+ -+ public void testGetLinkedRecordElements3() throws Exception { -+ prepareTest(); -+ SourceUtilsTestUtil.setSourceLevel(testFO, "17"); -+ TestUtilities.copyStringToFile(FileUtil.toFile(testFO), -+ """ -+ package test; -+ public record R(String component) { -+ public R(String component) { -+ this.component = component; -+ } -+ public String component() { -+ return component; -+ } -+ } -+ """ -+ ); -+ SourceLevelQueryImpl.sourceLevel = "17"; -+ -+ JavaSource javaSource = JavaSource.forFileObject(testFO); -+ javaSource.runUserActionTask((CompilationController controller) -> { -+ controller.toPhase(JavaSource.Phase.RESOLVED); -+ -+ TypeElement record = controller.getTopLevelElements().get(0); -+ ElementUtilities utils = controller.getElementUtilities(); -+ Collection linked = utils.getLinkedRecordElements(record.getRecordComponents().get(0)); -+ Set linkedEncoded = linked.stream() -+ .map(Element::getKind) -+ .map(ElementKind::name) -+ .collect(Collectors.toCollection(TreeSet::new)); -+ assertEquals(new TreeSet<>(Arrays.asList("FIELD", "METHOD", "PARAMETER", "RECORD_COMPONENT")), -+ linkedEncoded); -+ -+ for (Element linkedElement : linked) { -+ if (!linked.equals(utils.getLinkedRecordElements(linkedElement))) { -+ utils.getLinkedRecordElements(linkedElement); -+ } -+ assertEquals(linked, utils.getLinkedRecordElements(linkedElement)); -+ } -+ }, true); -+ } -+ -+ public void testGetLinkedRecordElements4() throws Exception { -+ prepareTest(); -+ SourceUtilsTestUtil.setSourceLevel(testFO, "17"); -+ TestUtilities.copyStringToFile(FileUtil.toFile(testFO), -+ """ -+ package test; -+ public record R(String component) { -+ public R(String anotherName) { //error -+ this.component = anotherName; -+ } -+ public String component() { -+ return component; -+ } -+ } -+ """ -+ ); -+ SourceLevelQueryImpl.sourceLevel = "17"; -+ -+ JavaSource javaSource = JavaSource.forFileObject(testFO); -+ javaSource.runUserActionTask((CompilationController controller) -> { -+ controller.toPhase(JavaSource.Phase.RESOLVED); -+ -+ TypeElement record = controller.getTopLevelElements().get(0); -+ Element brokenParameter = ElementFilter.constructorsIn(record.getEnclosedElements()).get(0).getParameters().get(0); -+ ElementUtilities utils = controller.getElementUtilities(); -+ Collection linked = utils.getLinkedRecordElements(brokenParameter); -+ Set linkedEncoded = linked.stream() -+ .map(Element::getKind) -+ .map(ElementKind::name) -+ .collect(Collectors.toCollection(TreeSet::new)); -+ assertEquals(new TreeSet<>(Arrays.asList("PARAMETER")), -+ linkedEncoded); -+ }, true); -+ } -+ -+ public void testGetLinkedRecordElements5() throws Exception { -+ prepareTest(); -+ SourceUtilsTestUtil.setSourceLevel(sourceRoot, "17"); -+ TestUtilities.copyStringToFile(FileUtil.toFile(testFO), -+ """ -+ package test; -+ public record R(String component) { -+ public R { -+ this.component = component; -+ } -+ public String component() { -+ return component; -+ } -+ } -+ """ -+ ); -+ FileObject useFO = FileUtil.createData(sourceRoot, "test/Use.java"); -+ TestUtilities.copyStringToFile(FileUtil.toFile(useFO), -+ """ -+ package test; -+ public class Use {} -+ """ -+ ); -+ SourceLevelQueryImpl.sourceLevel = "17"; -+ -+ SourceUtilsTestUtil.compileRecursively(sourceRoot); -+ -+ JavaSource javaSource = JavaSource.forFileObject(useFO); -+ javaSource.runUserActionTask((CompilationController controller) -> { -+ controller.toPhase(JavaSource.Phase.RESOLVED); -+ -+ TypeElement record = controller.getElements().getTypeElement("test.R"); -+ Element component = record.getRecordComponents().get(0); -+ ElementUtilities utils = controller.getElementUtilities(); -+ Collection linked = utils.getLinkedRecordElements(component); -+ Set linkedEncoded = linked.stream() -+ .map(Element::getKind) -+ .map(ElementKind::name) -+ .collect(Collectors.toCollection(TreeSet::new)); -+ assertEquals(new TreeSet<>(Arrays.asList("FIELD", "METHOD", "PARAMETER", "RECORD_COMPONENT")), -+ linkedEncoded); -+ -+ for (Element linkedElement : linked) { -+ if (!linked.equals(utils.getLinkedRecordElements(linkedElement))) { -+ utils.getLinkedRecordElements(linkedElement); -+ } -+ assertEquals(linked, utils.getLinkedRecordElements(linkedElement)); -+ } -+ }, true); -+ } -+ - } -diff --git a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/TestUtilities.java b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/TestUtilities.java -index c85365e643c2..21b1039a262b 100644 ---- a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/TestUtilities.java -+++ b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/TestUtilities.java -@@ -304,4 +304,15 @@ public static Path getJRTFS() throws IOException { - return null; - } - -+ public static TestInput splitCodeAndPos(String input) { -+ int pos = input.indexOf('|'); -+ -+ if (pos == (-1)) { -+ throw new IllegalArgumentException("Does not specify a caret position: " + input); -+ } -+ -+ return new TestInput(input.substring(0, pos) + input.substring(pos + 1), pos); -+ } -+ -+ public record TestInput(String code, int pos) {} - } -diff --git a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/TreeUtilitiesTest.java b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/TreeUtilitiesTest.java -index 67a769ca0b8b..14b7657f89ab 100644 ---- a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/TreeUtilitiesTest.java -+++ b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/TreeUtilitiesTest.java -@@ -18,6 +18,7 @@ - */ - package org.netbeans.api.java.source; - -+import com.sun.source.tree.AnnotationTree; - import com.sun.source.tree.AssignmentTree; - import com.sun.source.tree.BlockTree; - import com.sun.source.tree.ClassTree; -@@ -53,6 +54,7 @@ - import org.netbeans.api.java.classpath.ClassPath; - import org.netbeans.api.java.source.Comment.Style; - import org.netbeans.api.java.source.JavaSource.Phase; -+import org.netbeans.api.java.source.TestUtilities.TestInput; - import org.netbeans.junit.NbTestCase; - import org.netbeans.spi.java.classpath.support.ClassPathSupport; - import org.openide.filesystems.FileObject; -@@ -146,6 +148,54 @@ public void testIsSyntheticNewClassExtends() throws Exception { - assertFalse(info.getTreeUtilities().isSynthetic(new TreePath(tp, nct.getIdentifier()))); - } - -+ public void testIsSyntheticCompactConstructorParams() throws Exception { -+ TestInput input = TestUtilities.splitCodeAndPos(""" -+ package t; -+ public record R(String component) { -+ public R| { -+ } -+ } -+ """); -+ prepareTest("Test", input.code()); -+ -+ TreePath tp = info.getTreeUtilities().pathFor(input.pos()); -+ MethodTree mt = (MethodTree) tp.getLeaf(); -+ -+ assertTrue(info.getTreeUtilities().isSynthetic(new TreePath(tp, mt.getParameters().get(0)))); -+ } -+ -+ public void testIsNotSyntheticExplicitConstructorParams() throws Exception { -+ TestInput input = TestUtilities.splitCodeAndPos(""" -+ package t; -+ public record R(String component) { -+ public R|(String component) { -+ } -+ } -+ """); -+ prepareTest("Test", input.code()); -+ -+ TreePath tp = info.getTreeUtilities().pathFor(input.pos()); -+ MethodTree mt = (MethodTree) tp.getLeaf(); -+ -+ assertFalse(info.getTreeUtilities().isSynthetic(new TreePath(tp, mt.getParameters().get(0)))); -+ } -+ -+ public void testIsNotSyntheticImplicitValueAttributeAssignment() throws Exception { -+ TestInput input = TestUtilities.splitCodeAndPos(""" -+ package t; -+ @An|n(1) -+ public @interface Ann { -+ public int value(); -+ } -+ """); -+ prepareTest("Test", input.code()); -+ -+ TreePath tp = info.getTreeUtilities().pathFor(input.pos()); -+ AnnotationTree at = (AnnotationTree) tp.getParentPath().getLeaf(); -+ -+ assertFalse(info.getTreeUtilities().isSynthetic(new TreePath(tp, at.getArguments().get(0)))); -+ } -+ - public void testIsSyntheticNewClassImplements() throws Exception { - prepareTest("Test", "package test; import java.io.*; public class Test { void t() { new Serializable() { }; } }"); - -diff --git a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/gen/RecordTest.java b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/gen/RecordTest.java -new file mode 100644 -index 000000000000..bc33aa888e5d ---- /dev/null -+++ b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/gen/RecordTest.java -@@ -0,0 +1,250 @@ -+/* -+ * Licensed to the Apache Software Foundation (ASF) under one -+ * or more contributor license agreements. See the NOTICE file -+ * distributed with this work for additional information -+ * regarding copyright ownership. The ASF licenses this file -+ * to you under the Apache License, Version 2.0 (the -+ * "License"); you may not use this file except in compliance -+ * with the License. You may obtain a copy of the License at -+ * -+ * http://www.apache.org/licenses/LICENSE-2.0 -+ * -+ * Unless required by applicable law or agreed to in writing, -+ * software distributed under the License is distributed on an -+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -+ * KIND, either express or implied. See the License for the -+ * specific language governing permissions and limitations -+ * under the License. -+ */ -+package org.netbeans.api.java.source.gen; -+ -+import java.io.*; -+import com.sun.source.tree.*; -+import com.sun.source.tree.Tree.Kind; -+import java.util.EnumSet; -+import javax.lang.model.element.Modifier; -+import org.netbeans.api.java.source.*; -+import static org.netbeans.api.java.source.JavaSource.*; -+import org.netbeans.junit.NbTestSuite; -+ -+public class RecordTest extends GeneratorTestMDRCompat { -+ -+ private String sourceLevel; -+ -+ public RecordTest(String testName) { -+ super(testName); -+ } -+ -+ public static NbTestSuite suite() { -+ NbTestSuite suite = new NbTestSuite(); -+ suite.addTestSuite(RecordTest.class); -+ return suite; -+ } -+ -+ public void testRenameComponent() throws Exception { -+ testFile = new File(getWorkDir(), "Test.java"); -+ TestUtilities.copyStringToFile(testFile, -+ """ -+ package hierbas.del.litoral; -+ public record R(String component) {} -+ """); -+ String golden = -+ """ -+ package hierbas.del.litoral; -+ public record R(String newName) {} -+ """; -+ -+ JavaSource src = getJavaSource(testFile); -+ Task task = new Task() { -+ -+ public void run(WorkingCopy workingCopy) throws IOException { -+ workingCopy.toPhase(Phase.RESOLVED); -+ CompilationUnitTree cut = workingCopy.getCompilationUnit(); -+ TreeMaker make = workingCopy.getTreeMaker(); -+ -+ Tree recordDecl = cut.getTypeDecls().get(0); -+ assertEquals(Kind.RECORD, recordDecl.getKind()); -+ ClassTree classTree = (ClassTree) recordDecl; -+ for (Tree m : classTree.getMembers()) { -+ if (m.getKind() == Kind.VARIABLE) { -+ workingCopy.rewrite(m, make.setLabel(m, "newName")); -+ } -+ } -+ } -+ -+ }; -+ src.runModificationTask(task).commit(); -+ String res = TestUtilities.copyFileToString(testFile); -+ //System.err.println(res); -+ assertEquals(golden, res); -+ } -+ -+ public void testAddFirstComponent() throws Exception { -+ testFile = new File(getWorkDir(), "Test.java"); -+ TestUtilities.copyStringToFile(testFile, -+ """ -+ package hierbas.del.litoral; -+ public record R() {} -+ """); -+ String golden = -+ """ -+ package hierbas.del.litoral; -+ public record R(String component) {} -+ """; -+ -+ JavaSource src = getJavaSource(testFile); -+ Task task = new Task() { -+ -+ public void run(WorkingCopy workingCopy) throws IOException { -+ workingCopy.toPhase(Phase.RESOLVED); -+ CompilationUnitTree cut = workingCopy.getCompilationUnit(); -+ TreeMaker make = workingCopy.getTreeMaker(); -+ -+ Tree recordDecl = cut.getTypeDecls().get(0); -+ assertEquals(Kind.RECORD, recordDecl.getKind()); -+ ClassTree classTree = (ClassTree) recordDecl; -+ VariableTree newComponent = make.RecordComponent(make.Modifiers(EnumSet.noneOf(Modifier.class)), -+ "component", -+ make.Type("java.lang.String")); -+ ClassTree newClassTree = make.addClassMember(classTree, newComponent); -+ workingCopy.rewrite(classTree, newClassTree); -+ } -+ -+ }; -+ src.runModificationTask(task).commit(); -+ String res = TestUtilities.copyFileToString(testFile); -+ //System.err.println(res); -+ assertEquals(golden, res); -+ } -+ -+ public void testAddSecondComponent() throws Exception { -+ testFile = new File(getWorkDir(), "Test.java"); -+ TestUtilities.copyStringToFile(testFile, -+ """ -+ package hierbas.del.litoral; -+ public record R(String existing) {} -+ """); -+ String golden = -+ """ -+ package hierbas.del.litoral; -+ public record R(String existing, String component) {} -+ """; -+ -+ JavaSource src = getJavaSource(testFile); -+ Task task = new Task() { -+ -+ public void run(WorkingCopy workingCopy) throws IOException { -+ workingCopy.toPhase(Phase.RESOLVED); -+ CompilationUnitTree cut = workingCopy.getCompilationUnit(); -+ TreeMaker make = workingCopy.getTreeMaker(); -+ -+ Tree recordDecl = cut.getTypeDecls().get(0); -+ assertEquals(Kind.RECORD, recordDecl.getKind()); -+ ClassTree classTree = (ClassTree) recordDecl; -+ VariableTree newComponent = make.RecordComponent(make.Modifiers(EnumSet.noneOf(Modifier.class)), -+ "component", -+ make.Type("java.lang.String")); -+ ClassTree newClassTree = make.addClassMember(classTree, newComponent); -+ workingCopy.rewrite(classTree, newClassTree); -+ } -+ -+ }; -+ src.runModificationTask(task).commit(); -+ String res = TestUtilities.copyFileToString(testFile); -+ //System.err.println(res); -+ assertEquals(golden, res); -+ } -+ -+ public void testRemoveLastComponent() throws Exception { -+ testFile = new File(getWorkDir(), "Test.java"); -+ TestUtilities.copyStringToFile(testFile, -+ """ -+ package hierbas.del.litoral; -+ public record R(String component) {} -+ """); -+ String golden = -+ """ -+ package hierbas.del.litoral; -+ public record R() {} -+ """; -+ -+ JavaSource src = getJavaSource(testFile); -+ Task task = new Task() { -+ -+ public void run(WorkingCopy workingCopy) throws IOException { -+ workingCopy.toPhase(Phase.RESOLVED); -+ CompilationUnitTree cut = workingCopy.getCompilationUnit(); -+ TreeMaker make = workingCopy.getTreeMaker(); -+ -+ Tree recordDecl = cut.getTypeDecls().get(0); -+ assertEquals(Kind.RECORD, recordDecl.getKind()); -+ ClassTree classTree = (ClassTree) recordDecl; -+ for (Tree m : classTree.getMembers()) { -+ if (m.getKind() == Kind.VARIABLE) { -+ workingCopy.rewrite(classTree, make.removeClassMember(classTree, m)); -+ break; -+ } -+ } -+ } -+ -+ }; -+ src.runModificationTask(task).commit(); -+ String res = TestUtilities.copyFileToString(testFile); -+ //System.err.println(res); -+ assertEquals(golden, res); -+ } -+ -+ public void testRemoveComponent() throws Exception { -+ testFile = new File(getWorkDir(), "Test.java"); -+ TestUtilities.copyStringToFile(testFile, -+ """ -+ package hierbas.del.litoral; -+ public record R(String first, String component) {} -+ """); -+ String golden = -+ """ -+ package hierbas.del.litoral; -+ public record R(String first) {} -+ """; -+ -+ JavaSource src = getJavaSource(testFile); -+ Task task = new Task() { -+ -+ public void run(WorkingCopy workingCopy) throws IOException { -+ workingCopy.toPhase(Phase.RESOLVED); -+ CompilationUnitTree cut = workingCopy.getCompilationUnit(); -+ TreeMaker make = workingCopy.getTreeMaker(); -+ -+ Tree recordDecl = cut.getTypeDecls().get(0); -+ assertEquals(Kind.RECORD, recordDecl.getKind()); -+ ClassTree classTree = (ClassTree) recordDecl; -+ for (Tree m : classTree.getMembers()) { -+ if (m.getKind() == Kind.VARIABLE && -+ ((VariableTree) m).getName().contentEquals("component")) { -+ workingCopy.rewrite(classTree, make.removeClassMember(classTree, m)); -+ break; -+ } -+ } -+ } -+ -+ }; -+ src.runModificationTask(task).commit(); -+ String res = TestUtilities.copyFileToString(testFile); -+ //System.err.println(res); -+ assertEquals(golden, res); -+ } -+ -+ String getGoldenPckg() { -+ return ""; -+ } -+ -+ String getSourcePckg() { -+ return ""; -+ } -+ -+ @Override -+ String getSourceLevel() { -+ return sourceLevel; -+ } -+ -+} -diff --git a/java/refactoring.java/nbproject/project.properties b/java/refactoring.java/nbproject/project.properties -index 482f48c8d29e..5f57bb0404c3 100644 ---- a/java/refactoring.java/nbproject/project.properties -+++ b/java/refactoring.java/nbproject/project.properties -@@ -14,7 +14,7 @@ - # KIND, either express or implied. See the License for the - # specific language governing permissions and limitations - # under the License. --javac.source=1.8 -+javac.release=17 - javadoc.arch=${basedir}/arch.xml - javadoc.apichanges=${basedir}/apichanges.xml - -diff --git a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/JavaPluginUtils.java b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/JavaPluginUtils.java -index 8804024eb0b5..5377f15eb677 100644 ---- a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/JavaPluginUtils.java -+++ b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/JavaPluginUtils.java -@@ -332,69 +332,11 @@ public static boolean hasSetter(CompilationInfo info, TypeElement typeElement, V - return false; - } - -- /** -- * Works as TreeUtilities.isSynthetic, but treats implicit annotation parameter (value) as -- * non-synthetic. See defect #270036 -- */ - public static boolean isSyntheticPath(CompilationInfo ci, TreePath path) { - TreeUtilities tu = ci.getTreeUtilities(); -- if (path == null) -- throw new NullPointerException(); -- -- while (path != null) { -- SYNT: if (isSynthetic(ci, path.getCompilationUnit(), path.getLeaf())) { -- if (path.getLeaf().getKind() == Tree.Kind.ASSIGNMENT && -- path.getParentPath() != null && path.getParentPath().getLeaf().getKind() == Tree.Kind.ANNOTATION) { -- AssignmentTree aTree = (AssignmentTree)path.getLeaf(); -- if (aTree.getVariable().getKind() == Tree.Kind.IDENTIFIER && -- ((IdentifierTree)aTree.getVariable()).getName().contentEquals("value")) { // implicit value is not synthetic -- break SYNT; -- } -- } -- return true; -- } -- -- path = path.getParentPath(); -- } -- -- return false; -+ return tu.isSynthetic(path); - } - -- // -- static boolean isSynthetic(CompilationInfo info, CompilationUnitTree cut, Tree leaf) throws NullPointerException { -- JCTree tree = (JCTree) leaf; -- -- if (tree.pos == (-1)) -- return true; -- -- if (leaf.getKind() == Kind.METHOD) { -- //check for synthetic constructor: -- return (((JCTree.JCMethodDecl)leaf).mods.flags & Flags.GENERATEDCONSTR) != 0L; -- } -- -- //check for synthetic superconstructor call: -- if (leaf.getKind() == Kind.EXPRESSION_STATEMENT) { -- ExpressionStatementTree est = (ExpressionStatementTree) leaf; -- -- if (est.getExpression().getKind() == Kind.METHOD_INVOCATION) { -- MethodInvocationTree mit = (MethodInvocationTree) est.getExpression(); -- -- if (mit.getMethodSelect().getKind() == Kind.IDENTIFIER) { -- IdentifierTree it = (IdentifierTree) mit.getMethodSelect(); -- -- if ("super".equals(it.getName().toString())) { -- SourcePositions sp = info.getTrees().getSourcePositions(); -- -- return sp.getEndPosition(cut, leaf) == (-1); -- } -- } -- } -- } -- -- return false; -- } -- // -- - // - public static final String DEFAULT_NAME = "par"; // NOI18N - public static String makeNameUnique(CompilationInfo info, Scope s, String name) { -diff --git a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/RenameRefactoringPlugin.java b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/RenameRefactoringPlugin.java -index 9c50d4175347..8378dc3b3198 100644 ---- a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/RenameRefactoringPlugin.java -+++ b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/RenameRefactoringPlugin.java -@@ -54,6 +54,7 @@ - public class RenameRefactoringPlugin extends JavaRefactoringPlugin { - - private Set> allMethods = new HashSet>(); -+ private Set recordLinkedDeclarations = new HashSet<>(); - private boolean doCheckName = true; - private Integer overriddenByMethodsCount = null; - private Integer overridesMethodsCount = null; -@@ -422,36 +423,42 @@ public void run(CompilationController info) throws Exception { - : treePathHandle.resolveElement(info); - ElementKind kind = el.getKind(); - ElementHandle enclosingType; -- if (el instanceof TypeElement) { -+ if (kind.isClass() || kind.isInterface()) { - enclosingType = ElementHandle.create((TypeElement)el); - } else { - enclosingType = ElementHandle.create(info.getElementUtilities().enclosingTypeElement(el)); - } - set.add(SourceUtils.getFile(el, info.getClasspathInfo())); -- if (el.getModifiers().contains(Modifier.PRIVATE)) { -- if (kind == ElementKind.METHOD) { -- //add all references of overriding methods -- allMethods.add(ElementHandle.create((ExecutableElement)el)); -+ for (Element linked : info.getElementUtilities().getLinkedRecordElements(el)) { -+ ElementKind linkedKind = linked.getKind(); -+ if (!el.equals(linked)) { -+ recordLinkedDeclarations.add(TreePathHandle.create(linked, info)); - } -- } else { -- if (kind.isField()) { -- set.addAll(idx.getResources(enclosingType, EnumSet.of(ClassIndex.SearchKind.FIELD_REFERENCES), EnumSet.of(ClassIndex.SearchScope.SOURCE))); -- } else if (el instanceof TypeElement) { -- set.addAll(idx.getResources(enclosingType, EnumSet.of(ClassIndex.SearchKind.TYPE_REFERENCES, ClassIndex.SearchKind.IMPLEMENTORS),EnumSet.of(ClassIndex.SearchScope.SOURCE))); -- } else if (kind == ElementKind.METHOD) { -- //add all references of overriding methods -- allMethods.add(ElementHandle.create((ExecutableElement)el)); -- for (ExecutableElement e:JavaRefactoringUtils.getOverridingMethods((ExecutableElement)el, info, cancelRequested)) { -- addMethods(e, set, info, idx); -+ if (linked.getModifiers().contains(Modifier.PRIVATE)) { -+ if (linkedKind == ElementKind.METHOD) { -+ //add all references of overriding methods -+ allMethods.add(ElementHandle.create((ExecutableElement)linked)); - } -- //add all references of overriden methods -- for (ExecutableElement ov: JavaRefactoringUtils.getOverriddenMethods((ExecutableElement)el, info)) { -- addMethods(ov, set, info, idx); -- for (ExecutableElement e:JavaRefactoringUtils.getOverridingMethods( ov,info, cancelRequested)) { -+ } else { -+ if (linkedKind.isField()) { -+ set.addAll(idx.getResources(enclosingType, EnumSet.of(ClassIndex.SearchKind.FIELD_REFERENCES), EnumSet.of(ClassIndex.SearchScope.SOURCE))); -+ } else if (linked instanceof TypeElement) { -+ set.addAll(idx.getResources(enclosingType, EnumSet.of(ClassIndex.SearchKind.TYPE_REFERENCES, ClassIndex.SearchKind.IMPLEMENTORS),EnumSet.of(ClassIndex.SearchScope.SOURCE))); -+ } else if (linkedKind == ElementKind.METHOD) { -+ //add all references of overriding methods -+ allMethods.add(ElementHandle.create((ExecutableElement)linked)); -+ for (ExecutableElement e:JavaRefactoringUtils.getOverridingMethods((ExecutableElement)linked, info, cancelRequested)) { - addMethods(e, set, info, idx); - } -+ //add all references of overriden methods -+ for (ExecutableElement ov: JavaRefactoringUtils.getOverriddenMethods((ExecutableElement)linked, info)) { -+ addMethods(ov, set, info, idx); -+ for (ExecutableElement e:JavaRefactoringUtils.getOverridingMethods( ov,info, cancelRequested)) { -+ addMethods(e, set, info, idx); -+ } -+ } -+ set.addAll(idx.getResources(enclosingType, EnumSet.of(ClassIndex.SearchKind.METHOD_REFERENCES),EnumSet.of(ClassIndex.SearchScope.SOURCE))); //????? - } -- set.addAll(idx.getResources(enclosingType, EnumSet.of(ClassIndex.SearchKind.METHOD_REFERENCES),EnumSet.of(ClassIndex.SearchScope.SOURCE))); //????? - } - } - } -@@ -623,7 +630,7 @@ public Problem prepare(RefactoringElementsBag elements) { - } - Set a = getRelevantFiles(); - fireProgressListenerStart(AbstractRefactoring.PREPARE, a.size()); -- TransformTask transform = new TransformTask(new RenameTransformer(treePathHandle, docTreePathHandle, refactoring, allMethods, refactoring.isSearchInComments()), treePathHandle != null && treePathHandle.getKind() == Tree.Kind.LABELED_STATEMENT ? null : treePathHandle); -+ TransformTask transform = new TransformTask(new RenameTransformer(treePathHandle, docTreePathHandle, refactoring, allMethods, recordLinkedDeclarations, refactoring.isSearchInComments()), treePathHandle != null && treePathHandle.getKind() == Tree.Kind.LABELED_STATEMENT ? null : treePathHandle); - Problem problem = createAndAddElements(a, transform, elements, refactoring,getClasspathInfo(refactoring)); - fireProgressListenerStop(); - return problem; -diff --git a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/RenameTransformer.java b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/RenameTransformer.java -index a27dd2972623..c833058dbcb1 100644 ---- a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/RenameTransformer.java -+++ b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/RenameTransformer.java -@@ -59,6 +59,7 @@ - public class RenameTransformer extends RefactoringVisitor { - - private final Set> allMethods; -+ private final Set recordLinkedDeclarations; - private final TreePathHandle handle; - private final DocTreePathHandle docHandle; - private final String newName; -@@ -68,13 +69,14 @@ public class RenameTransformer extends RefactoringVisitor { - private Map imports; - private List newImports; - -- public RenameTransformer(TreePathHandle handle, DocTreePathHandle docHandle, RenameRefactoring refactoring, Set> am, boolean renameInComments) { -+ public RenameTransformer(TreePathHandle handle, DocTreePathHandle docHandle, RenameRefactoring refactoring, Set> am, Set recordLinkedDeclarations, boolean renameInComments) { - super(true); - this.handle = handle; - this.docHandle = docHandle; - this.refactoring = refactoring; - this.newName = refactoring.getNewName(); - this.allMethods = am; -+ this.recordLinkedDeclarations = recordLinkedDeclarations; - this.renameInComments = renameInComments; - } - -@@ -197,6 +199,17 @@ private void renameUsageIfMatch(final TreePath path, Tree tree, Element elementT - if (JavaPluginUtils.isSyntheticPath(workingCopy, path) || (handle != null && handle.getKind() == Tree.Kind.LABELED_STATEMENT)) { - return; - } -+ doRenameUsageIfMatch(getCurrentPath(), tree, elementToFind); -+ for (TreePathHandle h : recordLinkedDeclarations) { -+ Element linked = h.resolveElement(workingCopy); -+ -+ if (linked != null) { -+ doRenameUsageIfMatch(getCurrentPath(), tree, linked); -+ } -+ } -+ } -+ -+ private void doRenameUsageIfMatch(final TreePath path, Tree tree, Element elementToFind) { - TreePath elementPath = path; - Trees trees = workingCopy.getTrees(); - Element el = workingCopy.getTrees().getElement(elementPath); -@@ -391,14 +404,17 @@ public Tree visitClass(ClassTree tree, final Element p) { - Element el = workingCopy.getTrees().getElement(currentPath); - if (el != null && el.getEnclosedElements().contains(p)) { - Trees trees = workingCopy.getTrees(); -- Scope scope = trees.getScope(trees.getPath(p)); -- shadowed = workingCopy.getElementUtilities().getLocalMembersAndVars(scope, new ElementUtilities.ElementAcceptor() { -+ TreePath pPath = trees.getPath(p); -+ if (pPath != null) { //may be null for synthetic record accessors -+ Scope scope = trees.getScope(pPath); -+ shadowed = workingCopy.getElementUtilities().getLocalMembersAndVars(scope, new ElementUtilities.ElementAcceptor() { - -- @Override -- public boolean accept(Element element, TypeMirror type) { -- return !element.equals(p) && element.getKind() == p.getKind() && element.getSimpleName().contentEquals(newName); -- } -- }); -+ @Override -+ public boolean accept(Element element, TypeMirror type) { -+ return !element.equals(p) && element.getKind() == p.getKind() && element.getSimpleName().contentEquals(newName); -+ } -+ }); -+ } - } - Tree value = super.visitClass(tree, p); - shadowed = null; -@@ -421,6 +437,19 @@ private void renameDeclIfMatch(TreePath path, Tree tree, Element elementToFind) - if (JavaPluginUtils.isSyntheticPath(workingCopy, path) || (handle != null && handle.getKind() == Tree.Kind.LABELED_STATEMENT)) { - return; - } -+ -+ doRenameDeclIfMatch(path, tree, elementToFind); -+ -+ for (TreePathHandle h : recordLinkedDeclarations) { -+ Element linked = h.resolveElement(workingCopy); -+ -+ if (linked != null) { -+ doRenameDeclIfMatch(getCurrentPath(), tree, linked); -+ } -+ } -+ } -+ -+ private void doRenameDeclIfMatch(TreePath path, Tree tree, Element elementToFind) { - Element el = workingCopy.getTrees().getElement(path); - if (el==null) { - return; -diff --git a/java/refactoring.java/test/unit/src/org/netbeans/modules/refactoring/java/test/RenameRecordTest.java b/java/refactoring.java/test/unit/src/org/netbeans/modules/refactoring/java/test/RenameRecordTest.java -new file mode 100644 -index 000000000000..43fbba7faa72 ---- /dev/null -+++ b/java/refactoring.java/test/unit/src/org/netbeans/modules/refactoring/java/test/RenameRecordTest.java -@@ -0,0 +1,418 @@ -+/* -+ * Licensed to the Apache Software Foundation (ASF) under one -+ * or more contributor license agreements. See the NOTICE file -+ * distributed with this work for additional information -+ * regarding copyright ownership. The ASF licenses this file -+ * to you under the Apache License, Version 2.0 (the -+ * "License"); you may not use this file except in compliance -+ * with the License. You may obtain a copy of the License at -+ * -+ * http://www.apache.org/licenses/LICENSE-2.0 -+ * -+ * Unless required by applicable law or agreed to in writing, -+ * software distributed under the License is distributed on an -+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -+ * KIND, either express or implied. See the License for the -+ * specific language governing permissions and limitations -+ * under the License. -+ */ -+package org.netbeans.modules.refactoring.java.test; -+ -+import com.sun.source.tree.CompilationUnitTree; -+import com.sun.source.util.TreePath; -+import java.util.Arrays; -+import java.util.LinkedList; -+import java.util.List; -+import org.netbeans.api.java.source.CompilationController; -+import org.netbeans.api.java.source.JavaSource; -+import org.netbeans.api.java.source.Task; -+import org.netbeans.api.java.source.TestUtilities; -+import org.netbeans.api.java.source.TestUtilities.TestInput; -+import org.netbeans.api.java.source.TreePathHandle; -+import org.netbeans.modules.refactoring.api.Problem; -+import org.netbeans.modules.refactoring.api.RefactoringSession; -+import org.netbeans.modules.refactoring.api.RenameRefactoring; -+import org.netbeans.modules.refactoring.java.ui.JavaRenameProperties; -+import org.openide.filesystems.FileObject; -+import org.openide.util.lookup.Lookups; -+ -+public class RenameRecordTest extends RefactoringTestBase { -+ -+ public RenameRecordTest(String name) { -+ super(name, "17"); -+ } -+ -+ public void testRenameComponent1() throws Exception { -+ String testCode = """ -+ package test; -+ public record Test(int compo|nent) {} -+ """; -+ TestInput splitCode = TestUtilities.splitCodeAndPos(testCode); -+ writeFilesAndWaitForScan(src, -+ new File("Test.java", splitCode.code()), -+ new File("Use.java", -+ """ -+ package test; -+ public class Use { -+ private void test(Test t) { -+ int i = t.component(); -+ } -+ } -+ """)); -+ JavaRenameProperties props = new JavaRenameProperties(); -+ performRename(src.getFileObject("Test.java"), splitCode.pos(), "newName", props, true); -+ verifyContent(src, new File("Test.java", -+ """ -+ package test; -+ public record Test(int newName) {} -+ """), -+ new File("Use.java", -+ """ -+ package test; -+ public class Use { -+ private void test(Test t) { -+ int i = t.newName(); -+ } -+ } -+ """)); -+ -+ } -+ -+ public void testRenameComponent2() throws Exception { -+ String testCode = """ -+ package test; -+ public record Test(int compo|nent) { -+ public Test(int component) { -+ component = -1; -+ } -+ public int component() { -+ return component; -+ } -+ public int hashCode() { -+ return component; -+ } -+ } -+ """; -+ TestInput splitCode = TestUtilities.splitCodeAndPos(testCode); -+ writeFilesAndWaitForScan(src, -+ new File("Test.java", splitCode.code()), -+ new File("Use.java", -+ """ -+ package test; -+ public class Use { -+ private void test(Test t) { -+ int i = t.component(); -+ } -+ } -+ """)); -+ JavaRenameProperties props = new JavaRenameProperties(); -+ performRename(src.getFileObject("Test.java"), splitCode.pos(), "newName", props, true); -+ verifyContent(src, new File("Test.java", -+ """ -+ package test; -+ public record Test(int newName) { -+ public Test(int newName) { -+ newName = -1; -+ } -+ public int newName() { -+ return newName; -+ } -+ public int hashCode() { -+ return newName; -+ } -+ } -+ """), -+ new File("Use.java", -+ """ -+ package test; -+ public class Use { -+ private void test(Test t) { -+ int i = t.newName(); -+ } -+ } -+ """)); -+ -+ } -+ -+ public void testRenameComponent3() throws Exception { -+ String testCode = """ -+ package test; -+ public record Test(int compo|nent) { -+ public Test { -+ component = -1; -+ } -+ public int component() { -+ return component; -+ } -+ public int hashCode() { -+ return component; -+ } -+ } -+ """; -+ TestInput splitCode = TestUtilities.splitCodeAndPos(testCode); -+ writeFilesAndWaitForScan(src, -+ new File("Test.java", splitCode.code()), -+ new File("Use.java", -+ """ -+ package test; -+ public class Use { -+ private void test(Test t) { -+ int i = t.component(); -+ } -+ } -+ """)); -+ JavaRenameProperties props = new JavaRenameProperties(); -+ performRename(src.getFileObject("Test.java"), splitCode.pos(), "newName", props, true); -+ verifyContent(src, new File("Test.java", -+ """ -+ package test; -+ public record Test(int newName) { -+ public Test { -+ newName = -1; -+ } -+ public int newName() { -+ return newName; -+ } -+ public int hashCode() { -+ return newName; -+ } -+ } -+ """), -+ new File("Use.java", -+ """ -+ package test; -+ public class Use { -+ private void test(Test t) { -+ int i = t.newName(); -+ } -+ } -+ """)); -+ -+ } -+ -+ public void testRenameComponentStartFromAccessor1() throws Exception { -+ String useCode = """ -+ package test; -+ public class Use { -+ private void test(Test t) { -+ int i = t.com|ponent(); -+ } -+ } -+ """; -+ TestInput splitCode = TestUtilities.splitCodeAndPos(useCode); -+ writeFilesAndWaitForScan(src, -+ new File("Test.java", -+ """ -+ package test; -+ public record Test(int component) { -+ public Test { -+ component = -1; -+ } -+ public int component() { -+ return component; -+ } -+ public int hashCode() { -+ return component; -+ } -+ } -+ """), -+ new File("Use.java", splitCode.code())); -+ JavaRenameProperties props = new JavaRenameProperties(); -+ performRename(src.getFileObject("Use.java"), splitCode.pos(), "newName", props, true); -+ verifyContent(src, new File("Test.java", -+ """ -+ package test; -+ public record Test(int newName) { -+ public Test { -+ newName = -1; -+ } -+ public int newName() { -+ return newName; -+ } -+ public int hashCode() { -+ return newName; -+ } -+ } -+ """), -+ new File("Use.java", -+ """ -+ package test; -+ public class Use { -+ private void test(Test t) { -+ int i = t.newName(); -+ } -+ } -+ """)); -+ -+ } -+ -+ public void testRenameComponentStartFromAccessor2() throws Exception { -+ String useCode = """ -+ package test; -+ public class Use { -+ private void test(Test t) { -+ int i = t.com|ponent(); -+ } -+ } -+ """; -+ TestInput splitCode = TestUtilities.splitCodeAndPos(useCode); -+ writeFilesAndWaitForScan(src, -+ new File("Test.java", -+ """ -+ package test; -+ public record Test(int component) { -+ } -+ """), -+ new File("Use.java", splitCode.code())); -+ JavaRenameProperties props = new JavaRenameProperties(); -+ performRename(src.getFileObject("Use.java"), splitCode.pos(), "newName", props, true); -+ verifyContent(src, new File("Test.java", -+ """ -+ package test; -+ public record Test(int newName) { -+ } -+ """), -+ new File("Use.java", -+ """ -+ package test; -+ public class Use { -+ private void test(Test t) { -+ int i = t.newName(); -+ } -+ } -+ """)); -+ -+ } -+ -+ public void testRenameComponentStartFromConstructorArg() throws Exception { -+ String testCode = """ -+ package test; -+ public record Test(int component) { -+ public Test { -+ compo|nent = -1; -+ } -+ public int component() { -+ return component; -+ } -+ public int hashCode() { -+ return component; -+ } -+ } -+ """; -+ TestInput splitCode = TestUtilities.splitCodeAndPos(testCode); -+ writeFilesAndWaitForScan(src, -+ new File("Test.java", splitCode.code()), -+ new File("Use.java", -+ """ -+ package test; -+ public class Use { -+ private void test(Test t) { -+ int i = t.component(); -+ } -+ } -+ """)); -+ JavaRenameProperties props = new JavaRenameProperties(); -+ performRename(src.getFileObject("Test.java"), splitCode.pos(), "newName", props, true); -+ verifyContent(src, new File("Test.java", -+ """ -+ package test; -+ public record Test(int newName) { -+ public Test { -+ newName = -1; -+ } -+ public int newName() { -+ return newName; -+ } -+ public int hashCode() { -+ return newName; -+ } -+ } -+ """), -+ new File("Use.java", -+ """ -+ package test; -+ public class Use { -+ private void test(Test t) { -+ int i = t.newName(); -+ } -+ } -+ """)); -+ } -+ -+ public void testRenameRecord() throws Exception { -+ String testCode = """ -+ package test; -+ public record Te|st(int component) { -+ public Test { -+ component = ""; -+ } -+ } -+ """; -+ TestInput splitCode = TestUtilities.splitCodeAndPos(testCode); -+ writeFilesAndWaitForScan(src, -+ new File("Test.java", splitCode.code()), -+ new File("Use.java", -+ """ -+ package test; -+ public class Use { -+ private Test test() { -+ return new Test(0); -+ } -+ } -+ """)); -+ JavaRenameProperties props = new JavaRenameProperties(); -+ performRename(src.getFileObject("Test.java"), splitCode.pos(), "NewName", props, true); -+ verifyContent(src, new File("Test.java", -+ """ -+ package test; -+ public record NewName(int component) { -+ public NewName { -+ component = ""; -+ } -+ } -+ """), -+ new File("Use.java", -+ """ -+ package test; -+ public class Use { -+ private NewName test() { -+ return new NewName(0); -+ } -+ } -+ """)); -+ -+ } -+ private void performRename(FileObject source, final int absPos, final String newname, final JavaRenameProperties props, final boolean searchInComments, Problem... expectedProblems) throws Exception { -+ final RenameRefactoring[] r = new RenameRefactoring[1]; -+ JavaSource.forFileObject(source).runUserActionTask(new Task() { -+ -+ @Override -+ public void run(CompilationController javac) throws Exception { -+ javac.toPhase(JavaSource.Phase.RESOLVED); -+ CompilationUnitTree cut = javac.getCompilationUnit(); -+ -+ TreePath tp = javac.getTreeUtilities().pathFor(absPos); -+ -+ r[0] = new RenameRefactoring(Lookups.singleton(TreePathHandle.create(tp, javac))); -+ r[0].setNewName(newname); -+ r[0].setSearchInComments(searchInComments); -+ if(props != null) { -+ r[0].getContext().add(props); -+ } -+ } -+ }, true); -+ -+ RefactoringSession rs = RefactoringSession.create("Rename"); -+ List problems = new LinkedList<>(); -+ -+ addAllProblems(problems, r[0].preCheck()); -+ if (!problemIsFatal(problems)) { -+ addAllProblems(problems, r[0].prepare(rs)); -+ } -+ if (!problemIsFatal(problems)) { -+ addAllProblems(problems, rs.doRefactoring(true)); -+ } -+ -+ assertProblems(Arrays.asList(expectedProblems), problems); -+ } -+} -diff --git a/java/refactoring.java/test/unit/src/org/netbeans/modules/refactoring/java/test/RenameTest.java b/java/refactoring.java/test/unit/src/org/netbeans/modules/refactoring/java/test/RenameTest.java -index 0c22d6d57808..f2e961fa4af5 100644 ---- a/java/refactoring.java/test/unit/src/org/netbeans/modules/refactoring/java/test/RenameTest.java -+++ b/java/refactoring.java/test/unit/src/org/netbeans/modules/refactoring/java/test/RenameTest.java -@@ -28,6 +28,8 @@ - import org.netbeans.api.java.source.CompilationController; - import org.netbeans.api.java.source.JavaSource; - import org.netbeans.api.java.source.Task; -+import org.netbeans.api.java.source.TestUtilities; -+import org.netbeans.api.java.source.TestUtilities.TestInput; - import org.netbeans.api.java.source.TreePathHandle; - import org.netbeans.modules.refactoring.api.Problem; - import org.netbeans.modules.refactoring.api.RefactoringSession; -@@ -1573,6 +1575,60 @@ public void testRenameBindingVariableType() throws Exception { - - } - -+ public void testRenameClassInAnnotation() throws Exception { -+ TestInput input = TestUtilities.splitCodeAndPos(""" -+ package t; -+ public class T|est { -+ } -+ """); -+ -+ writeFilesAndWaitForScan(src, -+ new File("t/Test.java", input.code()), -+ new File("t/Ann.java", -+ """ -+ package t; -+ @interface Ann { -+ public Class value(); -+ } -+ """), -+ new File("t/Use.java", -+ """ -+ package t; -+ public class Use { -+ @Ann(Test.class) -+ void t1() {} -+ @Ann({Test.class}) -+ void t2() {} -+ } -+ """)); -+ JavaRenameProperties props = new JavaRenameProperties(); -+ performRename(src.getFileObject("t/Test.java"), input.pos(), "NewName", props, true); -+ verifyContent(src, -+ new File("t/Test.java", -+ """ -+ package t; -+ public class NewName { -+ } -+ """), -+ new File("t/Ann.java", -+ """ -+ package t; -+ @interface Ann { -+ public Class value(); -+ } -+ """), -+ new File("t/Use.java", -+ """ -+ package t; -+ public class Use { -+ @Ann(NewName.class) -+ void t1() {} -+ @Ann({NewName.class}) -+ void t2() {} -+ } -+ """)); -+ } -+ - private void performRename(FileObject source, final int position, final int position2, final String newname, final JavaRenameProperties props, final boolean searchInComments, Problem... expectedProblems) throws Exception { - final RenameRefactoring[] r = new RenameRefactoring[1]; - JavaSource.forFileObject(source).runUserActionTask(new Task() { diff --git a/patches/7699.diff b/patches/7699.diff deleted file mode 100644 index b508a11cf781..000000000000 --- a/patches/7699.diff +++ /dev/null @@ -1,36 +0,0 @@ -diff --git a/java/java.source.base/src/org/netbeans/api/java/source/GeneratorUtilities.java b/java/java.source.base/src/org/netbeans/api/java/source/GeneratorUtilities.java -index aed6e6110c..1c081994d8 100644 ---- a/java/java.source.base/src/org/netbeans/api/java/source/GeneratorUtilities.java -+++ b/java/java.source.base/src/org/netbeans/api/java/source/GeneratorUtilities.java -@@ -1276,7 +1276,7 @@ public final class GeneratorUtilities { - // check for possible name clashes originating from adding the package imports - Set explicitNamedImports = new HashSet(); - for (Element element : elementsToImport) { -- if (element.getKind().isClass() || element.getKind().isInterface()) { -+ if (element.getEnclosingElement() != pkg && (element.getKind().isClass() || element.getKind().isInterface())) { - for (Symbol sym : importScope.getSymbolsByName((com.sun.tools.javac.util.Name)element.getSimpleName())) { - if (sym.getKind().isClass() || sym.getKind().isInterface()) { - if (sym != element) { -diff --git a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/GeneratorUtilitiesTest.java b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/GeneratorUtilitiesTest.java -index 90d0261c19..3f13dde797 100644 ---- a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/GeneratorUtilitiesTest.java -+++ b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/GeneratorUtilitiesTest.java -@@ -583,6 +583,18 @@ public class GeneratorUtilitiesTest extends NbTestCase { - }, false); - } - -+ public void testAddImports14() throws Exception { -+ performTest("test/Process.java", "package test;\nimport java.util.Collections;\npublic class Process {\npublic static void main(String... args) {\n" + -+ "Collections.singleton(Process.class).forEach(System.out::println);\n}\n}", "1.5", new AddImportsTask("test.Process"), new Validator() { -+ public void validate(CompilationInfo info) { -+ assertEquals(0, info.getDiagnostics().size()); -+ List imports = info.getCompilationUnit().getImports(); -+ assertEquals(1, imports.size()); -+ assertEquals("java.util.Collections", imports.get(0).getQualifiedIdentifier().toString()); -+ } -+ }, false); -+ } -+ - public void testAddImportsIncrementallyWithStatic_JIRA3019() throws Exception { - JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true; - performTest("package test;\npublic class Test { public static final String CONST = null; }\n", "11", new AddImportsTask(true, "test.Test.CONST", "java.util.List"), new Validator() { diff --git a/patches/7709.diff b/patches/7709.diff deleted file mode 100644 index 09a5f36fede2..000000000000 --- a/patches/7709.diff +++ /dev/null @@ -1,37 +0,0 @@ -diff --git a/nbbuild/misc/prepare-bundles/src/main/java/org/netbeans/prepare/bundles/PrepareBundles.java b/nbbuild/misc/prepare-bundles/src/main/java/org/netbeans/prepare/bundles/PrepareBundles.java -index d24c6f5ec9..f2b040204e 100644 ---- a/nbbuild/misc/prepare-bundles/src/main/java/org/netbeans/prepare/bundles/PrepareBundles.java -+++ b/nbbuild/misc/prepare-bundles/src/main/java/org/netbeans/prepare/bundles/PrepareBundles.java -@@ -58,7 +58,8 @@ public class PrepareBundles { - Pattern.compile("license", Pattern.CASE_INSENSITIVE), - Pattern.compile("LICENSE.txt", Pattern.CASE_INSENSITIVE), - Pattern.compile("LICENSE-MIT.txt", Pattern.CASE_INSENSITIVE), -- Pattern.compile("LICENSE.md", Pattern.CASE_INSENSITIVE) -+ Pattern.compile("LICENSE.md", Pattern.CASE_INSENSITIVE), -+ Pattern.compile("LICENSE.markdown", Pattern.CASE_INSENSITIVE) - ); - private static final String nl = "\n"; - -@@ -69,7 +70,12 @@ public class PrepareBundles { - - Path targetDir = Paths.get(args[0]); - Path packagesDir = targetDir.resolve("package"); -- new ProcessBuilder("npm", "install").directory(packagesDir.toFile()).inheritIO().start().waitFor(); -+ String os = System.getProperty("os.name").toLowerCase(); -+ if (os.contains("windows")) { -+ new ProcessBuilder("npm.cmd", "install").directory(packagesDir.toFile()).inheritIO().start().waitFor(); -+ } else{ -+ new ProcessBuilder("npm", "install").directory(packagesDir.toFile()).inheritIO().start().waitFor(); -+ } - Path bundlesDir = targetDir.resolve("bundles"); - Files.createDirectories(bundlesDir); - try (DirectoryStream ds = Files.newDirectoryStream(bundlesDir)) { -@@ -107,6 +113,8 @@ public class PrepareBundles { - if ("@types".equals(module.getFileName().toString())) continue; - if ("@esbuild".equals(module.getFileName().toString())) continue; - if ("@microsoft".equals(module.getFileName().toString())) continue; -+ if ("eastasianwidth".equals(module.getFileName().toString())) continue; -+ if ("isarray".equals(module.getFileName().toString())) continue; - Path packageJson = module.resolve("package.json"); - if (Files.isReadable(packageJson)) { - checkModule(module, sb, tokens2Projects, project2License, bundlesDir, targetDir, externalDir, binariesList); diff --git a/patches/7722.diff b/patches/7722.diff deleted file mode 100644 index 38df2c6c9fea..000000000000 --- a/patches/7722.diff +++ /dev/null @@ -1,193 +0,0 @@ -diff --git a/extide/gradle/src/org/netbeans/modules/gradle/ProjectTrust.java b/extide/gradle/src/org/netbeans/modules/gradle/ProjectTrust.java -index 1d7ad89a8714..216291f86fd4 100644 ---- a/extide/gradle/src/org/netbeans/modules/gradle/ProjectTrust.java -+++ b/extide/gradle/src/org/netbeans/modules/gradle/ProjectTrust.java -@@ -25,10 +25,10 @@ - import java.security.InvalidKeyException; - import java.security.Key; - import java.security.NoSuchAlgorithmException; -+import java.security.SecureRandom; - import java.util.Collections; - import java.util.HashSet; - import java.util.List; --import java.util.Random; - import java.util.Set; - import java.util.logging.Level; - import java.util.logging.Logger; -@@ -69,7 +69,7 @@ public class ProjectTrust { - byte[] buf = prefs.getByteArray(KEY_SALT, null); - if (buf == null) { - buf = new byte[16]; -- new Random().nextBytes(buf); -+ new SecureRandom().nextBytes(buf); - prefs.putByteArray(KEY_SALT, buf); - } - salt = buf; -@@ -134,7 +134,7 @@ public void trustProject(Project project, boolean permanently) { - if (permanently && !isTrustedPermanently(project)) { - Path trustFile = getProjectTrustFile(project); - byte[] rnd = new byte[16]; -- new Random().nextBytes(rnd); -+ new SecureRandom().nextBytes(rnd); - String projectId = toHex(rnd); - projectTrust.put(pathId, projectId); - try { -diff --git a/ide/projectapi/arch.xml b/ide/projectapi/arch.xml -index e91502b7570b..81349ae3f7d0 100644 ---- a/ide/projectapi/arch.xml -+++ b/ide/projectapi/arch.xml -@@ -509,8 +509,9 @@ Nothing. -

- -

-- If defined, limits search for a parent project to a certain subtree. The property defines absolute path of a folder -- where upwards search for a project in parent folders is terminated. Queries outside of the root will not find any project. -+ If defined, limits search for a parent project to a certain subtree. The property defines the absolute path of a folder -+ where upwards search for a project in parent folders is terminated. Queries outside the root will not find any project. -+ Multiple folders may be specified when delimited with OS-specific path separators (':' on *nix, ';' on Windows). - Currently used for tests so the tested runtime does not escape the workdir. -

-
-diff --git a/ide/projectapi/src/org/netbeans/modules/projectapi/SimpleFileOwnerQueryImplementation.java b/ide/projectapi/src/org/netbeans/modules/projectapi/SimpleFileOwnerQueryImplementation.java -index 05b887129a52..bf4b435f4dbf 100644 ---- a/ide/projectapi/src/org/netbeans/modules/projectapi/SimpleFileOwnerQueryImplementation.java -+++ b/ide/projectapi/src/org/netbeans/modules/projectapi/SimpleFileOwnerQueryImplementation.java -@@ -19,6 +19,7 @@ - - package org.netbeans.modules.projectapi; - -+import java.io.File; - import java.io.IOException; - import java.lang.ref.Reference; - import java.lang.ref.WeakReference; -@@ -27,10 +28,9 @@ - import java.net.URISyntaxException; - import java.net.URL; - import java.util.ArrayList; --import java.util.Arrays; - import java.util.Collections; - import java.util.HashMap; --import java.util.HashSet; -+import java.util.LinkedHashSet; - import java.util.List; - import java.util.Map; - import java.util.Set; -@@ -45,6 +45,7 @@ - import org.netbeans.api.project.ProjectManager; - import org.netbeans.spi.project.FileOwnerQueryImplementation; - import org.openide.filesystems.FileObject; -+import org.openide.filesystems.FileUtil; - import org.openide.filesystems.URLMapper; - import org.openide.util.BaseUtilities; - import org.openide.util.NbPreferences; -@@ -59,21 +60,19 @@ public class SimpleFileOwnerQueryImplementation implements FileOwnerQueryImpleme - private static final Logger LOG = Logger.getLogger(SimpleFileOwnerQueryImplementation.class.getName()); - private static final URI UNOWNED_URI = URI.create("http:unowned"); - private static final Set forbiddenFolders; -- private static final String projectScanRoot; -+ private static final Set projectScanRoots; - - static { -- Set files = new HashSet(); -- String root = null; -+ Set folders = null; -+ Set roots = null; - try { -- root = System.getProperty("project.limitScanRoot"); // NOI18N -- String forbidden = System.getProperty("project.forbiddenFolders", System.getProperty("versioning.forbiddenFolders", "")); //NOI18N -- files.addAll(Arrays.asList(forbidden.split("\\;"))); //NOI18N -- files.remove(""); //NOI18N -+ roots = separatePaths(System.getProperty("project.limitScanRoot"), File.pathSeparator); //NOI18N -+ folders = separatePaths(System.getProperty("project.forbiddenFolders", System.getProperty("versioning.forbiddenFolders")), ";"); //NOI18N - } catch (Exception e) { - LOG.log(Level.INFO, e.getMessage(), e); - } -- forbiddenFolders = files; -- projectScanRoot = root; -+ forbiddenFolders = folders == null ? Collections.emptySet() : folders; -+ projectScanRoots = roots; - } - - /** Do nothing */ -@@ -113,7 +112,7 @@ public Project getOwner(FileObject f) { - - deserialize(); - while (f != null) { -- if (projectScanRoot != null && !f.getPath().startsWith(projectScanRoot)) { -+ if (projectScanRoots != null && projectScanRoots.stream().noneMatch(f.getPath()::startsWith)) { - break; - } - boolean folder = f.isFolder(); -@@ -137,8 +136,8 @@ public Project getOwner(FileObject f) { - } - folders.add(f); - if (!forbiddenFolders.contains(f.getPath()) && -- !hasRoot(externalOwners.keySet(), f, folder, furi) && -- !hasRoot(deserializedExternalOwners.keySet(), f, folder, furi)) { -+ !hasRoot(externalOwners.keySet(), f, true, furi) && -+ !hasRoot(deserializedExternalOwners.keySet(), f, true, furi)) { - Project p; - try { - p = ProjectManager.getDefault().findProject(f); -@@ -414,6 +413,40 @@ private static URI goUp(URI u) { - assert u.toString().startsWith(nue.toString()) : "not a parent: " + nue + " of " + u; - return nue; - } -+ -+ private static Set separatePaths(String joinedPaths, String pathSeparator) { -+ if (joinedPaths == null || joinedPaths.isEmpty()) -+ return null; -+ -+ Set paths = null; -+ for (String split : joinedPaths.split(pathSeparator)) { -+ if ((split = split.trim()).isEmpty()) continue; -+ -+ // Ensure that variations in terms of ".." or "." or windows drive-letter case differences are removed. -+ // File.getCanonicalFile() will additionally resolve symlinks, which is not required. -+ File file = FileUtil.normalizeFile(new File(split)); -+ -+ // Store FileObject.getPath(); because getOwner() compares these with FileObject.getPath() strings. -+ // This has some peculiarities as compared to File.getAbsolutePath(); such as return "" for File("/"). -+ FileObject fileObject = FileUtil.toFileObject(file); -+ // This conversion may get rid of non-existent paths. -+ if (fileObject == null) continue; -+ -+ String path = fileObject.getPath(); -+ if (path == null || path.isEmpty()) continue; -+ -+ if (paths == null) { -+ paths = Collections.singleton(path); // more performant in usage when only a single element is present. -+ } else { -+ if (paths.size() == 1) { -+ paths = new LinkedHashSet<>(paths); // more performant in iteration -+ } -+ paths.add(path); -+ } -+ } -+ return paths; -+ } -+ - private static final boolean WINDOWS = BaseUtilities.isWindows(); - - } -diff --git a/platform/o.n.bootstrap/src/org/netbeans/CLIHandler.java b/platform/o.n.bootstrap/src/org/netbeans/CLIHandler.java -index 633ee72340de..3140aba8eafa 100644 ---- a/platform/o.n.bootstrap/src/org/netbeans/CLIHandler.java -+++ b/platform/o.n.bootstrap/src/org/netbeans/CLIHandler.java -@@ -45,7 +45,6 @@ - import java.util.Collection; - import java.util.Collections; - import java.util.List; --import java.util.Random; - import java.util.logging.Level; - import java.util.logging.Logger; - import org.openide.util.RequestProcessor; -@@ -580,7 +579,7 @@ static Status initialize( - enterState(10, block); - - final byte[] arr = new byte[KEY_LENGTH]; -- new Random().nextBytes(arr); -+ new SecureRandom().nextBytes(arr); - - - final RandomAccessFile os = raf; diff --git a/patches/7724.diff b/patches/7724.diff deleted file mode 100644 index 4b45d2166fdd..000000000000 --- a/patches/7724.diff +++ /dev/null @@ -1,88 +0,0 @@ -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java -index 212f9ee51f..e3121e03c5 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java -@@ -1473,7 +1473,6 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli - refactoring[0] = new RenameRefactoring(Lookups.fixed(lookupContent.toArray(new Object[0]))); - refactoring[0].getContext().add(JavaRefactoringUtils.getClasspathInfoFor(cc.getFileObject())); - refactoring[0].setNewName(params.getNewName()); -- refactoring[0].setSearchInComments(true); //TODO? - } - }, true); - if (cancel.get()) return ; -diff --git a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java -index 684f5db0a2..b18c7850da 100644 ---- a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java -+++ b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java -@@ -3794,6 +3794,13 @@ public class ServerTest extends NbTestCase { - Set actual = edit.getDocumentChanges().stream().map(this::toString).collect(Collectors.toSet()); - Set expected = new HashSet<>(Arrays.asList("Test2.java:[0:27-0:31=>TestNew, 1:4-1:8=>TestNew, 2:11-2:15=>TestNew]", "Test.java:[0:13-0:17=>TestNew]", "Test.java=>TestNew.java")); - assertEquals(expected, actual); -+ }, -+ cf -> { -+ WorkspaceEdit edit = cf.get(); -+ assertTrue(edit.getChanges().isEmpty()); -+ Set actual = edit.getDocumentChanges().stream().map(this::toString).collect(Collectors.toSet()); -+ Set expected = new HashSet<>(Arrays.asList("Test3.java:[3:14-3:22=>arg, 6:36-6:44=>arg, 7:27-7:35=>arg]")); -+ assertEquals(expected, actual); - }); - } - -@@ -3818,12 +3825,20 @@ public class ServerTest extends NbTestCase { - Set actual = edit.getDocumentChanges().stream().map(this::toString).collect(Collectors.toSet()); - Set expected = new HashSet<>(Arrays.asList("Test2.java:[0:27-0:31=>TestNew, 1:4-1:8=>TestNew, 2:11-2:15=>TestNew]", "Test.java:[0:13-0:17=>TestNew]", "Test.java=>TestNew.java")); - assertEquals(expected, actual); -+ }, -+ cf -> { -+ WorkspaceEdit edit = cf.get(); -+ assertTrue(edit.getChanges().isEmpty()); -+ Set actual = edit.getDocumentChanges().stream().map(this::toString).collect(Collectors.toSet()); -+ Set expected = new HashSet<>(Arrays.asList("Test3.java:[3:14-3:22=>arg, 6:36-6:44=>arg, 7:27-7:35=>arg]")); -+ assertEquals(expected, actual); - }); - } - - private void doTestRename(Consumer settings, - Validator> validateFieldRename, -- Validator> validateClassRename) throws Exception { -+ Validator> validateClassRename, -+ Validator> validateArgumentRename) throws Exception { - File src = new File(getWorkDir(), "Test.java"); - src.getParentFile().mkdirs(); - try (Writer w = new FileWriter(new File(src.getParentFile(), ".test-project"))) {} -@@ -3842,6 +3857,20 @@ public class ServerTest extends NbTestCase { - try (Writer w = new FileWriter(src2)) { - w.write(code2); - } -+ File src3 = new File(getWorkDir(), "Test3.java"); -+ String code3 = "public class Test3 {\n" + -+ " /**\n" + -+ " * They had an argument\n" + -+ " * @param argument\n" + -+ " *\n" + -+ " */\n" + -+ " public static void greet(String argument){\n" + -+ " System.out.println(argument);\n" + -+ " }\n" + -+ "}"; -+ try (Writer w = new FileWriter(src3)) { -+ w.write(code3); -+ } - List[] diags = new List[1]; - CountDownLatch indexingComplete = new CountDownLatch(1); - Launcher serverLauncher = createClientLauncherWithLogging(new LspClient() { -@@ -3899,6 +3928,14 @@ public class ServerTest extends NbTestCase { - - validateClassRename.validate(server.getTextDocumentService().rename(params)); - } -+ server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(toURI(src3), "java", 0, code3))); -+ { -+ RenameParams params = new RenameParams(new TextDocumentIdentifier(src3.toURI().toString()), -+ new Position(6, 37), -+ "arg"); -+ -+ validateArgumentRename.validate(server.getTextDocumentService().rename(params)); -+ } - } - - public void testMoveClass() throws Exception { diff --git a/patches/7733.diff b/patches/7733.diff deleted file mode 100644 index 4b7646db5939..000000000000 --- a/patches/7733.diff +++ /dev/null @@ -1,614 +0,0 @@ -diff --git a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/AttributeBasedSingleFileOptions.java b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/AttributeBasedSingleFileOptions.java -index 65e6cd433a03..3312081635cf 100644 ---- a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/AttributeBasedSingleFileOptions.java -+++ b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/AttributeBasedSingleFileOptions.java -@@ -29,12 +29,15 @@ - import org.openide.filesystems.FileUtil; - import org.openide.util.ChangeSupport; - import org.openide.util.Lookup; -+import org.openide.util.RequestProcessor; - import org.openide.util.WeakListeners; - import org.openide.util.lookup.ServiceProvider; - - @ServiceProvider(service=SingleFileOptionsQueryImplementation.class) - public class AttributeBasedSingleFileOptions implements SingleFileOptionsQueryImplementation { - -+ private static final RequestProcessor WORKER = new RequestProcessor(AttributeBasedSingleFileOptions.class.getName(), 1, false, false); -+ - @Override - public Result optionsFor(FileObject file) { - if (!SingleSourceFileUtil.isSupportedFile(file)) { -@@ -66,6 +69,15 @@ private static final class ResultImpl implements Result { - private final FileChangeListener attributeChanges = new FileChangeAdapter() { - @Override - public void fileAttributeChanged(FileAttributeEvent fe) { -+ if (root != null && registerRoot()) { -+ //propagation of flags from files to the root is usually only -+ //started when the root is indexed. And when the registerRoot -+ //flag is flipped to true on a file in a non-indexed root, -+ //there's no other mechanism to propagate the flag to the root. -+ //So, when the flag is set to true on a file, force the propagation -+ //of the flags for the given root: -+ WORKER.post(() -> SharedRootData.ensureRootRegistered(root)); -+ } - cs.fireChange(); - } - }; -@@ -100,6 +112,14 @@ public URI getWorkDirectory() { - return root != null ? root.toURI() : source.getParent().toURI(); - } - -+ @Override -+ public boolean registerRoot() { -+ Object value = source != null ? source.getAttribute(SingleSourceFileUtil.FILE_REGISTER_ROOT) -+ : root != null ? root.getAttribute(SingleSourceFileUtil.FILE_REGISTER_ROOT) -+ : null; -+ return SingleSourceFileUtil.isTrue(value); -+ } -+ - @Override - public void addChangeListener(ChangeListener listener) { - cs.addChangeListener(listener); -diff --git a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/SharedRootData.java b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/SharedRootData.java -index 98530378dd60..7f965bee11e2 100644 ---- a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/SharedRootData.java -+++ b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/SharedRootData.java -@@ -21,10 +21,12 @@ - import java.io.IOException; - import java.util.Enumeration; - import java.util.HashMap; -+import java.util.List; - import java.util.Map; - import java.util.TreeMap; - import java.util.logging.Level; - import java.util.logging.Logger; -+import java.util.stream.Collectors; - import org.netbeans.api.annotations.common.CheckForNull; - import org.netbeans.modules.java.file.launcher.api.SourceLauncher; - import org.openide.filesystems.FileAttributeEvent; -@@ -45,7 +47,13 @@ public class SharedRootData { - private static final Map root2Data = new HashMap<>(); - - public static synchronized void ensureRootRegistered(FileObject root) { -- root2Data.computeIfAbsent(root, r -> new SharedRootData(r)); -+ if (root2Data.get(root) != null) { -+ return ; -+ } -+ -+ SharedRootData data = root2Data.computeIfAbsent(root, r -> new SharedRootData(r)); -+ -+ data.init(); - } - - public static synchronized @CheckForNull SharedRootData getDataForRoot(FileObject root) { -@@ -53,18 +61,18 @@ public static synchronized void ensureRootRegistered(FileObject root) { - } - - private final FileObject root; -- private final Map options = new TreeMap<>(); -+ private final Map properties = new TreeMap<>(); - private final FileChangeListener listener = new FileChangeAdapter() { - @Override - public void fileAttributeChanged(FileAttributeEvent fe) { -- Map newProperties = new HashMap<>(); -+ Map newProperties = new HashMap<>(); - - addPropertiesFor(fe.getFile(), newProperties); - setNewProperties(newProperties); - } - @Override - public void fileDeleted(FileEvent fe) { -- Map newProperties = new HashMap<>(); -+ Map newProperties = new HashMap<>(); - - newProperties.put(FileUtil.getRelativePath(root, fe.getFile()), null); - setNewProperties(newProperties); -@@ -73,9 +81,12 @@ public void fileDeleted(FileEvent fe) { - - private SharedRootData(FileObject root) { - this.root = root; -+ } -+ -+ private void init() { - root.addRecursiveListener(listener); - Enumeration todo = root.getChildren(true); -- Map newProperties = new HashMap<>(); -+ Map newProperties = new HashMap<>(); - while (todo.hasMoreElements()) { - FileObject current = todo.nextElement(); - addPropertiesFor(current, newProperties); -@@ -83,25 +94,32 @@ private SharedRootData(FileObject root) { - setNewProperties(newProperties); - } - -- private void addPropertiesFor(FileObject file, Map newProperties) { -+ private void addPropertiesFor(FileObject file, Map newProperties) { - if (file.isData() && "text/x-java".equals(file.getMIMEType())) { -- newProperties.put(FileUtil.getRelativePath(root, file), (String) file.getAttribute(SingleSourceFileUtil.FILE_VM_OPTIONS)); -+ newProperties.put(FileUtil.getRelativePath(root, file), new FileProperties((String) file.getAttribute(SingleSourceFileUtil.FILE_VM_OPTIONS), -+ SingleSourceFileUtil.isTrue(file.getAttribute(SingleSourceFileUtil.FILE_REGISTER_ROOT)))); - } - } - -- private synchronized void setNewProperties(Map newProperties) { -+ private synchronized void setNewProperties(Map newProperties) { - if (newProperties.isEmpty()) { - return ; - } - for (String key : newProperties.keySet()) { -- String value = newProperties.get(key); -- if (value == null) { -- options.remove(key); -+ FileProperties fileProperties = newProperties.get(key); -+ if (fileProperties == null) { -+ properties.remove(key); - } else { -- options.put(key, value); -+ properties.put(key, fileProperties); - } - } -- String joinedCommandLine = SourceLauncher.joinCommandLines(options.values()); -+ -+ List vmOptions = properties.values() -+ .stream() -+ .map(p -> p.vmOptions) -+ .filter(p -> p != null) -+ .collect(Collectors.toList()); -+ String joinedCommandLine = SourceLauncher.joinCommandLines(vmOptions); - try { - if (!joinedCommandLine.equals(root.getAttribute(SingleSourceFileUtil.FILE_VM_OPTIONS))) { - root.setAttribute(SingleSourceFileUtil.FILE_VM_OPTIONS, joinedCommandLine); -@@ -109,6 +127,21 @@ private synchronized void setNewProperties(Map newProperties) { - } catch (IOException ex) { - LOG.log(Level.INFO, "Failed to set " + SingleSourceFileUtil.FILE_VM_OPTIONS + " for " + root.getPath(), ex); - } -+ Boolean registerRoot = properties.values() -+ .stream() -+ .map(p -> p.registerRoot) -+ .filter(r -> r) -+ .findAny() -+ .isPresent(); -+ try { -+ if (!registerRoot.equals(root.getAttribute(SingleSourceFileUtil.FILE_REGISTER_ROOT))) { -+ root.setAttribute(SingleSourceFileUtil.FILE_REGISTER_ROOT, registerRoot); -+ } -+ } catch (IOException ex) { -+ LOG.log(Level.INFO, "Failed to set " + SingleSourceFileUtil.FILE_REGISTER_ROOT + " for " + root.getPath(), ex); -+ } - } - -+ record FileProperties(String vmOptions, boolean registerRoot) {} -+ - } -diff --git a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/SingleSourceFileUtil.java b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/SingleSourceFileUtil.java -index 0152f195164d..d8fb70bb990a 100644 ---- a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/SingleSourceFileUtil.java -+++ b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/SingleSourceFileUtil.java -@@ -64,6 +64,7 @@ public static int findJavaVersion() throws NumberFormatException { - public static final String FILE_ARGUMENTS = "single_file_run_arguments"; //NOI18N - public static final String FILE_JDK = "single_file_run_jdk"; //NOI18N - public static final String FILE_VM_OPTIONS = "single_file_vm_options"; //NOI18N -+ public static final String FILE_REGISTER_ROOT = "register_root"; //NOI18N - - public static FileObject getJavaFileWithoutProjectFromLookup(Lookup lookup) { - for (DataObject dObj : lookup.lookupAll(DataObject.class)) { -@@ -153,6 +154,10 @@ public static List parseLine(String line, URI workingDirectory) { - return PARSER.doParse(line, workingDirectory); - } - -+ public static boolean isTrue(Object value) { -+ return value instanceof Boolean b && b; -+ } -+ - private static final LineParser PARSER = new LineParser(); - - private static class LineParser extends CompilerOptionsQueryImplementation.Result { -@@ -217,6 +222,10 @@ public URI getWorkDirectory() { - return delegate.getWorkDirectory(); - } - -+ public boolean registerRoot() { -+ return delegate.registerRoot(); -+ } -+ - @Override - public void addChangeListener(ChangeListener listener) { - cs.addChangeListener(listener); -diff --git a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/MultiSourceRootProvider.java b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/MultiSourceRootProvider.java -index 0bfc6bfbc23d..ff0d0ea144b5 100644 ---- a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/MultiSourceRootProvider.java -+++ b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/MultiSourceRootProvider.java -@@ -22,6 +22,7 @@ - import java.beans.PropertyChangeSupport; - import java.io.File; - import java.io.IOException; -+import java.lang.ref.Reference; - import java.net.URL; - import java.util.ArrayList; - import java.util.Arrays; -@@ -90,20 +91,41 @@ public class MultiSourceRootProvider implements ClassPathProvider { - //TODO: the cache will probably be never cleared, as the ClassPath/value refers to the key(?) - private Map file2SourceCP = new WeakHashMap<>(); - private Map root2SourceCP = new WeakHashMap<>(); -+ private Map root2RegistrationRefresh = new WeakHashMap<>(); -+ private final Set registeredRoots = Collections.newSetFromMap(new WeakHashMap<>()); - private Map file2AllPath = new WeakHashMap<>(); - private Map file2ClassPath = new WeakHashMap<>(); - private Map file2ModulePath = new WeakHashMap<>(); - -- static boolean isSupportedFile(FileObject file) { -- return SingleSourceFileUtil.isSingleSourceFile(file) -- // MultiSourceRootProvider assumes it can convert FileObject to -- // java.io.File, so filter here -- && Objects.equals("file", file.toURI().getScheme()); -+ boolean isSupportedFile(FileObject file) { -+ // MultiSourceRootProvider assumes it can convert FileObject to -+ // java.io.File, so filter here -+ if (!Objects.equals("file", file.toURI().getScheme())) { -+ return false; -+ } -+ -+ if (SingleSourceFileUtil.isSingleSourceFile(file)) { -+ return true; -+ } -+ -+ Set registeredRootsCopy; -+ -+ synchronized (registeredRoots) { -+ registeredRootsCopy = registeredRoots; -+ } -+ -+ for (FileObject existingRoot : registeredRootsCopy) { -+ if (file.equals(existingRoot) || FileUtil.isParentOf(existingRoot, file)) { -+ return true; -+ } -+ } -+ -+ return false; - } - - @Override - public ClassPath findClassPath(FileObject file, String type) { -- if (! isSupportedFile(file)) { -+ if (!isSupportedFile(file)) { - return null; - } - switch (type) { -@@ -148,13 +170,17 @@ private ClassPath getSourcePath(FileObject file) { - } - } - -- return root2SourceCP.computeIfAbsent(root, r -> { -- ClassPath srcCP = ClassPathSupport.createClassPath(Arrays.asList(new RootPathResourceImplementation(r))); -- if (registerRoot(r)) { -- GlobalPathRegistry.getDefault().register(ClassPath.SOURCE, new ClassPath[] {srcCP}); -- } -- return srcCP; -+ ClassPath srcCP = root2SourceCP.computeIfAbsent(root, r -> { -+ return ClassPathSupport.createClassPath(Arrays.asList(new RootPathResourceImplementation(r))); - }); -+ -+ ParsedFileOptions options = SingleSourceFileUtil.getOptionsFor(root); -+ -+ if (options != null) { -+ WORKER.post(root2RegistrationRefresh.computeIfAbsent(root, r -> new RegistrationRefresh(srcCP, options, r))); -+ } -+ -+ return srcCP; - } catch (IOException ex) { - LOG.log(Level.FINE, "Failed to read sourcefile " + file, ex); - } -@@ -269,13 +295,6 @@ private ClassPath attributeBasedPath(FileObject file, Map - } - } - -- @Messages({ -- "SETTING_AutoRegisterAsRoot=false" -- }) -- private static boolean registerRoot(FileObject root) { -- return "true".equals(Bundle.SETTING_AutoRegisterAsRoot()); -- } -- - private static final class AttributeBasedClassPathImplementation extends FileChangeAdapter implements ChangeListener, ClassPathImplementation { - private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); - private final Task updateDelegatesTask = WORKER.create(this::doUpdateDelegates); -@@ -355,7 +374,10 @@ private void doUpdateDelegates() { - for (File expanded : expandedPaths) { - URL u = FileUtil.urlForArchiveOrDir(expanded); - if (u == null) { -- throw new IllegalArgumentException("Path entry looks to be invalid: " + piece); // NOI18N -+ LOG.log(Level.INFO, -+ "While parsing command line option '{0}' with parameter '{1}', path entry looks to be invalid: '{2}'", -+ new Object[] {currentOption, parsed.get(i + 1), piece}); -+ continue; - } - newURLs.add(u); - newDelegates.add(ClassPathSupport.createResource(u)); -@@ -468,4 +490,43 @@ public void removePropertyChangeListener(PropertyChangeListener listener) { - } - - } -+ -+ private class RegistrationRefresh implements ChangeListener, Runnable { -+ private final ClassPath srcCP; -+ private final ParsedFileOptions options; -+ private final FileObject root; -+ -+ public RegistrationRefresh(ClassPath srcCP, -+ ParsedFileOptions options, -+ FileObject root) { -+ this.srcCP = srcCP; -+ this.options = options; -+ this.root = root; -+ options.addChangeListener(this); -+ } -+ -+ @Override -+ public void run() { -+ GlobalPathRegistry registry = GlobalPathRegistry.getDefault(); -+ if (options.registerRoot()) { -+ synchronized (registeredRoots) { -+ registeredRoots.add(root); -+ } -+ registry.register(ClassPath.SOURCE, new ClassPath[] {srcCP}); -+ } else { -+ synchronized (registeredRoots) { -+ registeredRoots.remove(root); -+ } -+ if (registry.getPaths(ClassPath.SOURCE).contains(srcCP)) { -+ registry.unregister(ClassPath.SOURCE, new ClassPath[] {srcCP}); -+ } -+ } -+ } -+ -+ @Override -+ public void stateChanged(ChangeEvent e) { -+ WORKER.post(this); -+ } -+ } -+ - } -diff --git a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/spi/SingleFileOptionsQueryImplementation.java b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/spi/SingleFileOptionsQueryImplementation.java -index 216706463c3b..cd73cd5e028b 100644 ---- a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/spi/SingleFileOptionsQueryImplementation.java -+++ b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/spi/SingleFileOptionsQueryImplementation.java -@@ -28,10 +28,13 @@ public interface SingleFileOptionsQueryImplementation { - public Result optionsFor(FileObject file); - - public interface Result { -- public String getOptions(); -+ public @NonNull String getOptions(); - public default @NonNull URI getWorkDirectory() { - throw new UnsupportedOperationException(); - } -+ public default boolean registerRoot() { -+ return false; -+ } - public void addChangeListener(ChangeListener l); - public void removeChangeListener(ChangeListener l); - } -diff --git a/java/java.lsp.server/nbcode/branding/modules/org-netbeans-modules-java-file-launcher.jar/org/netbeans/modules/java/file/launcher/queries/Bundle.properties b/java/java.file.launcher/test/unit/src/org/netbeans/modules/java/file/launcher/queries/Bundle_registerroots.properties -similarity index 99% -rename from java/java.lsp.server/nbcode/branding/modules/org-netbeans-modules-java-file-launcher.jar/org/netbeans/modules/java/file/launcher/queries/Bundle.properties -rename to java/java.file.launcher/test/unit/src/org/netbeans/modules/java/file/launcher/queries/Bundle_registerroots.properties -index 684c2d3a5fff..d13c21373f31 100644 ---- a/java/java.lsp.server/nbcode/branding/modules/org-netbeans-modules-java-file-launcher.jar/org/netbeans/modules/java/file/launcher/queries/Bundle.properties -+++ b/java/java.file.launcher/test/unit/src/org/netbeans/modules/java/file/launcher/queries/Bundle_registerroots.properties -@@ -14,5 +14,4 @@ - # KIND, either express or implied. See the License for the - # specific language governing permissions and limitations - # under the License. -- - SETTING_AutoRegisterAsRoot=true -diff --git a/java/java.file.launcher/test/unit/src/org/netbeans/modules/java/file/launcher/queries/MultiSourceRootProviderTest.java b/java/java.file.launcher/test/unit/src/org/netbeans/modules/java/file/launcher/queries/MultiSourceRootProviderTest.java -index ff7245d3af48..c89798daad2c 100644 ---- a/java/java.file.launcher/test/unit/src/org/netbeans/modules/java/file/launcher/queries/MultiSourceRootProviderTest.java -+++ b/java/java.file.launcher/test/unit/src/org/netbeans/modules/java/file/launcher/queries/MultiSourceRootProviderTest.java -@@ -19,11 +19,14 @@ - package org.netbeans.modules.java.file.launcher.queries; - - import java.io.File; -+import java.io.FileOutputStream; - import java.io.IOException; -+import java.io.Writer; - import java.net.URI; - import java.nio.file.Files; - import java.util.Arrays; - import java.util.HashSet; -+import java.util.concurrent.atomic.AtomicBoolean; - import java.util.concurrent.atomic.AtomicInteger; - import java.util.concurrent.atomic.AtomicReference; - import javax.swing.event.ChangeListener; -@@ -31,12 +34,15 @@ - import org.netbeans.api.java.classpath.JavaClassPathConstants; - import org.netbeans.api.java.source.TestUtilities; - import org.netbeans.junit.NbTestCase; -+import org.netbeans.modules.java.file.launcher.SingleSourceFileUtil; - import org.netbeans.modules.java.file.launcher.spi.SingleFileOptionsQueryImplementation; - import org.netbeans.modules.java.file.launcher.spi.SingleFileOptionsQueryImplementation.Result; - import org.openide.filesystems.FileObject; - import org.openide.filesystems.FileUtil; - import org.openide.util.ChangeSupport; -+import org.openide.util.Exceptions; - import org.openide.util.Lookup; -+import org.openide.util.NbBundle; - import org.openide.util.lookup.Lookups; - import org.openide.util.lookup.ProxyLookup; - -@@ -254,9 +260,10 @@ public void testMultiSourceRootProviderOnlySupportedForLocalFiles() throws IOExc - supportedFile = Files.createTempFile("dummy", ".java").toFile(); - FileObject realFileSource = FileUtil.createData(supportedFile); - FileObject inMemorySource = FileUtil.createMemoryFileSystem().getRoot().createData("Ahoj.java"); -+ MultiSourceRootProvider provider = new MultiSourceRootProvider(); - -- assertFalse(MultiSourceRootProvider.isSupportedFile(inMemorySource)); -- assertTrue(MultiSourceRootProvider.isSupportedFile(realFileSource)); -+ assertFalse(provider.isSupportedFile(inMemorySource)); -+ assertTrue(provider.isSupportedFile(realFileSource)); - } finally { - if(supportedFile != null && supportedFile.exists()) { - supportedFile.delete(); -@@ -264,6 +271,45 @@ public void testMultiSourceRootProviderOnlySupportedForLocalFiles() throws IOExc - } - } - -+ public void testMultiSourceRootProviderRespondsForKnownFolders() throws IOException { -+ File wd = getWorkDir(); -+ File testDir = new File(wd, "test"); -+ File packDir = new File(testDir, "pack"); -+ File testFile = new File(packDir, "Test.java"); -+ -+ packDir.mkdirs(); -+ -+ try (Writer w = Files.newBufferedWriter(testFile.toPath())) { -+ w.write("package pack;"); -+ } -+ -+ testResult.setOptions(""); -+ testResult.setWorkDirectory(testDir.toURI()); -+ -+ MultiSourceRootProvider provider = new MultiSourceRootProvider(); -+ -+ //before recongizing testDir is a multi-source file root: -+ assertNull(provider.findClassPath(FileUtil.toFileObject(wd), ClassPath.SOURCE)); -+ assertNull(provider.findClassPath(FileUtil.toFileObject(testDir), ClassPath.SOURCE)); -+ assertNull(provider.findClassPath(FileUtil.toFileObject(packDir), ClassPath.SOURCE)); -+ -+ //recognize the source file as a multi-source file: -+ ClassPath cp = provider.findClassPath(FileUtil.toFileObject(testFile), ClassPath.SOURCE); -+ -+ assertNotNull(cp); -+ -+ //check properties: -+ assertNull(provider.findClassPath(FileUtil.toFileObject(wd), ClassPath.SOURCE)); -+ assertNull(provider.findClassPath(FileUtil.toFileObject(testDir), ClassPath.SOURCE)); -+ assertNull(provider.findClassPath(FileUtil.toFileObject(packDir), ClassPath.SOURCE)); -+ -+ testResult.setRegisterRoot(true); -+ -+ assertNull(provider.findClassPath(FileUtil.toFileObject(wd), ClassPath.SOURCE)); -+ assertSame(cp, provider.findClassPath(FileUtil.toFileObject(testDir), ClassPath.SOURCE)); -+ assertSame(cp, provider.findClassPath(FileUtil.toFileObject(packDir), ClassPath.SOURCE)); -+ } -+ - @Override - protected void setUp() throws Exception { - super.setUp(); -@@ -294,6 +340,7 @@ private static class TestResultImpl implements Result { - private final ChangeSupport cs = new ChangeSupport(this); - private final AtomicReference options = new AtomicReference<>(); - private final AtomicReference workdir = new AtomicReference<>(); -+ private final AtomicBoolean registerRoot = new AtomicBoolean(); - - public TestResultImpl() { - } -@@ -318,6 +365,16 @@ public void setWorkDirectory(URI workdir) { - cs.fireChange(); - } - -+ @Override -+ public boolean registerRoot() { -+ return registerRoot.get(); -+ } -+ -+ public void setRegisterRoot(boolean registerRoot) { -+ this.registerRoot.set(registerRoot); -+ cs.fireChange(); -+ } -+ - @Override - public void addChangeListener(ChangeListener l) { - cs.addChangeListener(l); -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/singlesourcefile/SingleFileOptionsQueryImpl.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/singlesourcefile/SingleFileOptionsQueryImpl.java -index da4898786f11..912078b072d6 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/singlesourcefile/SingleFileOptionsQueryImpl.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/singlesourcefile/SingleFileOptionsQueryImpl.java -@@ -118,7 +118,8 @@ public ResultImpl(Map workspaceFolders2Results, - - @Override - public String getOptions() { -- return workspaceSettings.getOptions(); -+ String options = workspaceSettings.getOptions(); -+ return options != null ? options : ""; - } - - @Override -@@ -129,6 +130,11 @@ public URI getWorkDirectory() { - return workDir.toURI(); - } - -+ @Override -+ public boolean registerRoot() { -+ return true; -+ } -+ - @Override - public void addChangeListener(ChangeListener l) { - cs.addChangeListener(l); -diff --git a/java/java.source/src/org/netbeans/modules/java/Bundle.properties b/java/java.source/src/org/netbeans/modules/java/Bundle.properties -index 74949b6a2eb2..1d3268d9067e 100644 ---- a/java/java.source/src/org/netbeans/modules/java/Bundle.properties -+++ b/java/java.source/src/org/netbeans/modules/java/Bundle.properties -@@ -37,6 +37,8 @@ PROP_JavaNode_singlefile_arguments=Program Arguments - HINT_JavaNode_singlefile_arguments=Arguments passed to the main method while running the file. - PROP_JavaNode_singlefile_options=VM Options - HINT_JavaNode_singlefile_options=VM Options to be considered while running the file. -+PROP_JavaNode_singlefile_registerRoot=Enable Source File Launcher Indexing -+HINT_JavaNode_singlefile_registerRoot=The root corresponding to this file should have source file launcher indexing enabled - PROP_JavaNode_classfile_version=Classfile Version - HINT_JavaNode_classfile_version=The Java API and Language Level of this class file - PROP_JavaNode_compile_classpath=Compile Classpath -diff --git a/java/java.source/src/org/netbeans/modules/java/JavaNode.java b/java/java.source/src/org/netbeans/modules/java/JavaNode.java -index 6dec93234e29..60bfeced4598 100644 ---- a/java/java.source/src/org/netbeans/modules/java/JavaNode.java -+++ b/java/java.source/src/org/netbeans/modules/java/JavaNode.java -@@ -111,6 +111,7 @@ public final class JavaNode extends DataNode implements ChangeListener { - private static final String FILE_ARGUMENTS = "single_file_run_arguments"; //NOI18N - private static final String FILE_JDK = "single_file_run_jdk"; //NOI18N - private static final String FILE_VM_OPTIONS = "single_file_vm_options"; //NOI18N -+ private static final String FILE_REGISTER_ROOT = "register_root"; //NOI18N - - private static final Map IMAGE_CACHE = new ConcurrentHashMap<>(); - private static final boolean ALWAYS_PREFFER_COMPUTED_ICON = Boolean.getBoolean("JavaNode.prefferComputedIcon"); //NOI18N -@@ -242,6 +243,7 @@ protected final Sheet createSheet () { - ss.setName("runFileArguments"); // NOI18N - ss.setDisplayName(getMessage(JavaNode.class, "LBL_JavaNode_without_project_run")); // NOI18N - ss.setShortDescription("Run the file's source code."); -+ ss.put(new JavaFileBooleanAttributeProperty(dObj, FILE_REGISTER_ROOT, "registerRoot", "singlefile_registerRoot")); // NOI18N - ss.put(new RunFileJDKProperty(dObj)); - ss.put(new JavaFileAttributeProperty(dObj, FILE_ARGUMENTS, "runFileArguments", "singlefile_arguments")); // NOI18N - ss.put(new JavaFileAttributeProperty(dObj, FILE_VM_OPTIONS, "runFileVMOptions", "singlefile_options")); // NOI18N -@@ -466,6 +468,33 @@ public Component getCustomEditor() { - - } - -+ // editable file attribute -+ private static final class JavaFileBooleanAttributeProperty extends PropertySupport.ReadWrite { -+ -+ private final String attribute; -+ private final DataObject dObj; -+ -+ public JavaFileBooleanAttributeProperty(DataObject dObj, String attribute, String name, String msgKeyPart) { -+ super(name, Boolean.class, getMessage(JavaNode.class, "PROP_JavaNode_" + msgKeyPart), getMessage(JavaNode.class, "HINT_JavaNode_" + msgKeyPart)); // NOI18N -+ this.dObj = dObj; -+ this.attribute = attribute; -+ } -+ -+ @Override -+ public Boolean getValue() { -+ return dObj.getPrimaryFile().getAttribute(attribute) instanceof Boolean val ? val : false; -+ } -+ -+ @Override -+ public void setValue(Boolean o) { -+ try { -+ dObj.getPrimaryFile().setAttribute(attribute, o); -+ } catch (IOException ex) { -+ LOG.log(Level.WARNING, "Java File does not exist : {0}", dObj.getPrimaryFile().getName()); //NOI18N -+ } -+ } -+ } -+ - // editable file attribute - private static final class JavaFileAttributeProperty extends PropertySupport.ReadWrite { - diff --git a/patches/7750.diff b/patches/7750.diff deleted file mode 100644 index ac35b6046196..000000000000 --- a/patches/7750.diff +++ /dev/null @@ -1,293 +0,0 @@ -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java -index c112b4eb73..ff1fe7f903 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java -@@ -44,14 +44,18 @@ import java.net.MalformedURLException; - import java.net.URISyntaxException; - import java.nio.file.Path; - import java.nio.file.Paths; -+import java.util.ArrayDeque; - import java.util.ArrayList; - import java.util.Arrays; - import java.util.Collection; - import java.util.Collections; -+import java.util.Comparator; -+import java.util.Deque; - import java.util.EnumMap; - import java.util.EnumSet; - import java.util.HashMap; - import java.util.HashSet; -+import java.util.Iterator; - import java.util.LinkedHashMap; - import java.util.List; - import java.util.Locale; -@@ -1561,12 +1565,13 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli - if (source == null) { - return CompletableFuture.completedFuture(Collections.emptyList()); - } -+ final boolean lineFoldingOnly = client.getNbCodeCapabilities().getClientCapabilities().getTextDocument().getFoldingRange().getLineFoldingOnly() == Boolean.TRUE; - CompletableFuture> result = new CompletableFuture<>(); - try { - source.runUserActionTask(cc -> { - cc.toPhase(JavaSource.Phase.RESOLVED); - Document doc = cc.getSnapshot().getSource().getDocument(true); -- JavaElementFoldVisitor v = new JavaElementFoldVisitor(cc, cc.getCompilationUnit(), cc.getTrees().getSourcePositions(), doc, new FoldCreator() { -+ JavaElementFoldVisitor v = new JavaElementFoldVisitor<>(cc, cc.getCompilationUnit(), cc.getTrees().getSourcePositions(), doc, new FoldCreator() { - @Override - public FoldingRange createImportsFold(int start, int end) { - return createFold(start, end, FoldingRangeKind.Imports); -@@ -1611,7 +1616,10 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli - }); - v.checkInitialFold(); - v.scan(cc.getCompilationUnit(), null); -- result.complete(v.getFolds()); -+ List folds = v.getFolds(); -+ if (lineFoldingOnly) -+ folds = convertToLineOnlyFolds(folds); -+ result.complete(folds); - }, true); - } catch (IOException ex) { - result.completeExceptionally(ex); -@@ -1619,6 +1627,76 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli - return result; - } - -+ /** -+ * Converts a list of code-folds to a line-only Range form, in place of the -+ * finer-grained form of {@linkplain Position Position-based} (line, column) Ranges. -+ *

-+ * This is needed for LSP clients that do not support the finer grained Range -+ * specification. This is expected to be advertised by the client in -+ * {@code FoldingRangeClientCapabilities.lineFoldingOnly}. -+ * -+ * @implSpec The line-only ranges computed uphold the code-folding invariant that: -+ * a fold does not end at the same point where another fold starts. -+ * -+ * @implNote This is performed in {@code O(n log n) + O(n)} time and {@code O(n)} space for the returned list. -+ * -+ * @param folds List of code-folding ranges computed for a textDocument, -+ * containing fine-grained {@linkplain Position Position-based} -+ * (line, column) ranges. -+ * @return List of code-folding ranges computed for a textDocument, -+ * containing coarse-grained line-only ranges. -+ * -+ * @see -+ * LSP FoldingRangeClientCapabilities -+ */ -+ static List convertToLineOnlyFolds(List folds) { -+ if (folds != null && folds.size() > 1) { -+ // Ensure that the folds are sorted in increasing order of their start position -+ folds = new ArrayList<>(folds); -+ folds.sort(Comparator.comparingInt(FoldingRange::getStartLine) -+ .thenComparing(FoldingRange::getStartCharacter)); -+ // Maintain a stack of enclosing folds -+ Deque enclosingFolds = new ArrayDeque<>(); -+ for (FoldingRange fold : folds) { -+ FoldingRange last; -+ while ((last = enclosingFolds.peek()) != null && -+ (last.getEndLine() < fold.getEndLine() || -+ (last.getEndLine() == fold.getEndLine() && last.getEndCharacter() < fold.getEndCharacter()))) { -+ // The last enclosingFold does not enclose this fold. -+ // Due to sortedness of the folds, last also ends before this fold starts. -+ enclosingFolds.pop(); -+ // If needed, adjust last to end on a line prior to this fold start -+ if (last.getEndLine() == fold.getStartLine()) { -+ last.setEndLine(last.getEndLine() - 1); -+ } -+ last.setEndCharacter(null); // null denotes the end of the line. -+ last.setStartCharacter(null); // null denotes the end of the line. -+ } -+ enclosingFolds.push(fold); -+ } -+ // empty the stack; since each fold completely encloses the next higher one. -+ FoldingRange fold; -+ while ((fold = enclosingFolds.poll()) != null) { -+ fold.setEndCharacter(null); // null denotes the end of the line. -+ fold.setStartCharacter(null); // null denotes the end of the line. -+ } -+ // Remove invalid or duplicate folds -+ Iterator it = folds.iterator(); -+ FoldingRange prev = null; -+ while(it.hasNext()) { -+ FoldingRange next = it.next(); -+ if (next.getEndLine() <= next.getStartLine() || -+ (prev != null && prev.equals(next))) { -+ it.remove(); -+ } else { -+ prev = next; -+ } -+ } -+ } -+ return folds; -+ } -+ -+ - @Override - public void didOpen(DidOpenTextDocumentParams params) { - LOG.log(Level.FINER, "didOpen: {0}", params); -diff --git a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImplTest.java b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImplTest.java -index 0f2bda50ae..06fd93d3e5 100644 ---- a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImplTest.java -+++ b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImplTest.java -@@ -18,14 +18,19 @@ - */ - package org.netbeans.modules.java.lsp.server.protocol; - -+import java.util.Collections; -+import java.util.List; - import java.util.concurrent.atomic.AtomicInteger; - import javax.swing.event.DocumentEvent; - import javax.swing.event.DocumentListener; - import javax.swing.text.BadLocationException; - import javax.swing.text.Document; - import javax.swing.text.PlainDocument; -+import org.eclipse.lsp4j.FoldingRange; - import org.netbeans.junit.NbTestCase; - -+import static org.netbeans.modules.java.lsp.server.protocol.TextDocumentServiceImpl.convertToLineOnlyFolds; -+ - public class TextDocumentServiceImplTest extends NbTestCase { - - public TextDocumentServiceImplTest(String name) { -@@ -117,4 +122,141 @@ public class TextDocumentServiceImplTest extends NbTestCase { - fail(String.valueOf(e)); - } - } -+ -+ public void testConvertToLineOnlyFolds() { -+ assertNull(convertToLineOnlyFolds(null)); -+ assertEquals(0, convertToLineOnlyFolds(Collections.emptyList()).size()); -+ List inputFolds, outputFolds; -+ inputFolds = Collections.singletonList(createRange(10, 20)); -+ assertEquals(inputFolds, convertToLineOnlyFolds(inputFolds)); -+ -+ // test stable sort by start index -+ inputFolds = List.of(createRange(10, 20, 9, 9), createRange(5, 9, 9, 9), createRange(10, 19, 9, 9), createRange(10, 14, 13, 13)); -+ outputFolds = List.of(createRange(5, 9), createRange(10, 20), createRange(10, 19), createRange(10, 14)); -+ assertEquals(outputFolds, convertToLineOnlyFolds(inputFolds)); -+ -+ // test already disjoint folds -+ inputFolds = List.of(createRange(10, 20, 9, 9), createRange(5, 9, 9, 9), createRange(15, 19, 13, 13), createRange(10, 14, 13, 13)); -+ outputFolds = List.of(createRange(5, 9), createRange(10, 20), createRange(10, 14), createRange(15, 19)); -+ assertEquals(outputFolds, convertToLineOnlyFolds(inputFolds)); -+ -+ // test invariant of range.endLine: there exists no otherRange.startLine == range.endLine. -+ inputFolds = List.of(createRange(10, 20, 35, 9), createRange(5, 10, 12, 9), createRange(15, 19, 20, 13), createRange(10, 15, 51, 13)); -+ assertEquals(outputFolds, convertToLineOnlyFolds(inputFolds)); -+ -+ // test a complex example of a full file: -+//import java.util.ArrayList; -+//import java.util.Collection; -+//import java.util.Collections; -+// -+///** -+// * A top-class action performer -+// * -+// * @since 1.1 -+// */ -+//public class TopClass { -+// -+// private final String action; -+// private final int index; -+// -+// /** -+// * @param action Top action to be done -+// */ -+// public TopClass(String action) { -+// this(action, 0); -+// } -+// -+// /** -+// * @param action Top action to be done -+// * @param index Action index -+// */ -+// public TopClass(String action, int index) { -+// this.action = action; -+// this.index = index; -+// } -+// -+// public void doSomethingTopClass(TopClass tc) { -+// // what can we do -+// { -+// if (tc == this) { -+// return; -+// } else if (tc.getClass() == this.getClass()) { -+// } else if (tc.getClass().isAssignableFrom(this.getClass())) { -+// -+// } else { -+// if (true) { -+// switch (tc) { -+// default: { /* this is some comment */ ; } -+// /// some outside default -+// } -+// } else { if (true) { { /* some */ } { /* bad blocks */ } -+// }} -+// /* done */ -+// } -+// } -+// tc.doSomethingTopClass(tc); -+// } -+// -+// public class InnerClass { -+// @Override -+// public String toString() { -+// StringBuilder sb = new StringBuilder(); -+// sb.append("InnerClass{"); -+// sb.append("action=").append(action); -+// sb.append(", index=").append(index); -+// sb.append('}'); -+// return sb.toString(); -+// } -+// } -+//} -+ inputFolds = List.of( -+ createRange(27, 30, 48, 5), -+ createRange(0, 3, 7, 30), -+ createRange(32, 52, 51, 5), -+ createRange(37, 38, 59, 13), -+ createRange(34, 50, 10, 9), -+ createRange(46, 46, 39, 51), -+ createRange(35, 37, 30, 13), -+ createRange(38, 40, 74, 13), -+ createRange(40, 49, 21, 13), -+ createRange(46, 47, 37, 17), -+ createRange(41, 46, 28, 17), -+ createRange(42, 45, 34, 21), -+ createRange(11, 66, 24, 1), -+ createRange(43, 43, 35, 65), -+ createRange(46, 47, 25, 18), -+ createRange(54, 64, 30, 5), -+ createRange(46, 46, 54, 72), -+ createRange(6, 10, 4, 1), -+ createRange(56, 63, 35, 9) -+ ); -+ outputFolds = List.of( -+ createRange(0, 3), -+ createRange(6, 10), -+ createRange(11, 66), -+ createRange(27, 30), -+ createRange(32, 52), -+ createRange(34, 50), -+ createRange(35, 36), -+ createRange(38, 39), -+ createRange(40, 49), -+ createRange(41, 45), -+ createRange(42, 45), -+ createRange(46, 47), -+ createRange(54, 64), -+ createRange(56, 63) -+ ); -+ assertEquals(outputFolds, convertToLineOnlyFolds(inputFolds)); -+ } -+ -+ private static FoldingRange createRange(int startLine, int endLine) { -+ return new FoldingRange(startLine, endLine); -+ } -+ -+ private static FoldingRange createRange(int startLine, int endLine, Integer startColumn, Integer endColumn) { -+ FoldingRange foldingRange = new FoldingRange(startLine, endLine); -+ foldingRange.setStartCharacter(startColumn); -+ foldingRange.setEndCharacter(endColumn); -+ return foldingRange; -+ } - } diff --git a/patches/7910.diff b/patches/7910.diff deleted file mode 100644 index 6a85c4f09e51..000000000000 --- a/patches/7910.diff +++ /dev/null @@ -1,249 +0,0 @@ -diff --git a/.gitattributes b/.gitattributes -index b96c7fd985..7c9b72b889 100644 ---- a/.gitattributes -+++ b/.gitattributes -@@ -35,3 +35,7 @@ - - # Define some file types explicitly as being binary. - *.jar binary -+ -+# Define rules for files in specific paths, such as test data, -+# for line endings, binary types etc. -+java/java.hints/test/unit/data/goldenfiles/** text eol=lf -diff --git a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/infrastructure/TreeRuleTestBase.java b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/infrastructure/TreeRuleTestBase.java -index ede2a586e888..a351d52bd5fc 100644 ---- a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/infrastructure/TreeRuleTestBase.java -+++ b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/infrastructure/TreeRuleTestBase.java -@@ -242,9 +242,11 @@ protected String performFixTest(String fileName, String code, int pos, String er - String realCode = toCheckDocument.getText(0, toCheckDocument.getLength()); - - //ignore whitespaces: -- realCode = realCode.replaceAll("[ \t\n]+", " "); -+ realCode = realCode.replaceAll("\\s+", " "); - - if (golden != null) { -+ //ignore CRLF/LF differences in golden: -+ if (!"\n".equals(System.lineSeparator())) golden = golden.replaceAll(System.lineSeparator(), " "); - assertEquals("The output code does not match the expected code.", golden, realCode); - } - -diff --git a/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/indexing/VanillaCompileWorkerTest.java b/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/indexing/VanillaCompileWorkerTest.java -index bcd1c07c8701..fb4f11400726 100644 ---- a/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/indexing/VanillaCompileWorkerTest.java -+++ b/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/indexing/VanillaCompileWorkerTest.java -@@ -685,6 +685,7 @@ public void testPreserveValidMethods1() throws Exception { - " System.err.println(\"Hello, world!\");\n" + - " }\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -729,6 +730,7 @@ public void testClearInvalidMethod() throws Exception { - " throw new java.lang.RuntimeException(\"Uncompilable code - compiler.err.cant.resolve.location\");\n" + - " }\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -792,6 +794,7 @@ public void testPreserveValidInitializers() throws Exception { - " private int F5a;\n" + - " private int F5b;\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -834,6 +837,7 @@ public void testBrokenClassHeader1() throws Exception { - " throw new java.lang.RuntimeException(\"Uncompilable code\");\n" + - " }\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -883,6 +887,7 @@ public void testNullReturnUnknown() throws Exception { - " throw new java.lang.RuntimeException(\"Uncompilable code\");\n" + - " }\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -932,6 +937,7 @@ public void testBrokenNewClass() throws Exception { - " throw new java.lang.RuntimeException(\"Uncompilable code\");\n" + - " }\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -983,6 +989,7 @@ public void testReturnBroken() throws Exception { - " throw new java.lang.RuntimeException(\"Uncompilable code\");\n" + - " }\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -1037,6 +1044,7 @@ public void testAssertBroken() throws Exception { - " throw new java.lang.RuntimeException(\"Uncompilable code\");\n" + - " }\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -1117,6 +1125,7 @@ public void testAnonymousComplex() throws Exception { - " };\n" + - " }\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -1165,6 +1174,7 @@ public void testFieldInit() throws Exception { - " super();\n" + - " }\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -1239,6 +1249,7 @@ public void testAnonymousComplex2() throws Exception { - " }\n" + - " }\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -1285,6 +1296,7 @@ public void testNewClass() throws Exception { - " throw new java.lang.RuntimeException(\"Uncompilable code - compiler.err.cant.apply.symbol\");\n" + - " }\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -1336,6 +1348,7 @@ public void testUndefNewArray() throws Exception { - " throw new java.lang.RuntimeException(\"Uncompilable code\");\n" + - " }\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -1412,6 +1425,7 @@ public void testUndefAnonymous() throws Exception { - " }\n" + - " }\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -1471,6 +1485,7 @@ public void testWeirdSuperCall() throws Exception { - " }\n" + - " }\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -1590,6 +1605,7 @@ public void testAnonymousComplex3() throws Exception { - " }\n" + - " }\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -1690,6 +1706,7 @@ public void testAnonymousComplex4() throws Exception { - " throw new java.lang.RuntimeException(\"Uncompilable code - compiler.err.cant.resolve.location.args\");\n" + - " }\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -1763,6 +1780,7 @@ public void testAnonymousComplexCorrect() throws Exception { - " }\n" + - " }\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -1806,6 +1824,7 @@ public void testWarningsAreNotErrors() throws Exception { - " }\n" + - " Test t;\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -1878,6 +1897,7 @@ public void testSuperCall() throws Exception { - " public default void test2() {\n" + - " }\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -1923,6 +1943,7 @@ public void testStaticInit() throws Exception { - " System.err.println();\n" + - " }\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -2161,6 +2182,7 @@ public void testMethodWithErroneousInMemberRef() throws Exception { - " \n" + - " public Object test(Object o);\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -2242,6 +2264,7 @@ public void testPatternSwitch() throws Exception { - " throw new java.lang.RuntimeException(\"Uncompilable code\");\n" + - " }\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -2294,6 +2317,7 @@ public void testTypeTest() throws Exception { - " }\n" + - " }\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -2339,6 +2363,7 @@ public void testWrongRecordComponent() throws Exception { - " }\n" + - " private final int wait;\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -2391,6 +2416,7 @@ public void testRecord1() throws Exception { - " }\n" + - " private final int i;\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - -@@ -2428,6 +2454,7 @@ public void testRecord2() throws Exception { - " }\n" + - " private final int i;\n" + - "}"); -+ if (!"\n".equals(System.lineSeparator())) file2Fixed.replaceAll((k, v) -> v.replaceAll(System.lineSeparator(), "\n")); - assertEquals(expected, file2Fixed); - } - diff --git a/patches/7921.diff b/patches/7921.diff deleted file mode 100644 index 75f831d6c920..000000000000 --- a/patches/7921.diff +++ /dev/null @@ -1,34 +0,0 @@ -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java -index 7a5a5f40e4f9..85c223130e32 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java -@@ -128,6 +128,7 @@ - import org.eclipse.lsp4j.DocumentSymbol; - import org.eclipse.lsp4j.DocumentSymbolParams; - import org.eclipse.lsp4j.FoldingRange; -+import org.eclipse.lsp4j.FoldingRangeCapabilities; - import org.eclipse.lsp4j.FoldingRangeKind; - import org.eclipse.lsp4j.FoldingRangeRequestParams; - import org.eclipse.lsp4j.Hover; -@@ -160,6 +161,7 @@ - import org.eclipse.lsp4j.SignatureHelpParams; - import org.eclipse.lsp4j.SignatureInformation; - import org.eclipse.lsp4j.SymbolInformation; -+import org.eclipse.lsp4j.TextDocumentClientCapabilities; - import org.eclipse.lsp4j.TextDocumentContentChangeEvent; - import org.eclipse.lsp4j.TextDocumentEdit; - import org.eclipse.lsp4j.TextEdit; -@@ -1638,7 +1640,12 @@ public CompletableFuture> foldingRange(FoldingRangeRequestPar - if (source == null) { - return CompletableFuture.completedFuture(Collections.emptyList()); - } -- final boolean lineFoldingOnly = client.getNbCodeCapabilities().getClientCapabilities().getTextDocument().getFoldingRange().getLineFoldingOnly() == Boolean.TRUE; -+ ClientCapabilities clientCapabilities = client.getNbCodeCapabilities() -+ .getClientCapabilities(); -+ TextDocumentClientCapabilities textDocumentCapabilities = clientCapabilities != null ? clientCapabilities.getTextDocument() : null; -+ FoldingRangeCapabilities foldingRangeCapabilities = textDocumentCapabilities != null ? textDocumentCapabilities.getFoldingRange() : null; -+ Boolean lineFoldingOnlyCapability = foldingRangeCapabilities != null ? foldingRangeCapabilities.getLineFoldingOnly() : null; -+ final boolean lineFoldingOnly = lineFoldingOnlyCapability == Boolean.TRUE; - CompletableFuture> result = new CompletableFuture<>(); - try { - source.runUserActionTask(cc -> { diff --git a/patches/7923.diff b/patches/7923.diff deleted file mode 100644 index a87ce06f3930..000000000000 --- a/patches/7923.diff +++ /dev/null @@ -1,24 +0,0 @@ -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/refactoring/MoveRefactoring.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/refactoring/MoveRefactoring.java -index a72abd44ef..12acbc7081 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/refactoring/MoveRefactoring.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/refactoring/MoveRefactoring.java -@@ -420,6 +420,9 @@ public final class MoveRefactoring extends CodeRefactoring { - } - - private static Project getSelectedProject(NamedPath selectedProject) { -+ if (selectedProject == null) { -+ return null; -+ } - try { - String path = selectedProject.getPath(); - return path != null ? FileOwnerQuery.getOwner(Utils.fromUri(path)) : null; -@@ -429,6 +432,9 @@ public final class MoveRefactoring extends CodeRefactoring { - } - - private static FileObject getSelectedRoot(NamedPath selectedRoot) { -+ if (selectedRoot == null) { -+ return null; -+ } - try { - String path = selectedRoot.getPath(); - return path != null ? Utils.fromUri(path) : null; diff --git a/patches/7926.diff b/patches/7926.diff deleted file mode 100644 index 1c1f3ef1539e..000000000000 --- a/patches/7926.diff +++ /dev/null @@ -1,54 +0,0 @@ -diff --git a/java/java.hints/test/unit/data/goldenfiles/org/netbeans/modules/java/hints/infrastructure/ErrorHintsProviderTest/testShortErrors5-nonmac.pass b/java/java.hints/test/unit/data/goldenfiles/org/netbeans/modules/java/hints/infrastructure/ErrorHintsProviderTest/testShortErrors5-nonmac.pass -deleted file mode 100644 -index 6564d61694f4..000000000000 ---- a/java/java.hints/test/unit/data/goldenfiles/org/netbeans/modules/java/hints/infrastructure/ErrorHintsProviderTest/testShortErrors5-nonmac.pass -+++ /dev/null -@@ -1,3 +0,0 @@ --5:8-5:14:error:cannot find symbol -- symbol:method create() -- location:class TestShortErrors5 -diff --git a/java/java.hints/test/unit/data/goldenfiles/org/netbeans/modules/java/hints/infrastructure/ErrorHintsProviderTest/testShortErrors5.pass b/java/java.hints/test/unit/data/goldenfiles/org/netbeans/modules/java/hints/infrastructure/ErrorHintsProviderTest/testShortErrors5.pass -index a040a93300df..6564d61694f4 100644 ---- a/java/java.hints/test/unit/data/goldenfiles/org/netbeans/modules/java/hints/infrastructure/ErrorHintsProviderTest/testShortErrors5.pass -+++ b/java/java.hints/test/unit/data/goldenfiles/org/netbeans/modules/java/hints/infrastructure/ErrorHintsProviderTest/testShortErrors5.pass -@@ -1 +1,3 @@ --5:8-5:14:error:compiler message file broken:key=compiler.err.cant.resolve.location.args arguments=method, create, , , class, javahints.TestShortErrors5, {6}, {7} -+5:8-5:14:error:cannot find symbol -+ symbol:method create() -+ location:class TestShortErrors5 -diff --git a/java/java.hints/test/unit/data/goldenfiles/org/netbeans/modules/java/hints/infrastructure/ErrorHintsProviderTest/testTestShortErrorsSVUIDWarning-nonmac.pass b/java/java.hints/test/unit/data/goldenfiles/org/netbeans/modules/java/hints/infrastructure/ErrorHintsProviderTest/testTestShortErrorsSVUIDWarning-nonmac.pass -deleted file mode 100644 -index a04bd4bb6bc3..000000000000 ---- a/java/java.hints/test/unit/data/goldenfiles/org/netbeans/modules/java/hints/infrastructure/ErrorHintsProviderTest/testTestShortErrorsSVUIDWarning-nonmac.pass -+++ /dev/null -@@ -1 +0,0 @@ --2:13-2:40:warning:[serial] serializable class TestShortErrorsSVUIDWarning has no definition of serialVersionUID -diff --git a/java/java.hints/test/unit/data/goldenfiles/org/netbeans/modules/java/hints/infrastructure/ErrorHintsProviderTest/testTestShortErrorsSVUIDWarning.pass b/java/java.hints/test/unit/data/goldenfiles/org/netbeans/modules/java/hints/infrastructure/ErrorHintsProviderTest/testTestShortErrorsSVUIDWarning.pass -index 24348a1a6a23..a04bd4bb6bc3 100644 ---- a/java/java.hints/test/unit/data/goldenfiles/org/netbeans/modules/java/hints/infrastructure/ErrorHintsProviderTest/testTestShortErrorsSVUIDWarning.pass -+++ b/java/java.hints/test/unit/data/goldenfiles/org/netbeans/modules/java/hints/infrastructure/ErrorHintsProviderTest/testTestShortErrorsSVUIDWarning.pass -@@ -1 +1 @@ --2:13-2:40:warning:serializable class javahints.TestShortErrorsSVUIDWarning has no definition of serialVersionUID -+2:13-2:40:warning:[serial] serializable class TestShortErrorsSVUIDWarning has no definition of serialVersionUID -diff --git a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/infrastructure/ErrorHintsProviderTest.java b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/infrastructure/ErrorHintsProviderTest.java -index f173387f11f7..de225bbab0d4 100644 ---- a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/infrastructure/ErrorHintsProviderTest.java -+++ b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/infrastructure/ErrorHintsProviderTest.java -@@ -180,7 +180,7 @@ public void testShortErrors4() throws Exception { - } - - public void testShortErrors5() throws Exception { -- performTest("TestShortErrors5", true); -+ performTest("TestShortErrors5", false); - } - - public void testShortErrors6() throws Exception { -@@ -228,7 +228,7 @@ public void testTestShortErrorsSVUIDWarning() throws Exception { - TestCompilerSettings.commandLine = "-Xlint:serial"; - - try { -- performTest("TestShortErrorsSVUIDWarning", true); -+ performTest("TestShortErrorsSVUIDWarning", false); - } finally { - TestCompilerSettings.commandLine = null; - } diff --git a/patches/no-security-manager-allow.diff b/patches/no-security-manager-allow.diff deleted file mode 100644 index 561a0f4a71b5..000000000000 --- a/patches/no-security-manager-allow.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/platform/o.n.bootstrap/launcher/unix/nbexec b/platform/o.n.bootstrap/launcher/unix/nbexec -index 1d6ad6e530..228b37cbe2 100755 ---- a/platform/o.n.bootstrap/launcher/unix/nbexec -+++ b/platform/o.n.bootstrap/launcher/unix/nbexec -@@ -192,7 +192,7 @@ fi - # rename old heap dump to .old - mv "${userdir}/var/log/heapdump.hprof" "${userdir}/var/log/heapdump.hprof.old" > /dev/null 2>&1 - --jargs_without_clusters="$jargs -Djava.security.manager=allow" -+jargs_without_clusters="$jargs" - jargs="-Dnetbeans.dirs=\"${clusters}\" $jargs_without_clusters" - - if [ -z "$cachedirspecified" ]; then diff --git a/patches/rename-debugger.diff b/patches/rename-debugger.diff index 9c2dd7435bd8..4ab83e198e46 100644 --- a/patches/rename-debugger.diff +++ b/patches/rename-debugger.diff @@ -27,18 +27,18 @@ index 91bf8e30db..6d9a77e63a 100644 @Override public CompletableFuture> configurations(Supplier> projectSupplier) { diff --git a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java -index 4fce71a094..2b6362ce37 100644 +index 4c504f9f25..560aaee021 100644 --- a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java +++ b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java -@@ -177,6 +177,7 @@ import org.netbeans.api.sendopts.CommandLine; - import org.netbeans.junit.NbTestCase; +@@ -178,6 +178,7 @@ import org.netbeans.junit.NbTestCase; import org.netbeans.modules.java.hints.infrastructure.JavaErrorProvider; + import static org.netbeans.modules.java.lsp.server.LspTestUtils.tripleSlashUri; import org.netbeans.modules.java.lsp.server.TestCodeLanguageClient; +import org.netbeans.modules.java.lsp.server.debugging.attach.AttachConfigurations; import org.netbeans.modules.java.lsp.server.input.QuickPickItem; import org.netbeans.modules.java.lsp.server.input.ShowQuickPickParams; import org.netbeans.modules.java.lsp.server.input.ShowInputBoxParams; -@@ -1223,7 +1224,7 @@ public class ServerTest extends NbTestCase { +@@ -1224,7 +1225,7 @@ public class ServerTest extends NbTestCase { } private void checkAttachToPort(DebugConnector c) { @@ -47,7 +47,7 @@ index 4fce71a094..2b6362ce37 100644 List arguments = c.getArguments(); assertEquals(2, arguments.size()); assertEquals("hostName", arguments.get(0)); -@@ -1232,7 +1233,7 @@ public class ServerTest extends NbTestCase { +@@ -1233,7 +1234,7 @@ public class ServerTest extends NbTestCase { } private void checkAttachToProcess(DebugConnector c) {