From 6310a3b08f132ee8554ad76427422c103ffeb4cc Mon Sep 17 00:00:00 2001 From: Achal Talati Date: Mon, 26 Aug 2024 11:15:28 +0530 Subject: [PATCH 01/18] Support to set Project level JDK (#244) * backporting 7497 Netbeans PR * Additional registration needed to provider Java default platform override. * updated configuration * README updated * update README --------- Co-authored-by: Jan Lahoda --- README.md | 33 +- build.xml | 1 + nbcode/integration/nbproject/project.xml | 9 + .../LspJavaPlatformProviderOverride.java | 31 ++ patches/7497.diff | 305 ++++++++++++++++++ vscode/README.md | 32 +- vscode/package.json | 11 +- vscode/src/extension.ts | 10 +- 8 files changed, 397 insertions(+), 35 deletions(-) create mode 100644 nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspJavaPlatformProviderOverride.java create mode 100644 patches/7497.diff diff --git a/README.md b/README.md index 192a8a84..45cf65e1 100644 --- a/README.md +++ b/README.md @@ -28,14 +28,21 @@ [![Build Status](https://img.shields.io/github/actions/workflow/status/oracle/javavscode/main.yml?branch=main&style=for-the-badge&logo=github)](https://github.com/oracle/javavscode/actions?query=workflow:Java%20Platform%20Support%20for%20Visual%20Studio%20Code) [![License](https://img.shields.io/github/license/oracle/javavscode?style=for-the-badge&logo=apache)](https://github.com/oracle/javavscode/blob/main/LICENSE.txt) -Java Platform extension from Oracle brings full featured development support (edit-compile-debug & test cycle) to VS Code. It offers support for Maven and Gradle projects. - +Java Platform extension from Oracle brings full featured development support (edit-compile-debug & test cycle) to VS Code. It also offers support for Maven and Gradle projects. Applications using JDK 8 and above are supported. ## Getting Started -1. Set JDK in `View | Command Palette | Preferences:Open User Settings (JSON) ...` __jdk: Jdkhome__ setting to point to JDK which Language Server will run on and projects will be compiled with. More below in section [Selecting the JDK](#selecting-the-jdk) -2. If no JDK is present in your system then extension can setup things for you. More below in section [JDK Downloader](#jdk-downloader) -3. Use __Java: New Project...__ " command to start creating new project, or -4. Open the folder with existing __pom.xml__ for Maven or ___Gradle___ project files (_build.gradle, gradle.properties_). Language Server opens the project, resolves dependencies if any and performs priming build, or -5. Simply create a new Java class file with `public static void main(String[] args)` method in opened folder and start coding, compiling, debugging. Works on JDK 11 and newer. +1. Setting up the JDK + - If no JDK is present in your system then the extension can set things up for you. For more details refer to [JDK Downloader](#jdk-downloader) section. + - Set the JDK in the `View | Command Palette | Preferences: Open User Settings | Jdk: Jdkhome` setting to point to the JDK that the Language Server will run on and also by default use for running and compiling projects. + - The extension requires JDK 17 or newer to run. + - Optionally, set a different JDK to compile and run projects in the `View | Command Palette | Preferences: Open User Settings | Jdk › Project: Jdkhome` setting. + - By default, the __jdk.jdkhome__ setting is used. + - Projects can run on JDK 8 and above. + - For more information, see the section [Selecting the JDK](#selecting-the-jdk). +4. Use any one of the following ways to start coding, compiling and debugging in Java. + - Simply create a new Java class with `public static void main(String[] args)` method. + - Use the __Java: New From Template...__ command to create a new Java file. + - Use the __Java: New Project...__ command to create a new project. + - Open the folder with existing __Maven__ or __Gradle__ project files (_pom.xml_ or _build.gradle, gradle.properties_). ## Supported Actions In the VS Code command palette : @@ -159,19 +166,11 @@ When adding JavaDoc to code Oracle Java Platform extension assists by suggesting Oracle Java Platform extension provides Test Explorer view which allows to run all tests in a project, examine the results, go to source code and run particular test. ![Test Explorer](vscode/images/Test_explorer.png) -## Supported Options - -* __jdk.jdkhome__ - path to the JDK, see dedicated section below -* __jdk.verbose__ - enables verbose extension logging - ## Selecting the JDK +The JDK to build, run and debug projects is being searched in the following locations: -The user projects are built, run and debugged using the same JDK which runs the -Oracle Java Platform extension. The JDK is being searched in -following locations: - +- `jdk.project.jdkhome` setting (workspace then user settings) - `jdk.jdkhome` setting (workspace then user settings) -- `java.home` setting (workspace then user settings) - `JDK_HOME` environment variable - `JAVA_HOME` environment variable - current system path diff --git a/build.xml b/build.xml index e9d1e404..5a05317b 100644 --- a/build.xml +++ b/build.xml @@ -41,6 +41,7 @@ patches/7370.diff patches/7382.diff patches/7491-preliminary.diff + patches/7497.diff patches/7548_source-1.8.diff patches/7583_source-1.8.diff patches/7610.diff diff --git a/nbcode/integration/nbproject/project.xml b/nbcode/integration/nbproject/project.xml index 7725ea9a..4ff1a9d3 100644 --- a/nbcode/integration/nbproject/project.xml +++ b/nbcode/integration/nbproject/project.xml @@ -112,6 +112,15 @@ + org.netbeans.modules.java.platform + + + + 1 + 1.66 + + + org.netbeans.modules.parsing.api diff --git a/nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspJavaPlatformProviderOverride.java b/nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspJavaPlatformProviderOverride.java new file mode 100644 index 00000000..31066657 --- /dev/null +++ b/nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspJavaPlatformProviderOverride.java @@ -0,0 +1,31 @@ +/* + * 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.nbcode.integration; + +import org.netbeans.modules.java.lsp.server.ui.AbstractJavaPlatformProviderOverride; +import org.netbeans.modules.java.platform.implspi.JavaPlatformProvider; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author sdedic + */ +@ServiceProvider(service = JavaPlatformProvider.class, position = 10_000) +public class LspJavaPlatformProviderOverride extends AbstractJavaPlatformProviderOverride { +} diff --git a/patches/7497.diff b/patches/7497.diff new file mode 100644 index 00000000..87962363 --- /dev/null +++ b/patches/7497.diff @@ -0,0 +1,305 @@ +diff --git a/java/java.lsp.server/nbcode/integration/nbproject/project.xml b/java/java.lsp.server/nbcode/integration/nbproject/project.xml +index 3cee4feb1698..70102d62f3a3 100644 +--- a/java/java.lsp.server/nbcode/integration/nbproject/project.xml ++++ b/java/java.lsp.server/nbcode/integration/nbproject/project.xml +@@ -118,6 +118,15 @@ + + + ++ ++ org.netbeans.modules.java.platform ++ ++ ++ ++ 1 ++ 1.66 ++ ++ + + org.netbeans.modules.parsing.api + +diff --git a/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspJavaPlatformProviderOverride.java b/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspJavaPlatformProviderOverride.java +new file mode 100644 +index 000000000000..31066657e35d +--- /dev/null ++++ b/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspJavaPlatformProviderOverride.java +@@ -0,0 +1,31 @@ ++/* ++ * 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.nbcode.integration; ++ ++import org.netbeans.modules.java.lsp.server.ui.AbstractJavaPlatformProviderOverride; ++import org.netbeans.modules.java.platform.implspi.JavaPlatformProvider; ++import org.openide.util.lookup.ServiceProvider; ++ ++/** ++ * ++ * @author sdedic ++ */ ++@ServiceProvider(service = JavaPlatformProvider.class, position = 10_000) ++public class LspJavaPlatformProviderOverride extends AbstractJavaPlatformProviderOverride { ++} +diff --git a/java/java.lsp.server/nbproject/project.xml b/java/java.lsp.server/nbproject/project.xml +index 2b5122a231bb..cea7ea966aae 100644 +--- a/java/java.lsp.server/nbproject/project.xml ++++ b/java/java.lsp.server/nbproject/project.xml +@@ -400,6 +400,15 @@ + 0.3 + + ++ ++ org.netbeans.modules.java.platform ++ ++ ++ ++ 1 ++ 1.66 ++ ++ + + org.netbeans.modules.java.project + +@@ -697,12 +706,6 @@ + 9.24 + + +- +- com.google.guava +- +- 27.16 +- +- + + + +diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java +index f42d7ee24809..2b4565d80dd5 100644 +--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java ++++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java +@@ -45,6 +45,7 @@ + import java.util.logging.Logger; + import com.google.gson.InstanceCreator; + import com.google.gson.JsonObject; ++import com.google.gson.JsonPrimitive; + import java.util.prefs.Preferences; + import java.util.LinkedHashSet; + import java.util.Objects; +@@ -382,6 +383,7 @@ public static class LanguageServerImpl implements LanguageServer, LanguageClient + + private static final String NETBEANS_FORMAT = "format"; + private static final String NETBEANS_JAVA_IMPORTS = "java.imports"; ++ private static final String NETBEANS_PROJECT_JDKHOME = "project.jdkhome"; + private static final String NETBEANS_JAVA_HINTS = "hints"; + + // change to a greater throughput if the initialization waits on more processes than just (serialized) project open. +@@ -1020,6 +1022,7 @@ private void collectProjectCandidates(FileObject fo, List candidates + private void initializeOptions() { + getWorkspaceProjects().thenAccept(projects -> { + ConfigurationItem item = new ConfigurationItem(); ++ // PENDING: what about doing just one roundtrip to the client- we may request multiple ConfiguratonItems in one message ? + item.setSection(client.getNbCodeCapabilities().getConfigurationPrefix() + NETBEANS_JAVA_HINTS); + client.configuration(new ConfigurationParams(Collections.singletonList(item))).thenAccept(c -> { + if (c != null && !c.isEmpty() && c.get(0) instanceof JsonObject) { +@@ -1030,6 +1033,16 @@ private void initializeOptions() { + textDocumentService.reRunDiagnostics(); + } + }); ++ item.setSection(client.getNbCodeCapabilities().getConfigurationPrefix() + NETBEANS_PROJECT_JDKHOME); ++ client.configuration(new ConfigurationParams(Collections.singletonList(item))).thenAccept(c -> { ++ JsonPrimitive newProjectJDKHomePath = null; ++ ++ if (c != null && !c.isEmpty() && c.get(0) instanceof JsonPrimitive) { ++ newProjectJDKHomePath = (JsonPrimitive) c.get(0); ++ } else { ++ } ++ textDocumentService.updateProjectJDKHome(newProjectJDKHomePath); ++ }); + if (projects != null && projects.length > 0) { + FileObject fo = projects[0].getProjectDirectory(); + item.setScopeUri(Utils.toUri(fo)); +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 ad0d82fa448e..c112b4eb73d7 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 +@@ -240,6 +240,7 @@ + import org.netbeans.api.lsp.StructureElement; + import org.netbeans.modules.editor.indent.api.Reformat; + import org.netbeans.modules.java.lsp.server.URITranslator; ++import org.netbeans.modules.java.lsp.server.ui.AbstractJavaPlatformProviderOverride; + import org.netbeans.modules.parsing.impl.SourceAccessor; + import org.netbeans.spi.editor.hints.ErrorDescription; + import org.netbeans.spi.editor.hints.Fix; +@@ -2086,6 +2087,15 @@ void updateJavaHintPreferences(JsonObject configuration) { + reRunDiagnostics(); + } + ++ void updateProjectJDKHome(JsonPrimitive configuration) { ++ if (configuration == null) { ++ client.logMessage(new MessageParams(MessageType.Log,"Project runtime JDK unset, defaults to NBLS JDK")); ++ } else { ++ client.logMessage(new MessageParams(MessageType.Log, "Project runtime JDK set to " + configuration.getAsString())); ++ } ++ AbstractJavaPlatformProviderOverride.setDefaultPlatformOverride(configuration != null ? configuration.getAsString() : null); ++ } ++ + private String key(ErrorProvider.Kind errorKind) { + return errorKind.name().toLowerCase(Locale.ROOT); + } +diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java +index d3027a2eb620..6e0755bfb6e6 100644 +--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java ++++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java +@@ -1347,7 +1347,9 @@ public void didChangeConfiguration(DidChangeConfigurationParams params) { + String fullConfigPrefix = client.getNbCodeCapabilities().getConfigurationPrefix(); + String configPrefix = fullConfigPrefix.substring(0, fullConfigPrefix.length() - 1); + server.openedProjects().thenAccept(projects -> { ++ // PENDING: invent a pluggable mechanism for this, this does not scale and the typecast to serviceImpl is ugly + ((TextDocumentServiceImpl)server.getTextDocumentService()).updateJavaHintPreferences(((JsonObject) params.getSettings()).getAsJsonObject(configPrefix).getAsJsonObject(NETBEANS_JAVA_HINTS)); ++ ((TextDocumentServiceImpl)server.getTextDocumentService()).updateProjectJDKHome(((JsonObject) params.getSettings()).getAsJsonObject(configPrefix).getAsJsonObject("project").getAsJsonPrimitive("jdkhome")); + if (projects != null && projects.length > 0) { + updateJavaFormatPreferences(projects[0].getProjectDirectory(), ((JsonObject) params.getSettings()).getAsJsonObject(configPrefix).getAsJsonObject("format")); + updateJavaImportPreferences(projects[0].getProjectDirectory(), ((JsonObject) params.getSettings()).getAsJsonObject(configPrefix).getAsJsonObject("java").getAsJsonObject("imports")); +diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/AbstractJavaPlatformProviderOverride.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/AbstractJavaPlatformProviderOverride.java +new file mode 100644 +index 000000000000..df668954dd94 +--- /dev/null ++++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/AbstractJavaPlatformProviderOverride.java +@@ -0,0 +1,122 @@ ++/* ++ * 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.lsp.server.ui; ++ ++import java.beans.PropertyChangeListener; ++import java.beans.PropertyChangeSupport; ++import java.io.File; ++import java.io.IOException; ++import java.util.HashSet; ++import java.util.Set; ++import org.netbeans.api.java.platform.JavaPlatform; ++import org.netbeans.api.java.platform.JavaPlatformManager; ++import org.netbeans.modules.java.platform.implspi.JavaPlatformProvider; ++import org.netbeans.spi.java.platform.JavaPlatformFactory; ++import org.openide.filesystems.FileObject; ++import org.openide.filesystems.FileUtil; ++import org.openide.util.Exceptions; ++import org.openide.util.Lookup; ++ ++public abstract class AbstractJavaPlatformProviderOverride implements JavaPlatformProvider { ++ ++ private static final JavaPlatform[] NO_PLATFORMS = new JavaPlatform[0]; ++ private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); ++ private FileObject defaultPlatformOverride; ++ ++ @Override ++ @SuppressWarnings("ReturnOfCollectionOrArrayField") ++ public JavaPlatform[] getInstalledPlatforms() { ++ return NO_PLATFORMS; ++ } ++ ++ @Override ++ public JavaPlatform getDefaultPlatform() { ++ FileObject override; ++ ++ synchronized (this) { ++ override = defaultPlatformOverride; ++ } ++ ++ if (override == null) { ++ return null; ++ } ++ ++ Set existingNames = new HashSet<>(); ++ JavaPlatform found = null; ++ ++ for (JavaPlatform platform : JavaPlatformManager.getDefault().getInstalledPlatforms()) { ++ if (platform.getInstallFolders().stream().anyMatch(folder -> folder.equals(override))) { ++ found = platform; ++ break; ++ } ++ existingNames.add(platform.getDisplayName()); ++ } ++ ++ if (found == null ){ ++ String newName = defaultPlatformOverride.getPath(); ++ ++ while (existingNames.contains(newName)) { ++ newName += "1"; ++ } ++ ++ for (JavaPlatformFactory.Provider provider : Lookup.getDefault().lookupAll(JavaPlatformFactory.Provider.class)) { ++ JavaPlatformFactory factory = provider.forType("j2se"); ++ if (factory != null) { ++ try { ++ found = factory.create(override, newName, true); ++ } catch (IOException ex) { ++ Exceptions.printStackTrace(ex); ++ } ++ } ++ } ++ } ++ ++ return found; ++ } ++ ++ @Override ++ public void addPropertyChangeListener(PropertyChangeListener listener) { ++ pcs.addPropertyChangeListener(listener); ++ } ++ ++ @Override ++ public void removePropertyChangeListener(PropertyChangeListener listener) { ++ pcs.removePropertyChangeListener(listener); ++ } ++ ++ private void dosetDefaultPlatformOverride(String defaultPlatformOverride) { ++ FileObject override = defaultPlatformOverride != null ? FileUtil.toFileObject(new File(defaultPlatformOverride)) ++ : null; ++ ++ synchronized (this) { ++ this.defaultPlatformOverride = override; ++ } ++ ++ pcs.firePropertyChange(null, null, null); ++ } ++ ++ public static void setDefaultPlatformOverride(String defaultPlatformOverride) { ++ for (JavaPlatformProvider p : Lookup.getDefault().lookupAll(JavaPlatformProvider.class)) { ++ if (p instanceof AbstractJavaPlatformProviderOverride) { ++ ((AbstractJavaPlatformProviderOverride) p).dosetDefaultPlatformOverride(defaultPlatformOverride); ++ } ++ } ++ } ++ ++} diff --git a/vscode/README.md b/vscode/README.md index 104e5121..068a40e6 100644 --- a/vscode/README.md +++ b/vscode/README.md @@ -24,13 +24,21 @@ # Java Platform Extension for Visual Studio Code -Java Platform extension from Oracle brings full featured development support (edit-compile-debug & test cycle) to VS Code. It offers support for Maven and Gradle projects. +Java Platform extension from Oracle brings full featured development support (edit-compile-debug & test cycle) to VS Code. It also offers support for Maven and Gradle projects. Applications using JDK 8 and above are supported. ## Getting Started -1. Set JDK in `View | Command Palette | Preferences:Open User Settings (JSON) ...` __jdk: Jdkhome__ setting to point to JDK which Language Server will run on and projects will be compiled with. More below in section [Selecting the JDK](#selecting-the-jdk) -2. If no JDK is present in your system then extension can setup things for you. More below in section [JDK Downloader](#jdk-downloader) -3. Use __Java: New Project...__ " command to start creating new project, or -4. Open the folder with existing __pom.xml__ for Maven or ___Gradle___ project files (_build.gradle, gradle.properties_). Language Server opens the project, resolves dependencies if any and performs priming build, or -5. Simply create a new Java class file with `public static void main(String[] args)` method in opened folder and start coding, compiling, debugging. Works on JDK 11 and newer. +1. Setting up the JDK + - If no JDK is present in your system then the extension can set things up for you. For more details refer to [JDK Downloader](#jdk-downloader) section. + - Set the JDK in the `View | Command Palette | Preferences: Open User Settings | Jdk: Jdkhome` setting to point to the JDK that the Language Server will run on and also by default use for running and compiling projects. + - The extension requires JDK 17 or newer to run. + - Optionally, set a different JDK to compile and run projects in the `View | Command Palette | Preferences: Open User Settings | Jdk › Project: Jdkhome` setting. + - By default, the __jdk.jdkhome__ setting is used. + - Projects can run on JDK 8 and above. + - For more information, see the section [Selecting the JDK](#selecting-the-jdk). +4. Use any one of the following ways to start coding, compiling and debugging in Java. + - Simply create a new Java class with `public static void main(String[] args)` method. + - Use the __Java: New From Template...__ command to create a new Java file. + - Use the __Java: New Project...__ command to create a new project. + - Open the folder with existing __Maven__ or __Gradle__ project files (_pom.xml_ or _build.gradle, gradle.properties_). ## Supported Actions In the VS Code command palette : @@ -153,19 +161,11 @@ When adding JavaDoc to code Oracle Java Platform extension assists by suggesting Oracle Java Platform extension provides Test Explorer view which allows to run all tests in a project, examine the results, go to source code and run particular test. ![Test Explorer](images/Test_explorer.png) -## Supported Options - -* __jdk.jdkhome__ - path to the JDK, see dedicated section below -* __jdk.verbose__ - enables verbose extension logging - ## Selecting the JDK +The JDK to build, run and debug projects is being searched in the following locations: -The user projects are built, run and debugged using the same JDK which runs the -Oracle Java Platform extension. The JDK is being searched in -following locations: - +- `jdk.project.jdkhome` setting (workspace then user settings) - `jdk.jdkhome` setting (workspace then user settings) -- `java.home` setting (workspace then user settings) - `JDK_HOME` environment variable - `JAVA_HOME` environment variable - current system path diff --git a/vscode/package.json b/vscode/package.json index 23122d41..fbe9e64d 100644 --- a/vscode/package.json +++ b/vscode/package.json @@ -116,7 +116,16 @@ "null" ], "default": null, - "description": "Specifies JDK for the Oracle Visual Studio Code Extension", + "description": "Specifies the JDK on which the Oracle Visual Studio Code Extension is run", + "scope": "machine-overridable" + }, + "jdk.project.jdkhome": { + "type": [ + "string", + "null" + ], + "default": null, + "description": "Specifies the JDK on which user's project will be run. Defaults to the value of jdk.jdkhome", "scope": "machine-overridable" }, "jdk.verbose": { diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index 92b042b7..2ea7042b 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -190,6 +190,7 @@ function findJDK(onChange: (path : string | null) => void): void { } let currentJdk = find(); + let projectJdk : string | undefined = getProjectJDKHome(); let timeout: NodeJS.Timeout | undefined = undefined; workspace.onDidChangeConfiguration(params => { if (timeout) { @@ -212,10 +213,12 @@ function findJDK(onChange: (path : string | null) => void): void { let newJdk = find(); let newD = isDarkColorTheme(); let newNbJavacDisabled = isNbJavacDisabled(); - if (newJdk !== currentJdk || newD != nowDark || newNbJavacDisabled != nowNbJavacDisabled) { + let newProjectJdk : string | undefined = workspace.getConfiguration('jdk')?.get('project.jdkhome') as string; + if (newJdk !== currentJdk || newD != nowDark || newNbJavacDisabled != nowNbJavacDisabled || newProjectJdk != projectJdk) { nowDark = newD; currentJdk = newJdk; nowNbJavacDisabled = newNbJavacDisabled; + projectJdk = newProjectJdk onChange(currentJdk); } }, 0); @@ -859,6 +862,10 @@ function isNbJavacDisabled() : boolean { return workspace.getConfiguration('jdk')?.get('advanced.disable.nbjavac') as boolean; } +function getProjectJDKHome() : string { + return workspace.getConfiguration('jdk')?.get('project.jdkhome') as string; +} + function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContext, log : vscode.OutputChannel, notifyKill: boolean, setClient : [(c : NbLanguageClient) => void, (err : any) => void] ): void { @@ -1044,6 +1051,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex 'jdk.hints', 'jdk.format', 'jdk.java.imports', + 'jdk.project.jdkhome', 'jdk.runConfig.vmOptions', 'jdk.runConfig.cwd' ], From e4b44271791948a5147f770019a675b3f6670c40 Mon Sep 17 00:00:00 2001 From: Siddharth Srinivasan Date: Tue, 27 Aug 2024 17:36:34 +0530 Subject: [PATCH 02/18] Backport NB23 patch 7390 to fix the Exception template (#247) In NB23, [patch #7390](apache/netbeans/pull/7390) fixed the regression where the newly created exception from template did not extend Exception, thereby failing to compile. This temporarily backports the fix until NB23 is incorporated. Signed-off-by: Siddharth Srinivasan --- build.xml | 1 + patches/7390.diff | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 patches/7390.diff diff --git a/build.xml b/build.xml index 5a05317b..e4ec82d3 100644 --- a/build.xml +++ b/build.xml @@ -40,6 +40,7 @@ patches/7368.diff patches/7370.diff patches/7382.diff + patches/7390.diff patches/7491-preliminary.diff patches/7497.diff patches/7548_source-1.8.diff diff --git a/patches/7390.diff b/patches/7390.diff new file mode 100644 index 00000000..c45e6dfe --- /dev/null +++ b/patches/7390.diff @@ -0,0 +1,13 @@ +diff --git a/java/java.project.ui/src/org/netbeans/modules/java/project/ui/resources/Exception.java.template b/java/java.project.ui/src/org/netbeans/modules/java/project/ui/resources/Exception.java.template +index ab05caa21d2e..bdccca88f8e3 100644 +--- a/java/java.project.ui/src/org/netbeans/modules/java/project/ui/resources/Exception.java.template ++++ b/java/java.project.ui/src/org/netbeans/modules/java/project/ui/resources/Exception.java.template +@@ -33,7 +33,7 @@ import ${interface}; + + <#assign implementation = "${implementation}"?remove_ending(", ")> + +-public class ${name}<#if extension?? && extension != ""> extends ${extension}<#if implementation?? && implementation != ""> implements ${implementation} { ++public class ${name}<#if extension?? && extension != ""> extends ${extension}<#else> extends Exception<#if implementation?? && implementation != ""> implements ${implementation} { + + /** + * Creates a new instance of ${name} without detail message. From fe811d454491c33fe5cf7183737b18f738713d25 Mon Sep 17 00:00:00 2001 From: Siddharth Srinivasan Date: Thu, 5 Sep 2024 08:19:13 +0530 Subject: [PATCH 03/18] Backport NB patch for Gradle tooling 8.10 upgrade This version of Gradle brings full JDK23 support. - Manually verified `shasum` of the gradle-tooling-api-8.10.jar recorded in the *extide/libs.gradle/external/binaries-list* file. - Especially important since this PR is still not approved. - BA available. Signed-off-by: Siddharth Srinivasan --- build.xml | 1 + patches/7690.diff | 83 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 patches/7690.diff diff --git a/build.xml b/build.xml index e4ec82d3..03916758 100644 --- a/build.xml +++ b/build.xml @@ -49,6 +49,7 @@ patches/7621.diff patches/7641.diff patches/7654.diff + patches/7690.diff patches/mvn-sh.diff patches/generate-dependencies.diff patches/rename-debugger.diff diff --git a/patches/7690.diff b/patches/7690.diff new file mode 100644 index 00000000..75ffbbfb --- /dev/null +++ b/patches/7690.diff @@ -0,0 +1,83 @@ +diff --git a/extide/gradle/src/org/netbeans/modules/gradle/api/execute/GradleDistributionManager.java b/extide/gradle/src/org/netbeans/modules/gradle/api/execute/GradleDistributionManager.java +index 8b93f19d3208..051d95a2cb8b 100644 +--- a/extide/gradle/src/org/netbeans/modules/gradle/api/execute/GradleDistributionManager.java ++++ b/extide/gradle/src/org/netbeans/modules/gradle/api/execute/GradleDistributionManager.java +@@ -100,9 +100,10 @@ public final class GradleDistributionManager { + GradleVersion.version("8.3"), // JDK-20 + GradleVersion.version("8.5"), // JDK-21 + GradleVersion.version("8.8"), // JDK-22 ++ GradleVersion.version("8.10"),// JDK-23 + }; + +- private static final GradleVersion LAST_KNOWN_GRADLE = GradleVersion.version("8.9"); //NOI18N ++ private static final GradleVersion LAST_KNOWN_GRADLE = GradleVersion.version("8.10"); //NOI18N + + final File gradleUserHome; + +diff --git a/extide/libs.gradle/external/binaries-list b/extide/libs.gradle/external/binaries-list +index dff2c2265b37..72218b9d5a29 100644 +--- a/extide/libs.gradle/external/binaries-list ++++ b/extide/libs.gradle/external/binaries-list +@@ -15,4 +15,4 @@ + # specific language governing permissions and limitations + # under the License. + +-7BCC4423C529A42ECA9D0CE5B5275369EF4DF55A https://repo.gradle.org/artifactory/libs-releases/org/gradle/gradle-tooling-api/8.9/gradle-tooling-api-8.9.jar gradle-tooling-api-8.9.jar ++1FC754376876B11AE26D811F8812AA37773660DD https://repo.gradle.org/artifactory/libs-releases/org/gradle/gradle-tooling-api/8.10/gradle-tooling-api-8.10.jar gradle-tooling-api-8.10.jar +diff --git a/extide/libs.gradle/external/gradle-tooling-api-8.9-license.txt b/extide/libs.gradle/external/gradle-tooling-api-8.10-license.txt +similarity index 99% +rename from extide/libs.gradle/external/gradle-tooling-api-8.9-license.txt +rename to extide/libs.gradle/external/gradle-tooling-api-8.10-license.txt +index 74cb1addb8d6..ab7d5a2a2558 100644 +--- a/extide/libs.gradle/external/gradle-tooling-api-8.9-license.txt ++++ b/extide/libs.gradle/external/gradle-tooling-api-8.10-license.txt +@@ -1,7 +1,7 @@ + Name: Gradle Tooling API + Description: Gradle Tooling API +-Version: 8.9 +-Files: gradle-tooling-api-8.9.jar ++Version: 8.10 ++Files: gradle-tooling-api-8.10.jar + License: Apache-2.0 + Origin: Gradle Inc. + URL: https://gradle.org/ +diff --git a/extide/libs.gradle/external/gradle-tooling-api-8.9-notice.txt b/extide/libs.gradle/external/gradle-tooling-api-8.10-notice.txt +similarity index 100% +rename from extide/libs.gradle/external/gradle-tooling-api-8.9-notice.txt +rename to extide/libs.gradle/external/gradle-tooling-api-8.10-notice.txt +diff --git a/extide/libs.gradle/nbproject/project.properties b/extide/libs.gradle/nbproject/project.properties +index 6e4605fe4922..d20b5a229d4c 100644 +--- a/extide/libs.gradle/nbproject/project.properties ++++ b/extide/libs.gradle/nbproject/project.properties +@@ -22,4 +22,4 @@ javac.compilerargs=-Xlint -Xlint:-serial + # Sigtest fails to read the classes in the gradle-tooling-api + sigtest.skip.gen=true + +-release.external/gradle-tooling-api-8.9.jar=modules/gradle/gradle-tooling-api.jar ++release.external/gradle-tooling-api-8.10.jar=modules/gradle/gradle-tooling-api.jar +diff --git a/extide/libs.gradle/nbproject/project.xml b/extide/libs.gradle/nbproject/project.xml +index d82027b5e615..9b1dfe36f2ad 100644 +--- a/extide/libs.gradle/nbproject/project.xml ++++ b/extide/libs.gradle/nbproject/project.xml +@@ -39,7 +39,7 @@ + + + gradle/gradle-tooling-api.jar +- external/gradle-tooling-api-8.9.jar ++ external/gradle-tooling-api-8.10.jar + + + +diff --git a/java/gradle.java/src/org/netbeans/modules/gradle/java/newproject/Wizards.java b/java/gradle.java/src/org/netbeans/modules/gradle/java/newproject/Wizards.java +index 255def8bce12..031c25128452 100644 +--- a/java/gradle.java/src/org/netbeans/modules/gradle/java/newproject/Wizards.java ++++ b/java/gradle.java/src/org/netbeans/modules/gradle/java/newproject/Wizards.java +@@ -35,7 +35,7 @@ public final class Wizards { + + private Wizards() {}; + +- private static final List JAVA_VERSIONS = List.of(22, 21, 17, 11, 8); ++ private static final List JAVA_VERSIONS = List.of(23, 22, 21, 17, 11, 8); + private static final List JAVA_TEST_FRAMEWORKS = List.of( + JUNIT, + JUNIT_5, From f8979a6e873711afd5cd160206696a03bfafc8ac Mon Sep 17 00:00:00 2001 From: Shivam Madan Date: Thu, 5 Sep 2024 10:45:50 +0530 Subject: [PATCH 04/18] Fix for delete cache error on windows (#245) * Fix for delete cache error on windows * Added error handling * Declared constant for windows os label * Refactored code to use await and try catch pattern * Fixed indent --- vscode/src/constants.ts | 2 ++ vscode/src/extension.ts | 27 +++++++++++++++++---------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/vscode/src/constants.ts b/vscode/src/constants.ts index aa095912..3fa5e83f 100644 --- a/vscode/src/constants.ts +++ b/vscode/src/constants.ts @@ -24,3 +24,5 @@ export const OPEN_JDK_VERSION_DOWNLOAD_LINKS: { [key: string]: string } = { "22": "https://download.java.net/java/GA/jdk22.0.1/c7ec1332f7bb44aeba2eb341ae18aca4/8/GPL/openjdk-22.0.1", "21": "https://download.java.net/java/GA/jdk21.0.2/f2283984656d49d69e91c558476027ac/13/GPL/openjdk-21.0.2" }; + +export const NODE_WINDOWS_LABEL = "Windows_NT"; diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index 2ea7042b..7cbd0611 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -67,7 +67,7 @@ import { initializeRunConfiguration, runConfigurationProvider, runConfigurationN import { InputStep, MultiStepInput } from './utils'; import { PropertiesView } from './propertiesView/propertiesView'; import { openJDKSelectionView } from './jdkDownloader'; - +import { NODE_WINDOWS_LABEL } from './constants'; const API_VERSION : string = "1.0"; const SERVER_NAME : string = "Oracle Java SE Language Server"; export const COMMAND_PREFIX : string = "jdk"; @@ -78,7 +78,7 @@ let nbProcess : ChildProcess | null = null; let debugPort: number = -1; let debugHash: string | undefined; let consoleLog: boolean = !!process.env['ENABLE_CONSOLE_LOG']; - +let deactivated:boolean = true; export class NbLanguageClient extends LanguageClient { private _treeViewService: TreeViewService; @@ -330,8 +330,8 @@ class InitialPromise extends Promise { } export function activate(context: ExtensionContext): VSNetBeansAPI { + deactivated=false; let log = vscode.window.createOutputChannel(SERVER_NAME); - var clientResolve : (x : NbLanguageClient) => void; var clientReject : (err : any) => void; @@ -513,19 +513,26 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { context.subscriptions.push(vscode.commands.registerCommand(COMMAND_PREFIX + ".delete.cache", async () => { const storagePath = context.storageUri?.fsPath; - if(!storagePath){ + if (!storagePath) { vscode.window.showErrorMessage('Cannot find workspace path'); return; } + const userDir = path.join(storagePath, "userdir"); if (userDir && fs.existsSync(userDir)) { - const confirmation = await vscode.window.showInformationMessage('Are you sure you want to delete cache for this workspace?', + const confirmation = await vscode.window.showInformationMessage('Are you sure you want to delete cache for this workspace and reload the window ?', 'Yes', 'Cancel'); if (confirmation === 'Yes') { - await fs.promises.rmdir(userDir, {recursive : true}); - const res = await vscode.window.showInformationMessage('Cache cleared successfully for this workspace', 'Reload window'); - if (res === 'Reload window') { - await vscode.commands.executeCommand('workbench.action.reloadWindow'); + try { + await stopClient(client); + deactivated = true; + await killNbProcess(false, log); + await fs.promises.rmdir(userDir, { recursive: true }); + await vscode.window.showInformationMessage("Cache deleted successfully", 'Reload window'); + } catch (err) { + await vscode.window.showErrorMessage('Error deleting the cache', 'Reload window'); + } finally { + vscode.commands.executeCommand("workbench.action.reloadWindow"); } } } else { @@ -960,7 +967,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex if (p == nbProcess && code != 0 && code) { vscode.window.showWarningMessage(`${SERVER_NAME} exited with ` + code); } - if (stdErr?.match(/Cannot find java/) || os.type() === "Windows_NT" ) { + if (stdErr?.match(/Cannot find java/) || (os.type() === NODE_WINDOWS_LABEL && !deactivated) ) { vscode.window.showInformationMessage( "No JDK found!", "Download JDK and setup automatically" From 7c8df21fa1b9d03363bb73c51a4d198403cde701 Mon Sep 17 00:00:00 2001 From: Achal Talati Date: Tue, 3 Sep 2024 19:50:28 +0530 Subject: [PATCH 05/18] Initial sketch of NB-23 upgrade --- .github/workflows/main.yml | 2 +- BUILD.md | 2 +- build.xml | 15 +- .../modules/java/testrunner/Bundle.properties | 21 + .../modules/nbcode/integration/layer.xml | 55 +- .../integration/maven-actions-override.xml | 130 ++ patches/7001.diff | 264 --- patches/7271.diff | 31 - patches/7353.diff | 16 - patches/7368.diff | 507 ---- patches/7370.diff | 135 -- patches/7382.diff | 1611 ------------- patches/7390.diff | 13 - patches/{7491-preliminary.diff => 7491.diff} | 758 +++--- patches/7497.diff | 305 --- patches/7548_source-1.8.diff | 73 - patches/7583_source-1.8.diff | 80 - patches/7621.diff | 125 - patches/7670.diff | 2037 +++++++++++++++++ patches/7733.diff | 116 + patches/remove-db.diff | 216 -- vscode/package-lock.json | 4 +- vscode/package.json | 4 +- vscode/src/extension.ts | 15 +- 24 files changed, 2821 insertions(+), 3714 deletions(-) create mode 100644 nbcode/branding/modules/org-netbeans-modules-java-testrunner.jar/org/netbeans/modules/java/testrunner/Bundle.properties create mode 100644 nbcode/integration/src/org/netbeans/modules/nbcode/integration/maven-actions-override.xml delete mode 100644 patches/7001.diff delete mode 100644 patches/7271.diff delete mode 100644 patches/7353.diff delete mode 100644 patches/7368.diff delete mode 100644 patches/7370.diff delete mode 100644 patches/7382.diff delete mode 100644 patches/7390.diff rename patches/{7491-preliminary.diff => 7491.diff} (76%) delete mode 100644 patches/7497.diff delete mode 100644 patches/7548_source-1.8.diff delete mode 100644 patches/7583_source-1.8.diff delete mode 100644 patches/7621.diff create mode 100644 patches/7670.diff create mode 100644 patches/7733.diff diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5d8185b7..016a2e81 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -62,7 +62,7 @@ jobs: with: repository: apache/netbeans path: netbeans - ref: 22 + ref: 23-rc2 - name: Apply NetBeans patches run: ant apply-patches diff --git a/BUILD.md b/BUILD.md index 579ab882..6d5166b9 100644 --- a/BUILD.md +++ b/BUILD.md @@ -39,7 +39,7 @@ $ git clone https://github.com/oracle/javavscode.git $ cd javavscode/ $ git clone https://github.com/apache/netbeans.git $ cd netbeans/ -$ git checkout 22 +$ git checkout 23-rc2 $ cd .. # the following target requires git executable to be on PATH: $ ant apply-patches diff --git a/build.xml b/build.xml index 03916758..a99ed953 100644 --- a/build.xml +++ b/build.xml @@ -34,22 +34,13 @@ patches/6330.diff - patches/7001.diff - patches/7271.diff - patches/7353.diff - patches/7368.diff - patches/7370.diff - patches/7382.diff - patches/7390.diff - patches/7491-preliminary.diff - patches/7497.diff - patches/7548_source-1.8.diff - patches/7583_source-1.8.diff + patches/7491.diff patches/7610.diff - patches/7621.diff patches/7641.diff patches/7654.diff + patches/7670.diff patches/7690.diff + patches/7733.diff patches/mvn-sh.diff patches/generate-dependencies.diff patches/rename-debugger.diff diff --git a/nbcode/branding/modules/org-netbeans-modules-java-testrunner.jar/org/netbeans/modules/java/testrunner/Bundle.properties b/nbcode/branding/modules/org-netbeans-modules-java-testrunner.jar/org/netbeans/modules/java/testrunner/Bundle.properties new file mode 100644 index 00000000..f630844e --- /dev/null +++ b/nbcode/branding/modules/org-netbeans-modules-java-testrunner.jar/org/netbeans/modules/java/testrunner/Bundle.properties @@ -0,0 +1,21 @@ +# 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. + +PROP_generate_setUp_default=false +PROP_generate_tearDown_default=false +PROP_generate_class_setUp_default=false +PROP_generate_class_tearDown_default=false diff --git a/nbcode/integration/src/org/netbeans/modules/nbcode/integration/layer.xml b/nbcode/integration/src/org/netbeans/modules/nbcode/integration/layer.xml index 11e1bf64..28db59fd 100644 --- a/nbcode/integration/src/org/netbeans/modules/nbcode/integration/layer.xml +++ b/nbcode/integration/src/org/netbeans/modules/nbcode/integration/layer.xml @@ -1,24 +1,3 @@ - - @@ -52,12 +31,16 @@ + + + + @@ -75,6 +58,9 @@ + + + @@ -123,4 +109,29 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/nbcode/integration/src/org/netbeans/modules/nbcode/integration/maven-actions-override.xml b/nbcode/integration/src/org/netbeans/modules/nbcode/integration/maven-actions-override.xml new file mode 100644 index 00000000..14e3f7db --- /dev/null +++ b/nbcode/integration/src/org/netbeans/modules/nbcode/integration/maven-actions-override.xml @@ -0,0 +1,130 @@ + + + + + + build + + * + + + install + + also-make + + + rebuild + + * + + + clean + install + + also-make + + + test.single + + * + + + process-test-classes + surefire:test + + + ${packageClassName} + + + + + run + + jar + + + process-classes + org.codehaus.mojo:exec-maven-plugin:3.1.0:exec + + + + ${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs} + + ${packageClassName} + java + + build-with-dependencies + + + debug + + jar + + + process-classes + org.codehaus.mojo:exec-maven-plugin:3.1.0:exec + + + -agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address} + ${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs} + + ${packageClassName} + java + true + + build-with-dependencies + + + run.single.main + + * + + + process-classes + org.codehaus.mojo:exec-maven-plugin:3.1.0:exec + + + + ${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs} + java + ${packageClassName} + ${classPathScope} + + build-with-dependencies + + + + debug.single.main + + * + + + process-test-classes + org.codehaus.mojo:exec-maven-plugin:3.1.0:exec + + + -agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address} + ${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs} + java + ${packageClassName} + ${classPathScope} + true + + build-with-dependencies + + diff --git a/patches/7001.diff b/patches/7001.diff deleted file mode 100644 index df519ca8..00000000 --- a/patches/7001.diff +++ /dev/null @@ -1,264 +0,0 @@ -diff --git a/java/maven.embedder/build.xml b/java/maven.embedder/build.xml -index 1cf553760703..9dd89c01e769 100644 ---- a/java/maven.embedder/build.xml -+++ b/java/maven.embedder/build.xml -@@ -25,7 +25,9 @@ - - - -- -+ -+ -+ - - - -diff --git a/java/maven.embedder/external/binaries-list b/java/maven.embedder/external/binaries-list -index 94e918c9a5f8..c62571b895cb 100644 ---- a/java/maven.embedder/external/binaries-list -+++ b/java/maven.embedder/external/binaries-list -@@ -17,3 +17,4 @@ - DC15DFF8F701B227EE523EEB7A17F77C10EAFE2F org.jdom:jdom2:2.0.6.1 - 5D9CE6ADD7B714B8095F0E3E396C5E9F8C5DCFEF org.apache.maven.shared:maven-dependency-tree:2.2 - FC5C01A07E4A2DDF84AF0DFADF382E6F7993462D org.apache.maven:apache-maven:3.9.6:bin@zip -+C4A06A64E650562F30B7BF9AAEC1BFED43ACA12B com.google.guava:failureaccess:1.0.2 -diff --git a/java/maven.embedder/external/failureaccess-1.0.2-license.txt b/java/maven.embedder/external/failureaccess-1.0.2-license.txt -new file mode 100644 -index 000000000000..0c948ba081a4 ---- /dev/null -+++ b/java/maven.embedder/external/failureaccess-1.0.2-license.txt -@@ -0,0 +1,208 @@ -+Name: Guava - Failure Access Library -+Version: 1.0.2 -+License: Apache-2.0 -+Origin: https://github.com/google/guava -+Description: A Guava subproject -+ -+ -+ Apache License -+ Version 2.0, January 2004 -+ http://www.apache.org/licenses/ -+ -+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -+ -+ 1. Definitions. -+ -+ "License" shall mean the terms and conditions for use, reproduction, -+ and distribution as defined by Sections 1 through 9 of this document. -+ -+ "Licensor" shall mean the copyright owner or entity authorized by -+ the copyright owner that is granting the License. -+ -+ "Legal Entity" shall mean the union of the acting entity and all -+ other entities that control, are controlled by, or are under common -+ control with that entity. For the purposes of this definition, -+ "control" means (i) the power, direct or indirect, to cause the -+ direction or management of such entity, whether by contract or -+ otherwise, or (ii) ownership of fifty percent (50%) or more of the -+ outstanding shares, or (iii) beneficial ownership of such entity. -+ -+ "You" (or "Your") shall mean an individual or Legal Entity -+ exercising permissions granted by this License. -+ -+ "Source" form shall mean the preferred form for making modifications, -+ including but not limited to software source code, documentation -+ source, and configuration files. -+ -+ "Object" form shall mean any form resulting from mechanical -+ transformation or translation of a Source form, including but -+ not limited to compiled object code, generated documentation, -+ and conversions to other media types. -+ -+ "Work" shall mean the work of authorship, whether in Source or -+ Object form, made available under the License, as indicated by a -+ copyright notice that is included in or attached to the work -+ (an example is provided in the Appendix below). -+ -+ "Derivative Works" shall mean any work, whether in Source or Object -+ form, that is based on (or derived from) the Work and for which the -+ editorial revisions, annotations, elaborations, or other modifications -+ represent, as a whole, an original work of authorship. For the purposes -+ of this License, Derivative Works shall not include works that remain -+ separable from, or merely link (or bind by name) to the interfaces of, -+ the Work and Derivative Works thereof. -+ -+ "Contribution" shall mean any work of authorship, including -+ the original version of the Work and any modifications or additions -+ to that Work or Derivative Works thereof, that is intentionally -+ submitted to Licensor for inclusion in the Work by the copyright owner -+ or by an individual or Legal Entity authorized to submit on behalf of -+ the copyright owner. For the purposes of this definition, "submitted" -+ means any form of electronic, verbal, or written communication sent -+ to the Licensor or its representatives, including but not limited to -+ communication on electronic mailing lists, source code control systems, -+ and issue tracking systems that are managed by, or on behalf of, the -+ Licensor for the purpose of discussing and improving the Work, but -+ excluding communication that is conspicuously marked or otherwise -+ designated in writing by the copyright owner as "Not a Contribution." -+ -+ "Contributor" shall mean Licensor and any individual or Legal Entity -+ on behalf of whom a Contribution has been received by Licensor and -+ subsequently incorporated within the Work. -+ -+ 2. Grant of Copyright License. Subject to the terms and conditions of -+ this License, each Contributor hereby grants to You a perpetual, -+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable -+ copyright license to reproduce, prepare Derivative Works of, -+ publicly display, publicly perform, sublicense, and distribute the -+ Work and such Derivative Works in Source or Object form. -+ -+ 3. Grant of Patent License. Subject to the terms and conditions of -+ this License, each Contributor hereby grants to You a perpetual, -+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable -+ (except as stated in this section) patent license to make, have made, -+ use, offer to sell, sell, import, and otherwise transfer the Work, -+ where such license applies only to those patent claims licensable -+ by such Contributor that are necessarily infringed by their -+ Contribution(s) alone or by combination of their Contribution(s) -+ with the Work to which such Contribution(s) was submitted. If You -+ institute patent litigation against any entity (including a -+ cross-claim or counterclaim in a lawsuit) alleging that the Work -+ or a Contribution incorporated within the Work constitutes direct -+ or contributory patent infringement, then any patent licenses -+ granted to You under this License for that Work shall terminate -+ as of the date such litigation is filed. -+ -+ 4. Redistribution. You may reproduce and distribute copies of the -+ Work or Derivative Works thereof in any medium, with or without -+ modifications, and in Source or Object form, provided that You -+ meet the following conditions: -+ -+ (a) You must give any other recipients of the Work or -+ Derivative Works a copy of this License; and -+ -+ (b) You must cause any modified files to carry prominent notices -+ stating that You changed the files; and -+ -+ (c) You must retain, in the Source form of any Derivative Works -+ that You distribute, all copyright, patent, trademark, and -+ attribution notices from the Source form of the Work, -+ excluding those notices that do not pertain to any part of -+ the Derivative Works; and -+ -+ (d) If the Work includes a "NOTICE" text file as part of its -+ distribution, then any Derivative Works that You distribute must -+ include a readable copy of the attribution notices contained -+ within such NOTICE file, excluding those notices that do not -+ pertain to any part of the Derivative Works, in at least one -+ of the following places: within a NOTICE text file distributed -+ as part of the Derivative Works; within the Source form or -+ documentation, if provided along with the Derivative Works; or, -+ within a display generated by the Derivative Works, if and -+ wherever such third-party notices normally appear. The contents -+ of the NOTICE file are for informational purposes only and -+ do not modify the License. You may add Your own attribution -+ notices within Derivative Works that You distribute, alongside -+ or as an addendum to the NOTICE text from the Work, provided -+ that such additional attribution notices cannot be construed -+ as modifying the License. -+ -+ You may add Your own copyright statement to Your modifications and -+ may provide additional or different license terms and conditions -+ for use, reproduction, or distribution of Your modifications, or -+ for any such Derivative Works as a whole, provided Your use, -+ reproduction, and distribution of the Work otherwise complies with -+ the conditions stated in this License. -+ -+ 5. Submission of Contributions. Unless You explicitly state otherwise, -+ any Contribution intentionally submitted for inclusion in the Work -+ by You to the Licensor shall be under the terms and conditions of -+ this License, without any additional terms or conditions. -+ Notwithstanding the above, nothing herein shall supersede or modify -+ the terms of any separate license agreement you may have executed -+ with Licensor regarding such Contributions. -+ -+ 6. Trademarks. This License does not grant permission to use the trade -+ names, trademarks, service marks, or product names of the Licensor, -+ except as required for reasonable and customary use in describing the -+ origin of the Work and reproducing the content of the NOTICE file. -+ -+ 7. Disclaimer of Warranty. Unless required by applicable law or -+ agreed to in writing, Licensor provides the Work (and each -+ Contributor provides its Contributions) on an "AS IS" BASIS, -+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -+ implied, including, without limitation, any warranties or conditions -+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A -+ PARTICULAR PURPOSE. You are solely responsible for determining the -+ appropriateness of using or redistributing the Work and assume any -+ risks associated with Your exercise of permissions under this License. -+ -+ 8. Limitation of Liability. In no event and under no legal theory, -+ whether in tort (including negligence), contract, or otherwise, -+ unless required by applicable law (such as deliberate and grossly -+ negligent acts) or agreed to in writing, shall any Contributor be -+ liable to You for damages, including any direct, indirect, special, -+ incidental, or consequential damages of any character arising as a -+ result of this License or out of the use or inability to use the -+ Work (including but not limited to damages for loss of goodwill, -+ work stoppage, computer failure or malfunction, or any and all -+ other commercial damages or losses), even if such Contributor -+ has been advised of the possibility of such damages. -+ -+ 9. Accepting Warranty or Additional Liability. While redistributing -+ the Work or Derivative Works thereof, You may choose to offer, -+ and charge a fee for, acceptance of support, warranty, indemnity, -+ or other liability obligations and/or rights consistent with this -+ License. However, in accepting such obligations, You may act only -+ on Your own behalf and on Your sole responsibility, not on behalf -+ of any other Contributor, and only if You agree to indemnify, -+ defend, and hold each Contributor harmless for any liability -+ incurred by, or claims asserted against, such Contributor by reason -+ of your accepting any such warranty or additional liability. -+ -+ END OF TERMS AND CONDITIONS -+ -+ APPENDIX: How to apply the Apache License to your work. -+ -+ To apply the Apache License to your work, attach the following -+ boilerplate notice, with the fields enclosed by brackets "[]" -+ replaced with your own identifying information. (Don't include -+ the brackets!) The text should be enclosed in the appropriate -+ comment syntax for the file format. We also recommend that a -+ file or class name and description of purpose be included on the -+ same "printed page" as the copyright notice for easier -+ identification within third-party archives. -+ -+ Copyright [yyyy] [name of copyright owner] -+ -+ Licensed 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. -diff --git a/java/maven.embedder/nbproject/project.xml b/java/maven.embedder/nbproject/project.xml -index 8271d91403a2..69494e4f2e05 100644 ---- a/java/maven.embedder/nbproject/project.xml -+++ b/java/maven.embedder/nbproject/project.xml -@@ -305,7 +305,7 @@ - ../maven/lib/commons-lang3-3.12.0.jar - - -- ../maven/lib/failureaccess-1.0.1.jar -+ ../maven/lib/failureaccess-1.0.2.jar - - - ../maven/lib/guava-32.0.1-jre.jar -diff --git a/nbbuild/antsrc/org/netbeans/nbbuild/extlibs/ignored-overlaps b/nbbuild/antsrc/org/netbeans/nbbuild/extlibs/ignored-overlaps -index ec07902509..dd52e08038 100644 ---- a/nbbuild/antsrc/org/netbeans/nbbuild/extlibs/ignored-overlaps -+++ b/nbbuild/antsrc/org/netbeans/nbbuild/extlibs/ignored-overlaps -@@ -26,7 +26,7 @@ ide/db.sql.visualeditor/external/javacc-7.0.10.jar java/performance/external/jav - java/maven.embedder/external/apache-maven-3.9.6-bin.zip ide/slf4j.api/external/slf4j-api-1.7.36.jar - java/maven.embedder/external/apache-maven-3.9.6-bin.zip platform/o.apache.commons.lang3/external/commons-lang3-3.12.0.jar - java/maven.embedder/external/apache-maven-3.9.6-bin.zip platform/o.apache.commons.codec/external/commons-codec-1.16.1.jar --java/maven.embedder/external/apache-maven-3.9.6-bin.zip ide/c.google.guava.failureaccess/external/failureaccess-1.0.2.jar -+java/maven.embedder/external/failureaccess-1.0.2.jar ide/c.google.guava.failureaccess/external/failureaccess-1.0.2.jar - java/maven.embedder/external/apache-maven-3.9.6-bin.zip java/maven.indexer/external/javax.annotation-api-1.3.2.jar - - # Used to parse data during build, but need to as a lib for ide cluster diff --git a/patches/7271.diff b/patches/7271.diff deleted file mode 100644 index dee2a821..00000000 --- a/patches/7271.diff +++ /dev/null @@ -1,31 +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 5349a20e091a..d24c6f5ec98a 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 -@@ -107,18 +107,16 @@ public static void main(String... args) throws IOException, InterruptedException - if ("@types".equals(module.getFileName().toString())) continue; - if ("@esbuild".equals(module.getFileName().toString())) continue; - if ("@microsoft".equals(module.getFileName().toString())) continue; -- if ("@vscode".equals(module.getFileName().toString())) { -- try (DirectoryStream sds = Files.newDirectoryStream(module)) { -- for (Path sModule : sds) { -- checkModule(sModule, sb, tokens2Projects, project2License, bundlesDir, targetDir, externalDir, binariesList); -- } -- } -- continue; -+ Path packageJson = module.resolve("package.json"); -+ if (Files.isReadable(packageJson)) { -+ checkModule(module, sb, tokens2Projects, project2License, bundlesDir, targetDir, externalDir, binariesList); -+ continue; - } -- if ("@ungap".equals(module.getFileName().toString())) { -- module = module.resolve("promise-all-settled"); -+ try (DirectoryStream sds = Files.newDirectoryStream(module)) { -+ for (Path sModule : sds) { -+ checkModule(sModule, sb, tokens2Projects, project2License, bundlesDir, targetDir, externalDir, binariesList); -+ } - } -- checkModule(module, sb, tokens2Projects, project2License, bundlesDir, targetDir, externalDir, binariesList); - } - } - if (sb.length() > 0) { diff --git a/patches/7353.diff b/patches/7353.diff deleted file mode 100644 index ea44d4c8..00000000 --- a/patches/7353.diff +++ /dev/null @@ -1,16 +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 b33ff46f4643..5e5d10079dae 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 -@@ -1022,6 +1022,11 @@ public CompletableFuture>> codeAction(CodeActio - if (err.getEndPosition().getOffset() < lineStartOffset || err.getStartPosition().getOffset() > lineEndOffset) { - continue; - } -+ int lineStart = NbDocument.findLineNumber(doc, startOffset); -+ int errStartLine = NbDocument.findLineNumber(doc, err.getStartPosition().getOffset()); -+ if(errStartLine != lineStart){ -+ continue; -+ } - } - Optional diag = diagnostics.stream().filter(d -> entry.getKey().equals(d.getCode().getLeft())).findFirst(); - org.netbeans.api.lsp.Diagnostic.LazyCodeActions actions = err.getActions(); diff --git a/patches/7368.diff b/patches/7368.diff deleted file mode 100644 index 9ea78b17..00000000 --- a/patches/7368.diff +++ /dev/null @@ -1,507 +0,0 @@ -diff --git a/ide/api.lsp/apichanges.xml b/ide/api.lsp/apichanges.xml -index faa0a1b27335..d3cbe5631476 100644 ---- a/ide/api.lsp/apichanges.xml -+++ b/ide/api.lsp/apichanges.xml -@@ -51,6 +51,19 @@ - - - -+ -+ -+ Adding CodeActionProvider.getSupportedCodeActionKinds method -+ -+ -+ -+ -+ -+ A CodeActionProvider.getSupportedCodeActionKinds method is -+ introduced that allows to specify supported kinds of code actions. -+ -+ -+ - - - Adding ErrorProvider.Context.getHintsConfigFile() method -diff --git a/ide/api.lsp/manifest.mf b/ide/api.lsp/manifest.mf -index ca8e28f14b05..476426e3b2eb 100644 ---- a/ide/api.lsp/manifest.mf -+++ b/ide/api.lsp/manifest.mf -@@ -1,5 +1,5 @@ - Manifest-Version: 1.0 - OpenIDE-Module: org.netbeans.api.lsp/1 - OpenIDE-Module-Localizing-Bundle: org/netbeans/api/lsp/Bundle.properties --OpenIDE-Module-Specification-Version: 1.25 -+OpenIDE-Module-Specification-Version: 1.27 - AutoUpdate-Show-In-Client: false -diff --git a/ide/api.lsp/src/org/netbeans/spi/lsp/CodeActionProvider.java b/ide/api.lsp/src/org/netbeans/spi/lsp/CodeActionProvider.java -index b234018a0d50..3006803ebd9b 100644 ---- a/ide/api.lsp/src/org/netbeans/spi/lsp/CodeActionProvider.java -+++ b/ide/api.lsp/src/org/netbeans/spi/lsp/CodeActionProvider.java -@@ -19,7 +19,9 @@ - package org.netbeans.spi.lsp; - - import java.util.List; -+import java.util.Set; - import javax.swing.text.Document; -+import org.netbeans.api.annotations.common.CheckForNull; - import org.netbeans.api.annotations.common.NonNull; - import org.netbeans.api.lsp.CodeAction; - import org.netbeans.api.lsp.Range; -@@ -42,4 +44,15 @@ public interface CodeActionProvider { - * @since 1.23 - */ - public List getCodeActions(@NonNull Document doc, @NonNull Range range, @NonNull Lookup context); -+ -+ /** -+ * Return the set of code action kinds produced by this provider. May return null -+ * if unknown/all kinds may be produced. -+ * -+ * @return the set of supported code action kinds, or {@code null} -+ * @since 1.27 -+ */ -+ public default @CheckForNull Set getSupportedCodeActionKinds() { -+ return null; -+ } - } -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Utils.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Utils.java -index d818c9315f71..bb69242e7cf0 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Utils.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Utils.java -@@ -33,6 +33,7 @@ - import java.util.List; - import java.util.Locale; - import java.util.Set; -+import java.util.function.Predicate; - import java.util.stream.Collectors; - import javax.lang.model.element.Element; - import javax.lang.model.element.ElementKind; -@@ -571,4 +572,10 @@ public static WorkspaceEdit workspaceEditFromApi(org.netbeans.api.lsp.WorkspaceE - } - return new WorkspaceEdit(documentChanges); - } -+ -+ public static Predicate codeActionKindFilter(List only) { -+ return k -> only == null || -+ only.stream() -+ .anyMatch(o -> k.equals(o) || k.startsWith(o + ".")); -+ } - } -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider.java -index 47eccb3455dd..5a167e313edc 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider.java -@@ -37,6 +37,7 @@ - import org.eclipse.lsp4j.Command; - import org.eclipse.lsp4j.Position; - import org.eclipse.xtext.xbase.lib.Pure; -+import org.netbeans.api.annotations.common.CheckForNull; - import org.netbeans.api.java.source.CompilationInfo; - import org.netbeans.api.java.source.ElementHandle; - import org.netbeans.modules.java.lsp.server.Utils; -@@ -54,6 +55,10 @@ public abstract class CodeActionsProvider { - public static final String DATA = "data"; - protected static final String ERROR = ""; //NOI18N - -+ public @CheckForNull Set getSupportedCodeActionKinds() { -+ return null; -+ } -+ - public abstract List getCodeActions(NbCodeLanguageClient client, ResultIterator resultIterator, CodeActionParams params) throws Exception; - - public CompletableFuture resolve(NbCodeLanguageClient client, CodeAction codeAction, Object data) { -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider2LspApiBridge.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider2LspApiBridge.java -index 7b7f70e9e272..6144650c7c53 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider2LspApiBridge.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider2LspApiBridge.java -@@ -25,6 +25,7 @@ - import java.util.List; - import java.util.Set; - import java.util.concurrent.CompletableFuture; -+import java.util.function.Predicate; - import javax.swing.text.Document; - import javax.swing.text.StyledDocument; - import org.eclipse.lsp4j.CodeAction; -@@ -72,6 +73,23 @@ public CompletableFuture processCommand(NbCodeLanguageClient client, Str - return CompletableFuture.completedFuture(false); - } - -+ @Override -+ public Set getSupportedCodeActionKinds() { -+ Set supportedCodeActionKinds = new HashSet<>(); -+ -+ for (CodeActionProvider caProvider : Lookup.getDefault().lookupAll(CodeActionProvider.class)) { -+ Set providerSupportedCodeActionKinds = caProvider.getSupportedCodeActionKinds(); -+ -+ if (providerSupportedCodeActionKinds == null) { -+ return null; -+ } -+ -+ supportedCodeActionKinds.addAll(providerSupportedCodeActionKinds); -+ } -+ -+ return supportedCodeActionKinds; -+ } -+ - @Override - public List getCodeActions(NbCodeLanguageClient client, ResultIterator resultIterator, CodeActionParams params) throws Exception { - lastCodeActions = new ArrayList<>(); -@@ -84,7 +102,14 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat - org.netbeans.api.lsp.Range r = new org.netbeans.api.lsp.Range(startOffset, endOffset); - List only = params.getContext().getOnly(); - Lookup l = only != null ? Lookups.fixed(client, resultIterator, only) : Lookups.fixed(client, resultIterator); -+ Predicate codeActionKindPermitted = Utils.codeActionKindFilter(only); - for (CodeActionProvider caProvider : Lookup.getDefault().lookupAll(CodeActionProvider.class)) { -+ Set supportedCodeActionKinds = caProvider.getSupportedCodeActionKinds(); -+ if (supportedCodeActionKinds != null && -+ supportedCodeActionKinds.stream() -+ .noneMatch(kind -> codeActionKindPermitted.test(kind))) { -+ continue; -+ } - try { - for (org.netbeans.api.lsp.CodeAction ca : caProvider.getCodeActions(doc, r, l)) { - Object data = null; -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/IntroduceCodeActions.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/IntroduceCodeActions.java -new file mode 100644 -index 000000000000..8fa35abf9038 ---- /dev/null -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/IntroduceCodeActions.java -@@ -0,0 +1,126 @@ -+/* -+ * 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.lsp.server.protocol; -+ -+import java.util.ArrayList; -+import java.util.Arrays; -+import java.util.Collections; -+import java.util.EnumMap; -+import java.util.HashSet; -+import java.util.List; -+import java.util.Set; -+import java.util.concurrent.atomic.AtomicBoolean; -+import javax.swing.text.StyledDocument; -+import org.eclipse.lsp4j.CodeAction; -+import org.eclipse.lsp4j.CodeActionKind; -+import org.eclipse.lsp4j.CodeActionParams; -+import org.eclipse.lsp4j.Command; -+import org.eclipse.lsp4j.Range; -+import org.eclipse.lsp4j.ResourceOperation; -+import org.eclipse.lsp4j.TextDocumentEdit; -+import org.eclipse.lsp4j.TextEdit; -+import org.eclipse.lsp4j.VersionedTextDocumentIdentifier; -+import org.eclipse.lsp4j.WorkspaceEdit; -+import org.eclipse.lsp4j.jsonrpc.messages.Either; -+import org.netbeans.api.java.source.CompilationController; -+import org.netbeans.api.java.source.JavaSource; -+import org.netbeans.api.java.source.ModificationResult; -+import org.netbeans.modules.java.editor.codegen.GeneratorUtils; -+import org.netbeans.modules.java.hints.introduce.IntroduceFixBase; -+import org.netbeans.modules.java.hints.introduce.IntroduceHint; -+import org.netbeans.modules.java.hints.introduce.IntroduceKind; -+import org.netbeans.modules.java.lsp.server.Utils; -+import org.netbeans.modules.parsing.api.ResultIterator; -+import org.netbeans.spi.editor.hints.ErrorDescription; -+import org.netbeans.spi.editor.hints.Fix; -+import org.openide.filesystems.FileObject; -+import org.openide.util.lookup.ServiceProvider; -+ -+@ServiceProvider(service = CodeActionsProvider.class) -+public final class IntroduceCodeActions extends CodeActionsProvider { -+ -+ private static final Set SUPPORTED_CODE_ACTION_KINDS = -+ Collections.unmodifiableSet(new HashSet<>(Arrays.asList(CodeActionKind.RefactorExtract))); -+ -+ public IntroduceCodeActions() { -+ } -+ -+ @Override -+ public Set getSupportedCodeActionKinds() { -+ return SUPPORTED_CODE_ACTION_KINDS; -+ } -+ -+ @Override -+ public List getCodeActions(NbCodeLanguageClient client, ResultIterator resultIterator, CodeActionParams params) throws Exception { -+ Range range = params.getRange(); -+ List result = new ArrayList<>(); -+ -+ if (client.getNbCodeCapabilities().wantsJavaSupport() && !range.getStart().equals(range.getEnd())) { -+ CompilationController cc = resultIterator.getParserResult() != null ? CompilationController.get(resultIterator.getParserResult()) : null; -+ -+ if (cc != null) { -+ cc.toPhase(JavaSource.Phase.RESOLVED); -+ -+ StyledDocument doc = (StyledDocument) cc.getDocument(); -+ int startOffset = Utils.getOffset(doc, range.getStart()); -+ int endOffset = Utils.getOffset(doc, range.getEnd()); -+ -+ for (ErrorDescription err : IntroduceHint.computeError(cc, startOffset, endOffset, new EnumMap(IntroduceKind.class), new EnumMap(IntroduceKind.class), new AtomicBoolean())) { -+ for (Fix fix : err.getFixes().getFixes()) { -+ if (fix instanceof IntroduceFixBase) { -+ try { -+ ModificationResult changes = ((IntroduceFixBase) fix).getModificationResult(); -+ if (changes != null) { -+ List> documentChanges = new ArrayList<>(); -+ Set fos = changes.getModifiedFileObjects(); -+ if (fos.size() == 1) { -+ FileObject fileObject = fos.iterator().next(); -+ List diffs = changes.getDifferences(fileObject); -+ if (diffs != null) { -+ List edits = new ArrayList<>(); -+ for (ModificationResult.Difference diff : diffs) { -+ String newText = diff.getNewText(); -+ edits.add(new TextEdit(new Range(Utils.createPosition(fileObject, diff.getStartPosition().getOffset()), -+ Utils.createPosition(fileObject, diff.getEndPosition().getOffset())), -+ newText != null ? newText : "")); -+ } -+ documentChanges.add(Either.forLeft(new TextDocumentEdit(new VersionedTextDocumentIdentifier(Utils.toUri(fileObject), -1), edits))); -+ } -+ CodeAction codeAction = new CodeAction(fix.getText()); -+ codeAction.setKind(CodeActionKind.RefactorExtract); -+ codeAction.setEdit(new WorkspaceEdit(documentChanges)); -+ int renameOffset = ((IntroduceFixBase) fix).getNameOffset(changes); -+ if (renameOffset >= 0) { -+ codeAction.setCommand(new Command("Rename", client.getNbCodeCapabilities().getCommandPrefix() + ".rename.element.at", Collections.singletonList(renameOffset))); -+ } -+ result.add(codeAction); -+ } -+ } -+ } catch (GeneratorUtils.DuplicateMemberException dme) { -+ } -+ } -+ } -+ } -+ } -+ } -+ -+ return result; -+ } -+ -+} -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 b33ff46f4643..e0cb2c215827 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 -@@ -67,6 +67,7 @@ - import java.util.concurrent.atomic.AtomicReference; - import java.util.function.BiConsumer; - import java.util.function.IntFunction; -+import java.util.function.Predicate; - import java.util.logging.Level; - import java.util.logging.Logger; - import java.util.prefs.Preferences; -@@ -92,6 +93,7 @@ - import org.eclipse.lsp4j.ClientCapabilities; - import org.eclipse.lsp4j.CodeAction; - import org.eclipse.lsp4j.CodeActionKind; -+import org.eclipse.lsp4j.CodeActionKindCapabilities; - import org.eclipse.lsp4j.CodeActionParams; - import org.eclipse.lsp4j.CodeLens; - import org.eclipse.lsp4j.CodeLensParams; -@@ -991,7 +993,9 @@ public CompletableFuture>> codeAction(CodeActio - Range range = params.getRange(); - int startOffset = Utils.getOffset(doc, range.getStart()); - int endOffset = Utils.getOffset(doc, range.getEnd()); -- if (startOffset == endOffset || !params.getContext().getDiagnostics().isEmpty()) { -+ Predicate codeActionKindPermitted = Utils.codeActionKindFilter(params.getContext().getOnly()); -+ if ((startOffset == endOffset || !params.getContext().getDiagnostics().isEmpty()) && -+ (codeActionKindPermitted.test(CodeActionKind.QuickFix) || codeActionKindPermitted.test(CodeActionKind.RefactorRewrite))) { - final javax.swing.text.Element elem = NbDocument.findLineRootElement(doc); - int lineStartOffset = elem.getStartOffset(); - int lineEndOffset = elem.getEndOffset(); -@@ -1031,7 +1035,11 @@ public CompletableFuture>> codeAction(CodeActio - if (diag.isPresent()) { - action.setDiagnostics(Collections.singletonList(diag.get())); - } -- action.setKind(kind(err.getSeverity())); -+ String codeActionKind = kind(err.getSeverity()); -+ if (!codeActionKindPermitted.test(codeActionKind)) { -+ continue; -+ } -+ action.setKind(codeActionKind); - if (inputAction.getCommand() != null) { - List commandParams = new ArrayList<>(); - -@@ -1068,59 +1076,23 @@ public CompletableFuture>> codeAction(CodeActio - public void run(ResultIterator resultIterator) throws Exception { - //code generators: - for (CodeActionsProvider codeGenerator : Lookup.getDefault().lookupAll(CodeActionsProvider.class)) { -+ Set supportedCodeActionKinds = codeGenerator.getSupportedCodeActionKinds(); -+ if (supportedCodeActionKinds != null && -+ supportedCodeActionKinds.stream() -+ .noneMatch(kind -> codeActionKindPermitted.test(kind))) { -+ continue; -+ } - try { - for (CodeAction codeAction : codeGenerator.getCodeActions(client, resultIterator, params)) { -+ if (!codeActionKindPermitted.test(codeAction.getKind())) { -+ continue; -+ } - result.add(Either.forRight(codeAction)); - } - } catch (Exception ex) { - client.logMessage(new MessageParams(MessageType.Error, ex.getMessage())); - } - } -- if (client.getNbCodeCapabilities().wantsJavaSupport()) { -- //introduce hints: -- CompilationController cc = resultIterator.getParserResult() != null ? CompilationController.get(resultIterator.getParserResult()) : null; -- if (cc != null) { -- cc.toPhase(JavaSource.Phase.RESOLVED); -- if (!range.getStart().equals(range.getEnd())) { -- for (ErrorDescription err : IntroduceHint.computeError(cc, startOffset, endOffset, new EnumMap(IntroduceKind.class), new EnumMap(IntroduceKind.class), new AtomicBoolean())) { -- for (Fix fix : err.getFixes().getFixes()) { -- if (fix instanceof IntroduceFixBase) { -- try { -- ModificationResult changes = ((IntroduceFixBase) fix).getModificationResult(); -- if (changes != null) { -- List> documentChanges = new ArrayList<>(); -- Set fos = changes.getModifiedFileObjects(); -- if (fos.size() == 1) { -- FileObject fileObject = fos.iterator().next(); -- List diffs = changes.getDifferences(fileObject); -- if (diffs != null) { -- List edits = new ArrayList<>(); -- for (ModificationResult.Difference diff : diffs) { -- String newText = diff.getNewText(); -- edits.add(new TextEdit(new Range(Utils.createPosition(fileObject, diff.getStartPosition().getOffset()), -- Utils.createPosition(fileObject, diff.getEndPosition().getOffset())), -- newText != null ? newText : "")); -- } -- documentChanges.add(Either.forLeft(new TextDocumentEdit(new VersionedTextDocumentIdentifier(Utils.toUri(fileObject), -1), edits))); -- } -- CodeAction codeAction = new CodeAction(fix.getText()); -- codeAction.setKind(CodeActionKind.RefactorExtract); -- codeAction.setEdit(new WorkspaceEdit(documentChanges)); -- int renameOffset = ((IntroduceFixBase) fix).getNameOffset(changes); -- if (renameOffset >= 0) { -- codeAction.setCommand(new Command("Rename", client.getNbCodeCapabilities().getCommandPrefix() + ".rename.element.at", Collections.singletonList(renameOffset))); -- } -- result.add(Either.forRight(codeAction)); -- } -- } -- } catch (GeneratorUtils.DuplicateMemberException dme) { -- } -- } -- } -- } -- } -- } -- } - } - }); - } catch (ParseException ex) { -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 1b8d71ec96fb..78b327fbec90 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 -@@ -5786,6 +5786,95 @@ public void testDefaultLookupJustOnce() throws Exception { - assertEquals(1, mm4.size()); - } - -+ public void testErrorBasedCodeActionFiltering() throws Exception { -+ File src = new File(getWorkDir(), "Test.java"); -+ src.getParentFile().mkdirs(); -+ String code = "public class Test {\n" + -+ " public void test() {\n" + -+ " System.err.println(0 << 0);\n" + -+ " }\n" + -+ "}\n"; -+ try (Writer w = new FileWriter(src)) { -+ w.write(code); -+ } -+ -+ List[] diags = new List[1]; -+ Launcher serverLauncher = createClientLauncherWithLogging(new LspClient() { -+ private int publishedDiagnosticsCount; -+ @Override -+ public void telemetryEvent(Object arg0) { -+ } -+ -+ @Override -+ public void publishDiagnostics(PublishDiagnosticsParams params) { -+ synchronized (diags) { -+ if (publishedDiagnosticsCount++ == 1) { -+ diags[0] = params.getDiagnostics(); -+ diags.notifyAll(); -+ } -+ } -+ } -+ -+ @Override -+ public void showMessage(MessageParams arg0) { -+ } -+ -+ @Override -+ public CompletableFuture showMessageRequest(ShowMessageRequestParams arg0) { -+ return CompletableFuture.completedFuture(new MessageActionItem(arg0.getActions().get(0).getTitle())); -+ } -+ -+ @Override -+ public void logMessage(MessageParams arg0) { -+ throw new UnsupportedOperationException("Not supported yet."); -+ } -+ -+ @Override -+ public CompletableFuture applyEdit(ApplyWorkspaceEditParams params) { -+ throw new UnsupportedOperationException("Not supported yet."); -+ } -+ -+ }, client.getInputStream(), client.getOutputStream()); -+ serverLauncher.startListening(); -+ LanguageServer server = serverLauncher.getRemoteProxy(); -+ server.initialize(new InitializeParams()).get(); -+ String uri = src.toURI().toString(); -+ server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(uri, "java", 0, code))); -+ synchronized (diags) { -+ while (diags[0] == null) { -+ try { -+ diags.wait(); -+ } catch (InterruptedException ex) { -+ } -+ } -+ } -+ VersionedTextDocumentIdentifier id = new VersionedTextDocumentIdentifier(src.toURI().toString(), 1); -+ Set presentKinds; -+ List> codeActions; -+ codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 32), new Position(2, 32)), new CodeActionContext(Collections.emptyList(), null))).get(); -+ presentKinds = codeActions.stream().map(d -> d.getRight().getKind()).collect(Collectors.toSet()); -+ assertEquals(new HashSet<>(Arrays.asList(CodeActionKind.RefactorRewrite, CodeActionKind.QuickFix)), presentKinds); -+ codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 32), new Position(2, 32)), new CodeActionContext(Collections.emptyList(), Arrays.asList(CodeActionKind.RefactorRewrite)))).get(); -+ presentKinds = codeActions.stream().map(d -> d.getRight().getKind()).collect(Collectors.toSet()); -+ assertEquals(new HashSet<>(Arrays.asList(CodeActionKind.RefactorRewrite)), presentKinds); -+ codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 32), new Position(2, 32)), new CodeActionContext(Collections.emptyList(), Arrays.asList(CodeActionKind.Refactor)))).get(); -+ presentKinds = codeActions.stream().map(d -> d.getRight().getKind()).collect(Collectors.toSet()); -+ assertEquals(new HashSet<>(Arrays.asList(CodeActionKind.RefactorRewrite)), presentKinds); -+ codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 32), new Position(2, 32)), new CodeActionContext(Collections.emptyList(), Arrays.asList(CodeActionKind.QuickFix)))).get(); -+ presentKinds = codeActions.stream().map(d -> d.getRight().getKind()).collect(Collectors.toSet()); -+ assertEquals(new HashSet<>(Arrays.asList(CodeActionKind.QuickFix)), presentKinds); -+ codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 32), new Position(2, 32)), new CodeActionContext(Collections.emptyList(), Arrays.asList(CodeActionKind.RefactorExtract)))).get(); -+ presentKinds = codeActions.stream().map(d -> d.getRight().getKind()).collect(Collectors.toSet()); -+ assertEquals(new HashSet<>(Arrays.asList()), presentKinds); -+ codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 27), new Position(2, 33)), new CodeActionContext(Collections.emptyList(), Arrays.asList(CodeActionKind.RefactorExtract)))).get(); -+ presentKinds = codeActions.stream().map(d -> d.getRight().getKind()).collect(Collectors.toSet()); -+ assertEquals(new HashSet<>(Arrays.asList(CodeActionKind.RefactorExtract)), presentKinds); -+ //verify surround-with hints are correctly filtered: -+ codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 0), new Position(3, 0)), new CodeActionContext(Collections.emptyList(), Arrays.asList(CodeActionKind.RefactorExtract)))).get(); -+ presentKinds = codeActions.stream().map(d -> d.getRight().getKind()).collect(Collectors.toSet()); -+ assertEquals(new HashSet<>(Arrays.asList(CodeActionKind.RefactorExtract)), presentKinds); -+ } -+ - static { - System.setProperty("SourcePath.no.source.filter", "true"); - JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true; diff --git a/patches/7370.diff b/patches/7370.diff deleted file mode 100644 index a36ed8ac..00000000 --- a/patches/7370.diff +++ /dev/null @@ -1,135 +0,0 @@ -diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/infrastructure/JavaErrorProvider.java b/java/java.hints/src/org/netbeans/modules/java/hints/infrastructure/JavaErrorProvider.java -index c8725dce9fdb..dbccee227fb5 100644 ---- a/java/java.hints/src/org/netbeans/modules/java/hints/infrastructure/JavaErrorProvider.java -+++ b/java/java.hints/src/org/netbeans/modules/java/hints/infrastructure/JavaErrorProvider.java -@@ -206,7 +206,7 @@ private static List convertFixes(ErrorDescription err, Consumer - - -+ -+ -+ Result.parseLine permits parsing of argfiles based on current working directory. -+ -+ -+ -+ -+ -+

-+ CompilerOptionsQueryImplementation.Result.parseLine is enhanced with the ability to parse -+ argfiles, based on a specified current working directory -+

-+
-+ -+
- - - Allows SourceJavadocAttacherImplementation.Definer to reject a binary root. -diff --git a/java/api.java/manifest.mf b/java/api.java/manifest.mf -index 56d512e79aa2..1c9b76387829 100644 ---- a/java/api.java/manifest.mf -+++ b/java/api.java/manifest.mf -@@ -1,6 +1,6 @@ - Manifest-Version: 1.0 - OpenIDE-Module: org.netbeans.api.java/1 --OpenIDE-Module-Specification-Version: 1.90 -+OpenIDE-Module-Specification-Version: 1.91 - OpenIDE-Module-Localizing-Bundle: org/netbeans/api/java/queries/Bundle.properties - AutoUpdate-Show-In-Client: false - -diff --git a/java/api.java/src/org/netbeans/spi/java/queries/CompilerOptionsQueryImplementation.java b/java/api.java/src/org/netbeans/spi/java/queries/CompilerOptionsQueryImplementation.java -index bc35eec9cda7..6f678a8c0658 100644 ---- a/java/api.java/src/org/netbeans/spi/java/queries/CompilerOptionsQueryImplementation.java -+++ b/java/api.java/src/org/netbeans/spi/java/queries/CompilerOptionsQueryImplementation.java -@@ -18,13 +18,20 @@ - */ - package org.netbeans.spi.java.queries; - -+import java.io.IOException; -+import java.net.URI; - import java.util.ArrayList; - import java.util.Collections; - import java.util.List; -+import java.util.function.Consumer; -+import java.util.logging.Level; -+import java.util.logging.Logger; - import javax.swing.event.ChangeListener; - import org.netbeans.api.annotations.common.CheckForNull; - import org.netbeans.api.annotations.common.NonNull; -+import org.netbeans.api.annotations.common.NullAllowed; - import org.openide.filesystems.FileObject; -+import org.openide.filesystems.URLMapper; - - /** - * Permits providers to return explicit compiler options for Java source file. -@@ -47,6 +54,8 @@ public interface CompilerOptionsQueryImplementation { - * ability to listen to it. - */ - public abstract static class Result { -+ private static final Logger LOG = Logger.getLogger(CompilerOptionsQueryImplementation.class.getName()); -+ - /** - * Gets the explicit compiler options. - * @return the list of the compiler options -@@ -72,9 +81,29 @@ public abstract static class Result { - * @return a list of command line arguments - */ - protected final List parseLine(@NonNull final String commandLine) { -+ return doParseLine(commandLine, null); -+ } -+ -+ /** -+ * Utility method the tokenize the command line into individual arguments. -+ * @param commandLine the command line to be tokenized -+ * @param workingDirectory if set to null, argument files will not be supported; -+ * if non-null, argument file names will be resolved relative to this directory -+ * @return a list of command line arguments -+ * @since 1.92 -+ */ -+ protected final List parseLine(@NonNull final String commandLine, -+ @NullAllowed URI workingDirectory) { -+ return doParseLine(commandLine, workingDirectory); -+ } -+ -+ static List doParseLine(@NonNull final String commandLine, -+ @NullAllowed URI workingDirectory) { - final List result = new ArrayList<>(); - StringBuilder current = new StringBuilder(); - boolean escape = false, doubleQuote = false, quote = false; -+ Consumer defaultHandleOption = result::add; -+ Consumer handleOption = defaultHandleOption; - for (int i = 0; i < commandLine.length(); i++) { - final char c = commandLine.charAt(i); - switch (c) { -@@ -99,7 +128,8 @@ protected final List parseLine(@NonNull final String commandLine) { - case '\t': //NOI18N - if (!escape && !quote && !doubleQuote) { - if (current.length() > 0) { -- result.add(current.toString()); -+ handleOption.accept(current.toString()); -+ handleOption = defaultHandleOption; - current = new StringBuilder(); - } - } else { -@@ -107,6 +137,27 @@ protected final List parseLine(@NonNull final String commandLine) { - } - escape = false; - break; -+ case '@': -+ if (workingDirectory != null && i + 1 < commandLine.length() && commandLine.charAt(i + 1) != '@' && current.length() == 0) { -+ handleOption = path -> { -+ try { -+ URI resolved = workingDirectory.resolve(path); -+ FileObject file = URLMapper.findFileObject(resolved.toURL()); -+ if (file == null) { -+ LOG.log(Level.FINE, "URI {0}, resolved to {1}, did not yield an existing file", new Object[] {path, resolved.toString()}); -+ result.add("@" + path); -+ return ; -+ } -+ for (String line : file.asLines()) { -+ result.addAll(doParseLine(line, null)); -+ } -+ } catch (IOException ex) { -+ LOG.log(Level.FINE, null, ex); -+ } -+ }; -+ break; -+ } -+ //fall-through - default: - current.append(c); - escape = false; -@@ -114,7 +165,7 @@ protected final List parseLine(@NonNull final String commandLine) { - } - } - if (current.length() > 0) { -- result.add(current.toString()); -+ handleOption.accept(current.toString()); - } - return Collections.unmodifiableList(result); - } -diff --git a/java/api.java/test/unit/src/org/netbeans/spi/java/queries/CompilerOptionsQueryImplementationTest.java b/java/api.java/test/unit/src/org/netbeans/spi/java/queries/CompilerOptionsQueryImplementationTest.java -new file mode 100644 -index 000000000000..29fff1485d40 ---- /dev/null -+++ b/java/api.java/test/unit/src/org/netbeans/spi/java/queries/CompilerOptionsQueryImplementationTest.java -@@ -0,0 +1,60 @@ -+/* -+ * 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.spi.java.queries; -+ -+import java.io.OutputStream; -+import java.nio.charset.StandardCharsets; -+import java.util.Arrays; -+import org.junit.Test; -+import org.netbeans.junit.NbTestCase; -+import org.netbeans.spi.java.queries.CompilerOptionsQueryImplementation.Result; -+import org.openide.filesystems.FileObject; -+import org.openide.filesystems.FileUtil; -+ -+public class CompilerOptionsQueryImplementationTest extends NbTestCase { -+ -+ public CompilerOptionsQueryImplementationTest(String name) { -+ super(name); -+ } -+ -+ @Test -+ public void testArgumentFiles() throws Exception { -+ FileObject wd = FileUtil.toFileObject(getWorkDir()); -+ FileObject argfile = FileUtil.createData(wd, "argfile"); -+ try (OutputStream out = argfile.getOutputStream()) { -+ out.write("test \t\t \"quoted1\" 'quoted2'\n \t\n @argfile\n".getBytes(StandardCharsets.UTF_8)); -+ } -+ -+ assertEquals(Arrays.asList("test", "quoted1", "quoted2", "@argfile"), -+ Result.doParseLine("@argfile", wd.toURI())); -+ assertEquals(Arrays.asList("@argfile"), -+ Result.doParseLine("@argfile", null)); -+ assertEquals(Arrays.asList("prefix@argfile"), -+ Result.doParseLine("prefix@argfile", wd.toURI())); -+ assertEquals(Arrays.asList("@@"), -+ Result.doParseLine("@@", wd.toURI())); -+ assertEquals(Arrays.asList("@"), -+ Result.doParseLine("@", wd.toURI())); -+ assertEquals(Arrays.asList("test", "quoted1", "quoted2", "@argfile"), -+ Result.doParseLine("@" + FileUtil.toFile(argfile).getAbsolutePath(), wd.toURI())); -+ assertEquals(Arrays.asList("@nonexistent"), -+ Result.doParseLine("@nonexistent", wd.toURI())); -+ } -+ -+} -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 cfa6c4d209aa..65e6cd433a03 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 -@@ -18,6 +18,7 @@ - */ - package org.netbeans.modules.java.file.launcher; - -+import java.net.URI; - import javax.swing.event.ChangeListener; - import org.netbeans.modules.java.file.launcher.queries.MultiSourceRootProvider; - import org.netbeans.modules.java.file.launcher.spi.SingleFileOptionsQueryImplementation; -@@ -94,6 +95,11 @@ public String getOptions() { - return vmOptionsObj != null ? (String) vmOptionsObj : ""; - } - -+ @Override -+ public URI getWorkDirectory() { -+ return root != null ? root.toURI() : source.getParent().toURI(); -+ } -+ - @Override - public void addChangeListener(ChangeListener listener) { - cs.addChangeListener(listener); -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 c3404b86cf8a..8ca36068a060 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 -@@ -20,23 +20,26 @@ - - import java.io.File; - import java.io.IOException; -+import java.net.URI; - import java.util.ArrayList; - import java.util.Arrays; -+import java.util.Collections; - import java.util.List; -+import java.util.concurrent.atomic.AtomicInteger; - import java.util.logging.Level; - import java.util.logging.Logger; -+import javax.swing.event.ChangeEvent; - import javax.swing.event.ChangeListener; - import org.netbeans.api.java.platform.JavaPlatformManager; - import org.netbeans.api.project.FileOwnerQuery; --import org.netbeans.api.project.Project; - import org.netbeans.modules.java.file.launcher.queries.MultiSourceRootProvider; - import org.netbeans.modules.java.file.launcher.spi.SingleFileOptionsQueryImplementation; --import org.netbeans.modules.java.file.launcher.spi.SingleFileOptionsQueryImplementation.Result; - import org.netbeans.spi.java.queries.CompilerOptionsQueryImplementation; - import org.openide.filesystems.FileObject; - import org.openide.filesystems.FileStateInvalidException; - import org.openide.filesystems.FileUtil; - import org.openide.loaders.DataObject; -+import org.openide.util.ChangeSupport; - import org.openide.util.Lookup; - - /** -@@ -127,30 +130,31 @@ public static boolean hasClassSibling(FileObject fo) { - return fo.getParent().getFileObject(fo.getName(), "class") != null; - } - -- public static Result getOptionsFor(FileObject file) { -+ public static ParsedFileOptions getOptionsFor(FileObject file) { - if (MultiSourceRootProvider.DISABLE_MULTI_SOURCE_ROOT) { - return null; - } - - for (SingleFileOptionsQueryImplementation i : Lookup.getDefault().lookupAll(SingleFileOptionsQueryImplementation.class)) { -- Result r = i.optionsFor(file); -+ SingleFileOptionsQueryImplementation.Result r = i.optionsFor(file); - - if (r != null) { -- return r; -+ return new ParsedFileOptions(r); - } - } -+ - return null; - } - -- public static List parseLine(String line) { -- return PARSER.doParse(line); -+ public static List parseLine(String line, URI workingDirectory) { -+ return PARSER.doParse(line, workingDirectory); - } - - private static final LineParser PARSER = new LineParser(); - - private static class LineParser extends CompilerOptionsQueryImplementation.Result { -- public List doParse(String line) { -- return parseLine(line); -+ public List doParse(String line, URI workingDirectory) { -+ return parseLine(line, workingDirectory); - } - - @Override -@@ -165,4 +169,69 @@ public void addChangeListener(ChangeListener listener) {} - public void removeChangeListener(ChangeListener listener) {} - } - -+ public static final class ParsedFileOptions extends CompilerOptionsQueryImplementation.Result implements ChangeListener { -+ -+ private final ChangeSupport cs; -+ private final SingleFileOptionsQueryImplementation.Result delegate; -+ private final AtomicInteger updateCount = new AtomicInteger(0); -+ private List arguments; -+ -+ private ParsedFileOptions(SingleFileOptionsQueryImplementation.Result delegate) { -+ this.cs = new ChangeSupport(this); -+ this.delegate = delegate; -+ this.delegate.addChangeListener(this); -+ } -+ -+ @Override -+ public List getArguments() { -+ int update; -+ synchronized (this) { -+ if (arguments != null) { -+ return arguments; -+ } -+ -+ update = updateCount.get(); -+ } -+ -+ while (true) { -+ List newArguments = -+ Collections.unmodifiableList(parseLine(delegate.getOptions(), -+ delegate.getWorkDirectory())); -+ -+ synchronized (this) { -+ if (update == updateCount.get()) { -+ arguments = newArguments; -+ return newArguments; -+ } -+ -+ //changed in the mean time, try again: -+ update = updateCount.get(); -+ } -+ } -+ } -+ -+ public URI getWorkDirectory() { -+ return delegate.getWorkDirectory(); -+ } -+ -+ @Override -+ public void addChangeListener(ChangeListener listener) { -+ cs.addChangeListener(listener); -+ } -+ -+ @Override -+ public void removeChangeListener(ChangeListener listener) { -+ cs.removeChangeListener(listener); -+ } -+ -+ @Override -+ public void stateChanged(ChangeEvent ce) { -+ synchronized (this) { -+ arguments = null; -+ updateCount.incrementAndGet(); -+ } -+ -+ cs.fireChange(); -+ } -+ } - } -diff --git a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/actions/LaunchProcess.java b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/actions/LaunchProcess.java -index 7646ab59554d..51e6b9182f5a 100644 ---- a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/actions/LaunchProcess.java -+++ b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/actions/LaunchProcess.java -@@ -20,6 +20,7 @@ - - import java.io.File; - import java.io.IOException; -+import java.net.URI; - import java.util.ArrayList; - import java.util.Arrays; - import java.util.List; -@@ -31,6 +32,7 @@ - import org.openide.filesystems.FileObject; - import org.openide.filesystems.FileUtil; - import org.openide.util.BaseUtilities; -+import org.openide.util.Utilities; - - final class LaunchProcess implements Callable { - -@@ -69,12 +71,14 @@ private Process setupProcess(String port) throws InterruptedException { - FileObject java = JavaPlatformManager.getDefault().getDefaultPlatform().findTool("java"); //NOI18N - File javaFile = FileUtil.toFile(java); - String javaPath = javaFile.getAbsolutePath(); -+ URI cwd = SingleSourceFileUtil.getOptionsFor(fileObject).getWorkDirectory(); -+ File workDir = Utilities.toFile(cwd); - - ExplicitProcessParameters paramsFromAttributes = - ExplicitProcessParameters.builder() - .args(readArgumentsFromAttribute(fileObject, SingleSourceFileUtil.FILE_ARGUMENTS)) - .launcherArgs(readArgumentsFromAttribute(fileObject, SingleSourceFileUtil.FILE_VM_OPTIONS)) -- .workingDirectory(FileUtil.toFile(fileObject.getParent())) -+ .workingDirectory(workDir) - .build(); - - ExplicitProcessParameters realParameters = -@@ -97,7 +101,7 @@ private Process setupProcess(String port) throws InterruptedException { - commandsList.add(FileUtil.toFile(fileObject.getParent()).toString()); - commandsList.add(fileObject.getName()); - } else { -- commandsList.add(fileObject.getNameExt()); -+ commandsList.add(FileUtil.toFile(fileObject).getAbsolutePath()); - } - - if (realParameters.getArguments() != null) { -diff --git a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/api/SourceLauncher.java b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/api/SourceLauncher.java -index 8603ed6b899b..c09b89592561 100644 ---- a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/api/SourceLauncher.java -+++ b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/api/SourceLauncher.java -@@ -59,7 +59,7 @@ public static String joinCommandLines(Iterable inputLines) { - Map joinedOptions = new HashMap<>(); - - for (String value : inputLines) { -- List args = SingleSourceFileUtil.parseLine(value); -+ List args = SingleSourceFileUtil.parseLine(value, null); - - for (int i = 0; i < args.size(); i++) { - switch (args.get(i)) { -diff --git a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/LauncherSourceLevelQueryImpl.java b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/LauncherSourceLevelQueryImpl.java -index f84258ecce93..2d0452e3f679 100644 ---- a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/LauncherSourceLevelQueryImpl.java -+++ b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/LauncherSourceLevelQueryImpl.java -@@ -24,7 +24,7 @@ - import javax.swing.event.ChangeListener; - import org.netbeans.api.java.platform.JavaPlatformManager; - import org.netbeans.modules.java.file.launcher.SingleSourceFileUtil; --import org.netbeans.modules.java.file.launcher.spi.SingleFileOptionsQueryImplementation; -+import org.netbeans.modules.java.file.launcher.SingleSourceFileUtil.ParsedFileOptions; - import org.netbeans.spi.java.queries.SourceLevelQueryImplementation2; - import org.openide.filesystems.FileObject; - import org.openide.util.ChangeSupport; -@@ -39,7 +39,7 @@ public class LauncherSourceLevelQueryImpl implements SourceLevelQueryImplementat - - @Override - public Result getSourceLevel(FileObject javaFile) { -- SingleFileOptionsQueryImplementation.Result delegate = SingleSourceFileUtil.getOptionsFor(javaFile); -+ ParsedFileOptions delegate = SingleSourceFileUtil.getOptionsFor(javaFile); - - if (delegate != null) { - return new ResultImpl(delegate); -@@ -54,17 +54,17 @@ private static final class ResultImpl implements ChangeListener, Result { - JavaPlatformManager.getDefault().getDefaultPlatform().getSpecification().getVersion().toString(); - - private final ChangeSupport cs = new ChangeSupport(this); -- private final SingleFileOptionsQueryImplementation.Result delegate; -+ private final ParsedFileOptions delegate; - private String sourceLevel; - -- public ResultImpl(SingleFileOptionsQueryImplementation.Result delegate) { -+ public ResultImpl(ParsedFileOptions delegate) { - this.delegate = delegate; - this.delegate.addChangeListener(this); - updateDelegate(); - } - - private void updateDelegate() { -- List parsed = SingleSourceFileUtil.parseLine(delegate.getOptions()); -+ List parsed = delegate.getArguments(); - String sourceLevel = DEFAULT_SOURCE_LEVEL; - - for (int i = 0; i < parsed.size(); i++) { -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 e5ed4166dc2d..7793d0663d79 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 -@@ -20,6 +20,7 @@ - - import java.beans.PropertyChangeListener; - import java.beans.PropertyChangeSupport; -+import java.io.File; - import java.io.IOException; - import java.net.URL; - import java.util.ArrayList; -@@ -47,7 +48,7 @@ - import org.netbeans.api.project.FileOwnerQuery; - import org.netbeans.api.queries.FileEncodingQuery; - import org.netbeans.modules.java.file.launcher.SingleSourceFileUtil; --import org.netbeans.modules.java.file.launcher.spi.SingleFileOptionsQueryImplementation; -+import org.netbeans.modules.java.file.launcher.SingleSourceFileUtil.ParsedFileOptions; - import org.netbeans.spi.java.classpath.ClassPathFactory; - import org.netbeans.spi.java.classpath.ClassPathImplementation; - -@@ -57,10 +58,16 @@ - import org.netbeans.spi.java.classpath.FilteringPathResourceImplementation; - import org.netbeans.spi.java.classpath.PathResourceImplementation; - import org.netbeans.spi.java.classpath.support.ClassPathSupport; -+import org.openide.filesystems.FileChangeAdapter; -+import org.openide.filesystems.FileEvent; - import org.openide.filesystems.FileObject; -+import org.openide.filesystems.FileRenameEvent; - import org.openide.filesystems.FileUtil; - import org.openide.filesystems.URLMapper; - import org.openide.util.NbBundle.Messages; -+import org.openide.util.RequestProcessor; -+import org.openide.util.RequestProcessor.Task; -+import org.openide.util.Utilities; - import org.openide.util.lookup.ServiceProvider; - import org.openide.util.lookup.ServiceProviders; - -@@ -70,9 +77,15 @@ - }) - public class MultiSourceRootProvider implements ClassPathProvider { - -+ private static final RequestProcessor WORKER = new RequestProcessor(MultiSourceRootProvider.class.getName(), 1, false, false); - private static final Logger LOG = Logger.getLogger(MultiSourceRootProvider.class.getName()); - - public static boolean DISABLE_MULTI_SOURCE_ROOT = Boolean.getBoolean("java.disable.multi.source.root"); -+ public static boolean SYNCHRONOUS_UPDATES = false; -+ -+ private static final Set MODULAR_DIRECTORY_OPTIONS = new HashSet<>(Arrays.asList( -+ "--module-path", "-p" -+ )); - - //TODO: the cache will probably be never cleared, as the ClassPath/value refers to the key(?) - private Map file2SourceCP = new WeakHashMap<>(); -@@ -234,7 +247,7 @@ private ClassPath attributeBasedPath(FileObject file, Map - - synchronized (this) { - return file2ClassPath.computeIfAbsent(file, f -> { -- SingleFileOptionsQueryImplementation.Result delegate = SingleSourceFileUtil.getOptionsFor(f); -+ ParsedFileOptions delegate = SingleSourceFileUtil.getOptionsFor(f); - - if (delegate == null) { - return null; -@@ -253,14 +266,16 @@ private static boolean registerRoot(FileObject root) { - return "true".equals(Bundle.SETTING_AutoRegisterAsRoot()); - } - -- private static final class AttributeBasedClassPathImplementation implements ChangeListener, ClassPathImplementation { -+ private static final class AttributeBasedClassPathImplementation extends FileChangeAdapter implements ChangeListener, ClassPathImplementation { - private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); -- private final SingleFileOptionsQueryImplementation.Result delegate; -+ private final Task updateDelegatesTask = WORKER.create(this::doUpdateDelegates); -+ private final Set directoriesWithListener = new HashSet<>(); -+ private final ParsedFileOptions delegate; - private final Set optionKeys; - private Set currentURLs; - private List delegates = Collections.emptyList(); - -- public AttributeBasedClassPathImplementation(SingleFileOptionsQueryImplementation.Result delegate, String... optionKeys) { -+ public AttributeBasedClassPathImplementation(ParsedFileOptions delegate, String... optionKeys) { - this.delegate = delegate; - this.optionKeys = new HashSet<>(Arrays.asList(optionKeys)); - delegate.addChangeListener(this); -@@ -273,29 +288,84 @@ public void stateChanged(ChangeEvent ce) { - } - - private void updateDelegates() { -+ if (SYNCHRONOUS_UPDATES) { -+ doUpdateDelegates(); -+ } else { -+ updateDelegatesTask.schedule(0); -+ } -+ } -+ -+ private void doUpdateDelegates() { - Set newURLs = new HashSet<>(); - List newDelegates = new ArrayList<>(); -- List parsed = SingleSourceFileUtil.parseLine(delegate.getOptions()); -- -- for (int i = 0; i < parsed.size(); i++) { -- if (optionKeys.contains(parsed.get(i)) && i + 1 < parsed.size()) { -- ClassPathSupport.createClassPath(parsed.get(i + 1)) -- .entries() -- .stream() -- .map(e -> e.getURL()) -- .forEach(u -> { -+ List parsed = delegate.getArguments(); -+ File workDirectory = Utilities.toFile(delegate.getWorkDirectory()); -+ Set toRemoveFSListeners = new HashSet<>(); -+ Set addedFSListeners = new HashSet<>(); -+ -+ synchronized (this) { -+ toRemoveFSListeners.addAll(directoriesWithListener); -+ } -+ -+ for (int i = 0; i < parsed.size() - 1; i++) { -+ String currentOption = parsed.get(i); -+ -+ if (optionKeys.contains(currentOption)) { -+ for (String piece : parsed.get(i + 1).split(File.pathSeparator)) { -+ File pieceFile = new File(piece); -+ -+ if (!pieceFile.isAbsolute()) { -+ pieceFile = new File(workDirectory, piece); -+ } -+ -+ File f = FileUtil.normalizeFile(pieceFile); -+ Iterable expandedPaths; -+ -+ if (MODULAR_DIRECTORY_OPTIONS.contains(currentOption)) { -+ if (!toRemoveFSListeners.remove(f.getAbsolutePath()) && -+ addedFSListeners.add(f.getAbsolutePath())) { -+ FileUtil.addFileChangeListener(this, f); -+ } -+ } -+ -+ if (MODULAR_DIRECTORY_OPTIONS.contains(currentOption) && -+ f.isDirectory() && -+ !new File(f, "module-info.class").exists()) { -+ File[] children = f.listFiles(); -+ -+ if (children != null) { -+ expandedPaths = Arrays.asList(children); -+ } else { -+ expandedPaths = Collections.emptyList(); -+ } -+ } else { -+ expandedPaths = Arrays.asList(f); -+ } -+ -+ for (File expanded : expandedPaths) { -+ URL u = FileUtil.urlForArchiveOrDir(expanded); -+ if (u == null) { -+ throw new IllegalArgumentException("Path entry looks to be invalid: " + piece); // NOI18N -+ } - newURLs.add(u); - newDelegates.add(ClassPathSupport.createResource(u)); -- }); -+ } -+ } - } - } - -+ for (String removeFSListener : toRemoveFSListeners) { -+ FileUtil.removeFileChangeListener(this, new File(removeFSListener)); -+ } -+ - synchronized (this) { - if (Objects.equals(currentURLs, newURLs)) { - return ; - } - this.currentURLs = newURLs; - this.delegates = newDelegates; -+ this.directoriesWithListener.removeAll(toRemoveFSListeners); -+ this.directoriesWithListener.addAll(addedFSListeners); - } - - pcs.firePropertyChange(PROP_RESOURCES, null, null); -@@ -316,6 +386,26 @@ public void removePropertyChangeListener(PropertyChangeListener listener) { - pcs.removePropertyChangeListener(listener); - } - -+ @Override -+ public void fileDataCreated(FileEvent fe) { -+ updateDelegates(); -+ } -+ -+ @Override -+ public void fileDeleted(FileEvent fe) { -+ updateDelegates(); -+ } -+ -+ @Override -+ public void fileFolderCreated(FileEvent fe) { -+ updateDelegates(); -+ } -+ -+ @Override -+ public void fileRenamed(FileRenameEvent fe) { -+ updateDelegates(); -+ } -+ - } - - private static final class RootPathResourceImplementation implements FilteringPathResourceImplementation { -diff --git a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/SingleSourceCompilerOptQueryImpl.java b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/SingleSourceCompilerOptQueryImpl.java -index 28d08d914e16..c6e9b64bb69e 100644 ---- a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/SingleSourceCompilerOptQueryImpl.java -+++ b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/SingleSourceCompilerOptQueryImpl.java -@@ -18,14 +18,9 @@ - */ - package org.netbeans.modules.java.file.launcher.queries; - --import java.util.List; --import javax.swing.event.ChangeEvent; --import javax.swing.event.ChangeListener; - import org.netbeans.modules.java.file.launcher.SingleSourceFileUtil; --import org.netbeans.modules.java.file.launcher.spi.SingleFileOptionsQueryImplementation; - import org.netbeans.spi.java.queries.CompilerOptionsQueryImplementation; - import org.openide.filesystems.FileObject; --import org.openide.util.ChangeSupport; - import org.openide.util.lookup.ServiceProvider; - - /** -@@ -35,46 +30,11 @@ - @ServiceProvider(service = CompilerOptionsQueryImplementation.class, position = 100) - public class SingleSourceCompilerOptQueryImpl implements CompilerOptionsQueryImplementation { - -+ public static final SingleSourceCompilerOptQueryImpl INSTANCE = new SingleSourceCompilerOptQueryImpl(); -+ - @Override - public CompilerOptionsQueryImplementation.Result getOptions(FileObject file) { -- SingleFileOptionsQueryImplementation.Result delegate = SingleSourceFileUtil.getOptionsFor(file); -- -- if (delegate != null) { -- return new ResultImpl(delegate); -- } else { -- return null; -- } -+ return SingleSourceFileUtil.getOptionsFor(file); - } - -- private static final class ResultImpl extends CompilerOptionsQueryImplementation.Result implements ChangeListener { -- -- private final ChangeSupport cs; -- private final SingleFileOptionsQueryImplementation.Result delegate; -- -- ResultImpl(SingleFileOptionsQueryImplementation.Result delegate) { -- this.cs = new ChangeSupport(this); -- this.delegate = delegate; -- this.delegate.addChangeListener(this); -- } -- -- @Override -- public List getArguments() { -- return parseLine(delegate.getOptions()); -- } -- -- @Override -- public void addChangeListener(ChangeListener listener) { -- cs.addChangeListener(listener); -- } -- -- @Override -- public void removeChangeListener(ChangeListener listener) { -- cs.removeChangeListener(listener); -- } -- -- @Override -- public void stateChanged(ChangeEvent ce) { -- cs.fireChange(); -- } -- } - } -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 ed29d610d89d..216706463c3b 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 -@@ -18,7 +18,9 @@ - */ - package org.netbeans.modules.java.file.launcher.spi; - -+import java.net.URI; - import javax.swing.event.ChangeListener; -+import org.netbeans.api.annotations.common.NonNull; - import org.openide.filesystems.FileObject; - - public interface SingleFileOptionsQueryImplementation { -@@ -27,6 +29,9 @@ public interface SingleFileOptionsQueryImplementation { - - public interface Result { - public String getOptions(); -+ public default @NonNull URI getWorkDirectory() { -+ throw new UnsupportedOperationException(); -+ } - public void addChangeListener(ChangeListener l); - public void removeChangeListener(ChangeListener l); - } -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 48083a04b762..61694bb7845a 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 -@@ -18,11 +18,24 @@ - */ - package org.netbeans.modules.java.file.launcher.queries; - -+import java.net.URI; -+import java.util.Arrays; -+import java.util.HashSet; -+import java.util.concurrent.atomic.AtomicInteger; -+import java.util.concurrent.atomic.AtomicReference; -+import javax.swing.event.ChangeListener; - import org.netbeans.api.java.classpath.ClassPath; -+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.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.Lookup; -+import org.openide.util.lookup.Lookups; -+import org.openide.util.lookup.ProxyLookup; - - /** - * -@@ -30,6 +43,8 @@ - */ - public class MultiSourceRootProviderTest extends NbTestCase { - -+ private final TestResultImpl testResult = new TestResultImpl(); -+ - public MultiSourceRootProviderTest(String name) { - super(name); - } -@@ -40,8 +55,6 @@ public void testFindPackage() { - } - - public void testSourcePathFiltering() throws Exception { -- clearWorkDir(); -- - FileObject wd = FileUtil.toFileObject(getWorkDir()); - FileObject validTest = FileUtil.createData(wd, "valid/pack/Test1.java"); - FileObject invalidTest1 = FileUtil.createData(wd, "valid/pack/Test2.java"); -@@ -61,4 +74,243 @@ public void testSourcePathFiltering() throws Exception { - assertNull(provider.findClassPath(invalidTest1, ClassPath.SOURCE)); - assertNull(provider.findClassPath(invalidTest2, ClassPath.SOURCE)); - } -+ -+ public void testRelativePaths() throws Exception { -+ FileObject wd = FileUtil.toFileObject(getWorkDir()); -+ FileObject test = FileUtil.createData(wd, "src/pack/Test1.java"); -+ FileObject libJar = FileUtil.createData(wd, "libs/lib.jar"); -+ FileObject other = FileUtil.createFolder(wd, "other"); -+ FileObject otherLibJar = FileUtil.createData(other, "libs/lib.jar"); -+ FileObject otherLib2Jar = FileUtil.createData(other, "libs/lib2.jar"); -+ -+ TestUtilities.copyStringToFile(test, "package pack;"); -+ -+ testResult.setOptions("--class-path libs/lib.jar"); -+ testResult.setWorkDirectory(wd.toURI()); -+ -+ MultiSourceRootProvider provider = new MultiSourceRootProvider(); -+ ClassPath compileCP = provider.findClassPath(test, ClassPath.COMPILE); -+ AtomicInteger changeCount = new AtomicInteger(); -+ -+ compileCP.addPropertyChangeListener(evt -> { -+ if (ClassPath.PROP_ENTRIES.equals(evt.getPropertyName())) { -+ changeCount.incrementAndGet(); -+ } -+ }); -+ assertEquals(FileUtil.toFile(libJar).getAbsolutePath(), compileCP.toString()); -+ -+ testResult.setWorkDirectory(other.toURI()); -+ -+ assertEquals(1, changeCount.get()); -+ -+ assertEquals(FileUtil.toFile(otherLibJar).getAbsolutePath(), compileCP.toString()); -+ -+ testResult.setOptions("--class-path libs/lib2.jar"); -+ -+ assertEquals(2, changeCount.get()); -+ -+ assertEquals(FileUtil.toFile(otherLib2Jar).getAbsolutePath(), compileCP.toString()); -+ } -+ -+ public void testExpandModularDir() throws Exception { -+ FileObject wd = FileUtil.toFileObject(getWorkDir()); -+ FileObject test = FileUtil.createData(wd, "src/pack/Test1.java"); -+ FileObject libsDir = FileUtil.createFolder(wd, "libs"); -+ FileObject lib1Jar = FileUtil.createData(libsDir, "lib1.jar"); -+ FileObject lib2Jar = FileUtil.createData(libsDir, "lib2.jar"); -+ FileObject lib3Dir = FileUtil.createFolder(libsDir, "lib3"); -+ -+ FileUtil.createData(lib3Dir, "module-info.class"); -+ -+ TestUtilities.copyStringToFile(test, "package pack;"); -+ -+ testResult.setOptions("--module-path " + FileUtil.toFile(libsDir).getAbsolutePath()); -+ testResult.setWorkDirectory(FileUtil.toFileObject(getWorkDir()).toURI()); -+ -+ MultiSourceRootProvider provider = new MultiSourceRootProvider(); -+ ClassPath moduleCP = provider.findClassPath(test, JavaClassPathConstants.MODULE_COMPILE_PATH); -+ ClassPath compileCP = provider.findClassPath(test, ClassPath.COMPILE); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir)), -+ new HashSet<>(Arrays.asList(moduleCP.getRoots()))); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir)), -+ new HashSet<>(Arrays.asList(compileCP.getRoots()))); -+ -+ FileObject lib4Jar = FileUtil.createData(libsDir, "lib4.jar"); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir, -+ FileUtil.getArchiveRoot(lib4Jar))), -+ new HashSet<>(Arrays.asList(moduleCP.getRoots()))); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir, -+ FileUtil.getArchiveRoot(lib4Jar))), -+ new HashSet<>(Arrays.asList(compileCP.getRoots()))); -+ -+ testResult.setOptions("--module-path " + FileUtil.toFile(lib1Jar).getAbsolutePath()); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar))), -+ new HashSet<>(Arrays.asList(moduleCP.getRoots()))); -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar))), -+ new HashSet<>(Arrays.asList(compileCP.getRoots()))); -+ -+ testResult.setOptions("--module-path " + FileUtil.toFile(lib3Dir).getAbsolutePath()); -+ -+ assertEquals(new HashSet<>(Arrays.asList(lib3Dir)), -+ new HashSet<>(Arrays.asList(moduleCP.getRoots()))); -+ assertEquals(new HashSet<>(Arrays.asList(lib3Dir)), -+ new HashSet<>(Arrays.asList(compileCP.getRoots()))); -+ -+ testResult.setOptions("--module-path " + FileUtil.toFile(libsDir).getAbsolutePath()); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir, -+ FileUtil.getArchiveRoot(lib4Jar))), -+ new HashSet<>(Arrays.asList(moduleCP.getRoots()))); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir, -+ FileUtil.getArchiveRoot(lib4Jar))), -+ new HashSet<>(Arrays.asList(compileCP.getRoots()))); -+ -+ FileObject lib5Dir = FileUtil.createFolder(libsDir, "lib5Dir"); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir, -+ FileUtil.getArchiveRoot(lib4Jar), -+ lib5Dir)), -+ new HashSet<>(Arrays.asList(moduleCP.getRoots()))); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir, -+ FileUtil.getArchiveRoot(lib4Jar), -+ lib5Dir)), -+ new HashSet<>(Arrays.asList(compileCP.getRoots()))); -+ -+ lib5Dir.delete(); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir, -+ FileUtil.getArchiveRoot(lib4Jar))), -+ new HashSet<>(Arrays.asList(moduleCP.getRoots()))); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir, -+ FileUtil.getArchiveRoot(lib4Jar))), -+ new HashSet<>(Arrays.asList(compileCP.getRoots()))); -+ -+ lib4Jar.delete(); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir)), -+ new HashSet<>(Arrays.asList(moduleCP.getRoots()))); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir)), -+ new HashSet<>(Arrays.asList(compileCP.getRoots()))); -+ -+ FileUtil.createData(libsDir, "module-info.class"); -+ -+ assertEquals(new HashSet<>(Arrays.asList(libsDir)), -+ new HashSet<>(Arrays.asList(moduleCP.getRoots()))); -+ assertEquals(new HashSet<>(Arrays.asList(libsDir)), -+ new HashSet<>(Arrays.asList(compileCP.getRoots()))); -+ } -+ -+ public void testBrokenOptions() throws Exception { -+ FileObject wd = FileUtil.toFileObject(getWorkDir()); -+ FileObject test = FileUtil.createData(wd, "src/pack/Test1.java"); -+ -+ testResult.setOptions("--module-path"); -+ testResult.setWorkDirectory(FileUtil.toFileObject(getWorkDir()).toURI()); -+ -+ MultiSourceRootProvider provider = new MultiSourceRootProvider(); -+ -+ provider.findClassPath(test, JavaClassPathConstants.MODULE_COMPILE_PATH); -+ } -+ -+ @Override -+ protected void setUp() throws Exception { -+ super.setUp(); -+ clearWorkDir(); -+ } -+ -+ @Override -+ protected void runTest() throws Throwable { -+ SingleFileOptionsQueryImplementation queryImpl = file -> testResult; -+ ProxyLookup newQueryLookup = new ProxyLookup(Lookups.fixed(queryImpl), -+ Lookups.exclude(Lookup.getDefault(), -+ SingleFileOptionsQueryImplementation.class)); -+ Lookups.executeWith(newQueryLookup, () -> { -+ try { -+ super.runTest(); -+ } catch (Error err) { -+ throw err; -+ } catch (RuntimeException ex) { -+ throw ex; -+ } catch (Throwable ex) { -+ throw new IllegalStateException(ex); -+ } -+ }); -+ } -+ -+ 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<>(); -+ -+ public TestResultImpl() { -+ } -+ -+ @Override -+ public String getOptions() { -+ return options.get(); -+ } -+ -+ public void setOptions(String options) { -+ this.options.set(options); -+ cs.fireChange(); -+ } -+ -+ @Override -+ public URI getWorkDirectory() { -+ return workdir.get(); -+ } -+ -+ public void setWorkDirectory(URI workdir) { -+ this.workdir.set(workdir); -+ cs.fireChange(); -+ } -+ -+ @Override -+ public void addChangeListener(ChangeListener l) { -+ cs.addChangeListener(l); -+ } -+ -+ @Override -+ public void removeChangeListener(ChangeListener l) { -+ cs.removeChangeListener(l); -+ } -+ } -+ -+ static { -+ MultiSourceRootProvider.SYNCHRONOUS_UPDATES = true; -+ } - } -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java -index ea7db731562e..06411f66497d 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java -@@ -1108,6 +1108,7 @@ protected LanguageClient client() { - sessionServices.add(new WorkspaceUIContext(client)); - sessionServices.add(treeService.getNodeRegistry()); - sessionServices.add(inputService.getRegistry()); -+ sessionServices.add(workspaceService.getWorkspace()); - ((LanguageClientAware) getTextDocumentService()).connect(client); - ((LanguageClientAware) getWorkspaceService()).connect(client); - ((LanguageClientAware) treeService).connect(client); -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Workspace.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Workspace.java -new file mode 100644 -index 000000000000..0ed7ae25386a ---- /dev/null -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Workspace.java -@@ -0,0 +1,29 @@ -+/* -+ * 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.lsp.server.protocol; -+ -+import java.util.List; -+import org.openide.filesystems.FileObject; -+ -+/** -+ * One workspace opened in the UI. -+ */ -+public interface Workspace { -+ public List getClientWorkspaceFolders(); -+} -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java -index 7060c264a420..9cb5bde33036 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java -@@ -169,6 +169,12 @@ public final class WorkspaceServiceImpl implements WorkspaceService, LanguageCli - - private final Gson gson = new Gson(); - private final LspServerState server; -+ private final Workspace workspace = new Workspace() { -+ @Override -+ public List getClientWorkspaceFolders() { -+ return WorkspaceServiceImpl.this.getClientWorkspaceFolders(); -+ } -+ }; - private NbCodeLanguageClient client; - - /** -@@ -1342,15 +1348,18 @@ public void didChangeConfiguration(DidChangeConfigurationParams params) { - String altConfigPrefix = fullAltConfigPrefix.substring(0, fullAltConfigPrefix.length() - 1); - boolean modified = false; - String newVMOptions = ""; -+ String newWorkingDirectory = null; - JsonObject javaPlus = ((JsonObject) params.getSettings()).getAsJsonObject(altConfigPrefix); - if (javaPlus != null) { - JsonObject runConfig = javaPlus.getAsJsonObject("runConfig"); - if (runConfig != null) { - newVMOptions = runConfig.getAsJsonPrimitive("vmOptions").getAsString(); -+ JsonPrimitive cwd = runConfig.getAsJsonPrimitive("cwd"); -+ newWorkingDirectory = cwd != null ? cwd.getAsString() : null; - } - } - for (SingleFileOptionsQueryImpl query : Lookup.getDefault().lookupAll(SingleFileOptionsQueryImpl.class)) { -- modified |= query.setConfiguration(client, newVMOptions); -+ modified |= query.setConfiguration(workspace, newVMOptions, newWorkingDirectory); - } - if (modified) { - ((TextDocumentServiceImpl)server.getTextDocumentService()).reRunDiagnostics(); -@@ -1484,6 +1493,10 @@ public void connect(LanguageClient client) { - this.client = (NbCodeLanguageClient)client; - } - -+ public Workspace getWorkspace() { -+ return workspace; -+ } -+ - private static final class CommandProgress extends ActionProgress { - - private final CompletableFuture commandFinished = new CompletableFuture<>(); -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 c2e70cc1027c..da4898786f11 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 -@@ -18,59 +18,115 @@ - */ - package org.netbeans.modules.java.lsp.server.singlesourcefile; - --import java.util.ArrayList; --import java.util.List; -+import java.io.File; -+import java.net.URI; -+import java.util.HashMap; - import java.util.Map; - import java.util.Objects; -+import java.util.Set; - import java.util.WeakHashMap; -+import javax.swing.event.ChangeEvent; - import javax.swing.event.ChangeListener; - import org.netbeans.api.project.FileOwnerQuery; - import org.netbeans.api.project.Project; - import org.netbeans.modules.java.file.launcher.api.SourceLauncher; - import org.netbeans.modules.java.file.launcher.spi.SingleFileOptionsQueryImplementation; --import org.netbeans.modules.java.lsp.server.protocol.NbCodeLanguageClient; -+import org.netbeans.modules.java.lsp.server.protocol.Workspace; -+import org.openide.filesystems.FileChangeAdapter; -+import org.openide.filesystems.FileEvent; - import org.openide.filesystems.FileObject; -+import org.openide.filesystems.FileUtil; - import org.openide.util.ChangeSupport; - import org.openide.util.Lookup; -+import org.openide.util.Parameters; - - public abstract class SingleFileOptionsQueryImpl implements SingleFileOptionsQueryImplementation { - -- private final Map client2Options = new WeakHashMap<>(); -- private final GlobalResultImpl globalOptions = new GlobalResultImpl(); -+ private final Map workspace2Settings = new WeakHashMap<>(); -+ private final Map> workspace2Folder2Options = new WeakHashMap<>(); - - @Override - public Result optionsFor(FileObject file) { - if (isSingleSourceFile(file)) { -- NbCodeLanguageClient client = Lookup.getDefault().lookup(NbCodeLanguageClient.class); -+ Workspace workspace = Lookup.getDefault().lookup(Workspace.class); -+ FileObject workspaceFolder = workspace != null ? findWorkspaceFolder(workspace, file) : null; - -- if (client != null) { -- return getResult(client); -+ if (workspaceFolder != null) { -+ return getResult(workspace, workspaceFolder); - } else { -- return globalOptions; -+ Set workspaces; -+ -+ synchronized (this) { -+ workspaces = workspace2Settings.keySet(); -+ } -+ -+ for (Workspace w : workspaces) { -+ FileObject folder = findWorkspaceFolder(w, file); -+ if (folder != null) { -+ return getResult(w, folder); -+ } -+ } -+ -+ return null; -+ } -+ } -+ return null; -+ } -+ -+ private synchronized Result getResult(Workspace workspace, FileObject workspaceFolder) { -+ Map folder2Result = -+ workspace2Folder2Options.computeIfAbsent(workspace, w -> new HashMap<>()); -+ return folder2Result.computeIfAbsent(workspaceFolder, f -> new ResultImpl(folder2Result, -+ workspaceFolder, -+ getWorkspaceSettings(workspace))); -+ } -+ -+ static FileObject findWorkspaceFolder(Workspace workspace, FileObject file) { -+ for (FileObject workspaceFolder : workspace.getClientWorkspaceFolders()) { -+ if (FileUtil.isParentOf(workspaceFolder, file) || workspaceFolder == file) { -+ return workspaceFolder; - } - } -+ -+ //in case file is a source root, and the workspace folder is nested inside the root: -+ for (FileObject workspaceFolder : workspace.getClientWorkspaceFolders()) { -+ if (FileUtil.isParentOf(file, workspaceFolder)) { -+ return workspaceFolder; -+ } -+ } -+ - return null; - } - -- private static final class ResultImpl implements Result { -+ private static final class ResultImpl extends FileChangeAdapter implements Result, ChangeListener { - - private final ChangeSupport cs = new ChangeSupport(this); -- private String options = ""; -+ private final Map workspaceFolders2Results; -+ private final FileObject workspaceFolder; -+ private final WorkspaceSettings workspaceSettings; -+ -+ public ResultImpl(Map workspaceFolders2Results, -+ FileObject workspaceFolder, -+ WorkspaceSettings workspaceSettings) { -+ this.workspaceFolders2Results = workspaceFolders2Results; -+ this.workspaceFolder = workspaceFolder; -+ this.workspaceSettings = workspaceSettings; -+ -+ workspaceSettings.addChangeListener(this); -+ workspaceFolder.addFileChangeListener(this); -+ } - - @Override -- public synchronized String getOptions() { -- return options; -+ public String getOptions() { -+ return workspaceSettings.getOptions(); - } - -- public boolean setOptions(String options) { -- synchronized (this) { -- if (Objects.equals(this.options, options)) { -- return false; -- } -- this.options = options; -- } -- cs.fireChange(); -- return true; -+ @Override -+ public URI getWorkDirectory() { -+ String cwd = workspaceSettings.getWorkDirectory(); -+ FileObject workDir = cwd != null ? FileUtil.toFileObject(new File(cwd)) -+ : workspaceFolder; -+ return workDir.toURI(); - } - - @Override -@@ -83,48 +139,69 @@ public void removeChangeListener(ChangeListener l) { - cs.removeChangeListener(l); - } - -+ @Override -+ public void stateChanged(ChangeEvent ce) { -+ cs.fireChange(); -+ } -+ -+ @Override -+ public void fileDeleted(FileEvent fe) { -+ workspaceFolders2Results.remove(workspaceFolder); -+ } -+ - } - -- private final class GlobalResultImpl implements Result { -+ private final class WorkspaceSettings { - - private final ChangeSupport cs = new ChangeSupport(this); - -- @Override -- public String getOptions() { -- List options = new ArrayList<>(); -+ private String options; -+ private String workdirDirectory; - -- synchronized (SingleFileOptionsQueryImpl.this) { -- for (ResultImpl r : client2Options.values()) { -- options.add(r.getOptions()); -+ public synchronized String getOptions() { -+ return options; -+ } -+ -+ public synchronized String getWorkDirectory() { -+ return workdirDirectory; -+ } -+ -+ public boolean setOptions(String options, String workingDirectory) { -+ boolean modified = false; -+ synchronized (this) { -+ if (!Objects.equals(this.options, options)) { -+ this.options = options; -+ modified = true; -+ } -+ if (!Objects.equals(this.workdirDirectory, workingDirectory)) { -+ this.workdirDirectory = workingDirectory; -+ modified = true; - } - } -- -- return SourceLauncher.joinCommandLines(options); -+ if (modified) { -+ cs.fireChange(); -+ } -+ return modified; - } - -- @Override - public void addChangeListener(ChangeListener l) { - cs.addChangeListener(l); - } - -- @Override - public void removeChangeListener(ChangeListener l) { - cs.removeChangeListener(l); - } - - } - -- public boolean setConfiguration(NbCodeLanguageClient client, String vmOptions) { -- if (getResult(client).setOptions(vmOptions)) { -- globalOptions.cs.fireChange(); -- return true; -- } -- return false; -+ public boolean setConfiguration(Workspace workspace, String vmOptions, String workDirectory) { -+ return getWorkspaceSettings(workspace).setOptions(vmOptions, workDirectory); - } - -- private synchronized ResultImpl getResult(NbCodeLanguageClient client) { -- return client2Options.computeIfAbsent(client, cl -> { -- return new ResultImpl(); -+ private synchronized WorkspaceSettings getWorkspaceSettings(Workspace workspace) { -+ Parameters.notNull("workspace", workspace); -+ return workspace2Settings.computeIfAbsent(workspace, w -> { -+ return new WorkspaceSettings(); - }); - } - -diff --git a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/singlesourcefile/SingleFileOptionsQueryImplTest.java b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/singlesourcefile/SingleFileOptionsQueryImplTest.java -new file mode 100644 -index 000000000000..4c6d3c812f3a ---- /dev/null -+++ b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/singlesourcefile/SingleFileOptionsQueryImplTest.java -@@ -0,0 +1,169 @@ -+/* -+ * 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.lsp.server.singlesourcefile; -+ -+import java.util.Arrays; -+import java.util.List; -+import java.util.concurrent.atomic.AtomicInteger; -+import org.netbeans.junit.NbTestCase; -+import org.netbeans.modules.java.lsp.server.protocol.Workspace; -+import org.openide.filesystems.FileObject; -+import org.openide.filesystems.FileUtil; -+import org.openide.util.Lookup; -+import org.openide.util.lookup.Lookups; -+import org.openide.util.lookup.ProxyLookup; -+ -+public class SingleFileOptionsQueryImplTest extends NbTestCase { -+ -+ public SingleFileOptionsQueryImplTest(String name) { -+ super(name); -+ } -+ -+ @Override -+ protected void setUp() throws Exception { -+ super.setUp(); -+ -+ clearWorkDir(); -+ } -+ -+ public void testFindWorkspaceFolder() throws Exception { -+ FileObject wd = FileUtil.toFileObject(getWorkDir()); -+ FileObject workspace1 = FileUtil.createFolder(wd, "workspace1"); -+ FileObject source1 = FileUtil.createData(workspace1, "test/Test.java"); -+ FileObject prjRoot = FileUtil.createFolder(wd, "prjRoot"); -+ FileObject workspace2 = FileUtil.createFolder(prjRoot, "test2"); -+ FileObject source2 = FileUtil.createData(workspace2, "Test.java"); -+ -+ Workspace workspace = new WorkspaceImpl(Arrays.asList(workspace1, workspace2)); -+ -+ assertEquals(workspace1, SingleFileOptionsQueryImpl.findWorkspaceFolder(workspace, source1)); -+ assertEquals(workspace1, SingleFileOptionsQueryImpl.findWorkspaceFolder(workspace, source1.getParent())); -+ assertEquals(workspace1, SingleFileOptionsQueryImpl.findWorkspaceFolder(workspace, workspace1)); -+ assertEquals(workspace2, SingleFileOptionsQueryImpl.findWorkspaceFolder(workspace, source2)); -+ assertEquals(workspace2, SingleFileOptionsQueryImpl.findWorkspaceFolder(workspace, source2.getParent())); -+ assertEquals(workspace2, SingleFileOptionsQueryImpl.findWorkspaceFolder(workspace, source2.getParent().getParent())); -+ } -+ -+ public void testWorkspaceOptions() throws Exception { -+ FileObject wd = FileUtil.toFileObject(getWorkDir()); -+ FileObject workspace1 = FileUtil.createFolder(wd, "workspace1"); -+ FileObject source1 = FileUtil.createData(workspace1, "test1/Test.java"); -+ FileObject workspace2 = FileUtil.createFolder(wd, "workspace2"); -+ FileObject source2 = FileUtil.createData(workspace2, "test2/Test.java"); -+ FileObject source3 = FileUtil.createData(wd, "test3/Test.java"); -+ -+ SingleFileOptionsQueryImpl query = new SingleFileOptionsQueryImpl() {}; -+ Workspace workspace = new WorkspaceImpl(Arrays.asList(workspace1, workspace2)); -+ -+ query.setConfiguration(workspace, "-Dtest=test", null); -+ -+ Lookups.executeWith(new ProxyLookup(Lookups.fixed(workspace), Lookup.getDefault()), () -> { -+ assertEquals("-Dtest=test", query.optionsFor(source1).getOptions()); -+ assertEquals(workspace1.toURI(), query.optionsFor(source1).getWorkDirectory()); -+ assertEquals("-Dtest=test", query.optionsFor(source1.getParent()).getOptions()); -+ assertEquals(workspace1.toURI(), query.optionsFor(source1.getParent()).getWorkDirectory()); -+ -+ assertEquals("-Dtest=test", query.optionsFor(source2).getOptions()); -+ assertEquals(workspace2.toURI(), query.optionsFor(source2).getWorkDirectory()); -+ assertEquals("-Dtest=test", query.optionsFor(source2.getParent()).getOptions()); -+ assertEquals(workspace2.toURI(), query.optionsFor(source2.getParent()).getWorkDirectory()); -+ -+ AtomicInteger changeCount = new AtomicInteger(); -+ -+ query.optionsFor(source1).addChangeListener(evt -> changeCount.incrementAndGet()); -+ -+ query.setConfiguration(workspace, "-Dtest=test", null); -+ -+ assertEquals(0, changeCount.get()); -+ -+ FileObject newWD = source1.getParent(); -+ -+ query.setConfiguration(workspace, "-Dtest=test", FileUtil.toFile(newWD).getAbsolutePath()); -+ -+ assertEquals(1, changeCount.get()); -+ -+ assertEquals("-Dtest=test", query.optionsFor(source1).getOptions()); -+ assertEquals(newWD.toURI(), query.optionsFor(source1).getWorkDirectory()); -+ assertEquals("-Dtest=test", query.optionsFor(source1.getParent()).getOptions()); -+ assertEquals(newWD.toURI(), query.optionsFor(source1.getParent()).getWorkDirectory()); -+ -+ assertEquals("-Dtest=test", query.optionsFor(source2).getOptions()); -+ assertEquals(newWD.toURI(), query.optionsFor(source2).getWorkDirectory()); -+ assertEquals("-Dtest=test", query.optionsFor(source2.getParent()).getOptions()); -+ assertEquals(newWD.toURI(), query.optionsFor(source2.getParent()).getWorkDirectory()); -+ -+ query.setConfiguration(workspace, "-Dtest=test2", FileUtil.toFile(newWD).getAbsolutePath()); -+ -+ assertEquals(2, changeCount.get()); -+ -+ assertEquals("-Dtest=test2", query.optionsFor(source1).getOptions()); -+ assertEquals(newWD.toURI(), query.optionsFor(source1).getWorkDirectory()); -+ assertEquals("-Dtest=test2", query.optionsFor(source1.getParent()).getOptions()); -+ assertEquals(newWD.toURI(), query.optionsFor(source1.getParent()).getWorkDirectory()); -+ -+ assertEquals("-Dtest=test2", query.optionsFor(source2).getOptions()); -+ assertEquals(newWD.toURI(), query.optionsFor(source2).getWorkDirectory()); -+ assertEquals("-Dtest=test2", query.optionsFor(source2.getParent()).getOptions()); -+ assertEquals(newWD.toURI(), query.optionsFor(source2.getParent()).getWorkDirectory()); -+ -+ query.setConfiguration(workspace, "-Dtest=test2", null); -+ -+ assertEquals(3, changeCount.get()); -+ -+ assertEquals("-Dtest=test2", query.optionsFor(source1).getOptions()); -+ assertEquals(workspace1.toURI(), query.optionsFor(source1).getWorkDirectory()); -+ assertEquals("-Dtest=test2", query.optionsFor(source1.getParent()).getOptions()); -+ assertEquals(workspace1.toURI(), query.optionsFor(source1.getParent()).getWorkDirectory()); -+ -+ assertEquals("-Dtest=test2", query.optionsFor(source2).getOptions()); -+ assertEquals(workspace2.toURI(), query.optionsFor(source2).getWorkDirectory()); -+ assertEquals("-Dtest=test2", query.optionsFor(source2.getParent()).getOptions()); -+ assertEquals(workspace2.toURI(), query.optionsFor(source2.getParent()).getWorkDirectory()); -+ }); -+ -+ -+ //with no workspace context: -+ assertEquals("-Dtest=test2", query.optionsFor(source1).getOptions()); -+ assertEquals(workspace1.toURI(), query.optionsFor(source1).getWorkDirectory()); -+ assertEquals("-Dtest=test2", query.optionsFor(source1.getParent()).getOptions()); -+ assertEquals(workspace1.toURI(), query.optionsFor(source1.getParent()).getWorkDirectory()); -+ -+ assertEquals("-Dtest=test2", query.optionsFor(source2).getOptions()); -+ assertEquals(workspace2.toURI(), query.optionsFor(source2).getWorkDirectory()); -+ assertEquals("-Dtest=test2", query.optionsFor(source2.getParent()).getOptions()); -+ assertEquals(workspace2.toURI(), query.optionsFor(source2.getParent()).getWorkDirectory()); -+ -+ assertNull(query.optionsFor(source3)); -+ assertNull(query.optionsFor(source3.getParent())); -+ } -+ -+ private static final class WorkspaceImpl implements Workspace { -+ private final List workspaceFolders; -+ -+ public WorkspaceImpl(List workspaceFolders) { -+ this.workspaceFolders = workspaceFolders; -+ } -+ -+ @Override -+ public List getClientWorkspaceFolders() { -+ return workspaceFolders; -+ } -+ -+ } -+} -diff --git a/java/java.lsp.server/vscode/src/extension.ts b/java/java.lsp.server/vscode/src/extension.ts -index a4fdf4b1e59d..426d2e457e38 100644 ---- a/java/java.lsp.server/vscode/src/extension.ts -+++ b/java/java.lsp.server/vscode/src/extension.ts -@@ -1071,7 +1071,8 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex - 'netbeans.hints', - 'netbeans.format', - 'netbeans.java.imports', -- 'java+.runConfig.vmOptions' -+ 'java+.runConfig.vmOptions', -+ 'java+.runConfig.cwd' - ], - fileEvents: [ - workspace.createFileSystemWatcher('**/*.java') -diff --git a/java/java.source.base/src/org/netbeans/modules/java/source/indexing/APTUtils.java b/java/java.source.base/src/org/netbeans/modules/java/source/indexing/APTUtils.java -index 2aa6f9657cb3..54ec330cbe36 100644 ---- a/java/java.source.base/src/org/netbeans/modules/java/source/indexing/APTUtils.java -+++ b/java/java.source.base/src/org/netbeans/modules/java/source/indexing/APTUtils.java -@@ -135,9 +135,6 @@ private APTUtils(@NonNull final FileObject root) { - this.root = root; - bootPath = ClassPath.getClassPath(root, ClassPath.BOOT); - compilePath = ClassPath.getClassPath(root, ClassPath.COMPILE); -- if (compilePath != null) { -- compilePath.addPropertyChangeListener(this); -- } - processorPath = new AtomicReference<>(ClassPath.getClassPath(root, JavaClassPathConstants.PROCESSOR_PATH)); - processorModulePath = new AtomicReference<>(ClassPath.getClassPath(root, JavaClassPathConstants.MODULE_PROCESSOR_PATH)); - aptOptions = AnnotationProcessingQuery.getAnnotationProcessingOptions(root); -@@ -150,6 +147,9 @@ private APTUtils(@NonNull final FileObject root) { - false); - }); - usedRoots = new UsedRoots(root.toURL()); -+ if (compilePath != null) { -+ compilePath.addPropertyChangeListener(this); -+ } - } - - @CheckForNull diff --git a/patches/7390.diff b/patches/7390.diff deleted file mode 100644 index c45e6dfe..00000000 --- a/patches/7390.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/java/java.project.ui/src/org/netbeans/modules/java/project/ui/resources/Exception.java.template b/java/java.project.ui/src/org/netbeans/modules/java/project/ui/resources/Exception.java.template -index ab05caa21d2e..bdccca88f8e3 100644 ---- a/java/java.project.ui/src/org/netbeans/modules/java/project/ui/resources/Exception.java.template -+++ b/java/java.project.ui/src/org/netbeans/modules/java/project/ui/resources/Exception.java.template -@@ -33,7 +33,7 @@ import ${interface}; - - <#assign implementation = "${implementation}"?remove_ending(", ")> - --public class ${name}<#if extension?? && extension != ""> extends ${extension}<#if implementation?? && implementation != ""> implements ${implementation} { -+public class ${name}<#if extension?? && extension != ""> extends ${extension}<#else> extends Exception<#if implementation?? && implementation != ""> implements ${implementation} { - - /** - * Creates a new instance of ${name} without detail message. diff --git a/patches/7491-preliminary.diff b/patches/7491.diff similarity index 76% rename from patches/7491-preliminary.diff rename to patches/7491.diff index ca336235..a0a90593 100644 --- a/patches/7491-preliminary.diff +++ b/patches/7491.diff @@ -1,8 +1,8 @@ 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..95ae5235c6 100644 +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 @@ abstract class BaseTask extends UserTask { +@@ -176,6 +176,7 @@ TokenSequence nextNonWhitespaceToken(TokenSequence ts) case LINE_COMMENT: case BLOCK_COMMENT: case JAVADOC_COMMENT: @@ -10,7 +10,7 @@ index 78fdd4caa3..95ae5235c6 100644 break; default: return ts; -@@ -206,6 +207,7 @@ abstract class BaseTask extends UserTask { +@@ -206,6 +207,7 @@ TokenSequence previousNonWhitespaceToken(TokenSequence case LINE_COMMENT: case BLOCK_COMMENT: case JAVADOC_COMMENT: @@ -18,7 +18,7 @@ index 78fdd4caa3..95ae5235c6 100644 break; default: return ts; -@@ -427,6 +429,7 @@ abstract class BaseTask extends UserTask { +@@ -427,6 +429,7 @@ private Env getEnvImpl(CompilationController controller, TreePath orig, TreePath case LINE_COMMENT: case BLOCK_COMMENT: case JAVADOC_COMMENT: @@ -26,7 +26,7 @@ index 78fdd4caa3..95ae5235c6 100644 break; case ARROW: scope = controller.getTrees().getScope(blockPath); -@@ -456,6 +459,7 @@ abstract class BaseTask extends UserTask { +@@ -456,6 +459,7 @@ private Env getEnvImpl(CompilationController controller, TreePath orig, TreePath case LINE_COMMENT: case BLOCK_COMMENT: case JAVADOC_COMMENT: @@ -35,10 +35,10 @@ index 78fdd4caa3..95ae5235c6 100644 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 9a2d8de483..2cf8691813 100644 +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 -@@ -1611,6 +1611,7 @@ public final class JavaCompletionTask extends BaseTask { +@@ -1608,6 +1608,7 @@ private void insideMemberSelect(Env env) throws IOException { case LINE_COMMENT: case BLOCK_COMMENT: case JAVADOC_COMMENT: @@ -46,11 +46,24 @@ index 9a2d8de483..2cf8691813 100644 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 a0682653d7..c6f54e77b8 100644 +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 @@ import org.netbeans.api.lexer.TokenSequence; +@@ -51,7 +51,7 @@ */ public final class JavadocCompletionUtils { @@ -68,7 +81,7 @@ index a0682653d7..c6f54e77b8 100644 private static final Logger LOGGER = Logger.getLogger(JavadocCompletionUtils.class.getName()); /** -@@ -196,6 +196,7 @@ public final class JavadocCompletionUtils { +@@ -196,6 +196,7 @@ public static TokenSequence findJavadocTokenSequence(Compilation break; } case JAVADOC_COMMENT: @@ -76,7 +89,7 @@ index a0682653d7..c6f54e77b8 100644 if (token.partType() == PartType.COMPLETE) { return javac.getElements().getDocComment(e) == null ? null : s.embedded(JavadocTokenId.language()); -@@ -246,36 +247,39 @@ public final class JavadocCompletionUtils { +@@ -246,36 +247,39 @@ static boolean isInsideIndent(Token token, int offset) { /** * Is javadoc line break? @@ -114,7 +127,7 @@ index a0682653d7..c6f54e77b8 100644 && JAVADOC_LINE_BREAK.matcher(text).find() - && (pos == token.length() || !isInsideIndent(token, pos)); + && (pos == token.length() || !isInsideIndent(token, pos)) -+ ) || ts.index() == 0; ++ ); return result; } catch (IndexOutOfBoundsException e) { throw (IndexOutOfBoundsException) new IndexOutOfBoundsException("pos: " + pos + ", token.length: " + token.length() + ", token text: " + token.text()).initCause(e); @@ -125,7 +138,7 @@ index a0682653d7..c6f54e77b8 100644 public static boolean isWhiteSpace(CharSequence text) { return text != null && text.length() > 0 && !JAVADOC_WHITE_SPACE.matcher(text).find(); } -@@ -437,7 +441,8 @@ public final class JavadocCompletionUtils { +@@ -437,7 +441,8 @@ private static boolean movedToJavadocToken(TokenSequence ts, int of return false; } @@ -135,7 +148,7 @@ index a0682653d7..c6f54e77b8 100644 return false; } -@@ -456,6 +461,11 @@ public final class JavadocCompletionUtils { +@@ -456,6 +461,11 @@ private static boolean isEmptyJavadoc(Token token, int offset) { // check special case /**|*/ return offset == 3 && "/***/".contentEquals(text); //NOI18N } @@ -148,10 +161,10 @@ index a0682653d7..c6f54e77b8 100644 } 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 07ccd639c0..2d91d7065f 100644 +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 final class JavadocImports { +@@ -641,7 +641,7 @@ public static boolean isInsideReference(TokenSequence jdts, int } case OTHER_TEXT: isBeforeWS |= JavadocCompletionUtils.isWhiteSpace(jdt); @@ -160,7 +173,7 @@ index 07ccd639c0..2d91d7065f 100644 if (isBeforeWS) { continue; } else { -@@ -690,7 +690,9 @@ public final class JavadocImports { +@@ -690,7 +690,9 @@ private static TokenSequence getJavadocTS(CompilationInfo javac, TokenSequence javadoc = null; TokenSequence ts = SourceUtils.getJavaTokenSequence(javac.getTokenHierarchy(), start); @@ -171,7 +184,7 @@ index 07ccd639c0..2d91d7065f 100644 javadoc = ts.embedded(JavadocTokenId.language()); } -@@ -893,14 +895,14 @@ public final class JavadocImports { +@@ -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) @@ -189,10 +202,10 @@ index 07ccd639c0..2d91d7065f 100644 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 add1ca7dc2..33926dc0b4 100644 +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 class JavadocCompletionUtilsTest extends JavadocTestSupport { +@@ -270,32 +270,32 @@ public void testIsLineBreak() throws Exception { TokenSequence jdts = JavadocCompletionUtils.findJavadocTokenSequence(info, offset); assertTrue(jdts.moveNext()); assertTrue(insertPointer(code, offset), @@ -231,7 +244,7 @@ index add1ca7dc2..33926dc0b4 100644 } public void testIsLineBreak2() throws Exception { -@@ -319,10 +319,10 @@ public class JavadocCompletionUtilsTest extends JavadocTestSupport { +@@ -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()), @@ -245,43 +258,45 @@ index add1ca7dc2..33926dc0b4 100644 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 e64ec4698b..88443c53d7 100644 +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,122 @@ public class JavadocImportsTest extends JavadocTestSupport { +@@ -207,6 +207,124 @@ public void testComputeReferencedElements() throws Exception { assertEquals(exp, sortedResult); } + public void testComputeReferencedElementsMarkdown() throws Exception { + String code = -+ "package p;\n" + -+ "import java.io.IOException;\n" + -+ "import java.util.Collections;\n" + -+ "import java.util.List;\n" + -+ "class C {\n" + -+ " ///link1 {@link Runnable}\n" + -+ " ///link3 {@linkplain Collections#binarySearch(java.util.List, Object) search}\n" + -+ " ///{@link java. uncomplete reference}\n" + -+// " ///unclosed link {@value Math#PI\n" + //TODO: does not work -+ " ///unclosed link {@value Math#PI}\n" + -+ " ///@see List\n" + -+ " ///@throws IOException\n" + -+ " void m() throws java.io.IOException {\n" + -+ " }\n" + -+ " ///\n" + -+ " ///{@link Collections}\n" + -+ " ///\n" + -+ " int field;\n" + -+ " /// {@link IOException\n" + -+ " interface InnerInterface {}\n" + -+ " /// {@link Collections}\n" + -+ " @interface InnerAnnotationType {}\n" + -+ "}\n" + -+ "/// {@link Collections}\n" + -+ "enum TopLevelEnum {\n" + -+ " /** {@link Collections} */" + -+ " E1\n" + -+ "}\n"; ++ """ ++ 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() @@ -371,28 +386,30 @@ index e64ec4698b..88443c53d7 100644 public void testComputeTokensOfReferencedElements() throws Exception { String code = "package p;\n" + -@@ -286,6 +402,85 @@ public class JavadocImportsTest extends JavadocTestSupport { +@@ -286,6 +404,87 @@ public void testComputeTokensOfReferencedElements() throws Exception { // assertEquals(toFind.toString(), exp, tokens); } + public void testComputeTokensOfReferencedElementsMarkdown() throws Exception { + String code = -+ "package p;\n" + -+ "import java.util.Collections;\n" + -+ "class C {\n" + -+ " ///link1 {@link Runnable}\n" + -+ " ///link2 {@link Collections#binarySearch(java.util.List, java.lang.Object) search}\n" + -+ " ///{@link java. uncomplete reference}" + -+// " ///unclosed link {@value Math#PI\n" + //TODO: does not work -+ " ///unclosed link {@value Math#PI}\n" + -+ " ///@see java.util.Collections\n" + -+ " ///@throws ThrowsUnresolved\n" + -+ " ///\n" + -+ " void m() throws java.io.IOException {\n" + -+ " Collections.binarySearch(Collections.emptyList(), \"\");\n" + -+ " double pi = Math.PI;\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ 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"); @@ -457,11 +474,66 @@ index e64ec4698b..88443c53d7 100644 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 8a60126a72..3e8b08e4e7 100644 +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 class GoToSupport { +@@ -408,7 +408,7 @@ public static Context resolveContext(CompilationInfo controller, Document doc, i boolean insideImportStmt = false; TreePath path = controller.getTreeUtilities().pathFor(exactOffset); @@ -470,7 +542,7 @@ index 8a60126a72..3e8b08e4e7 100644 el = JavadocImports.findReferencedElement(controller, offset); } else { path = adjustPathForModuleName(path); -@@ -662,7 +662,7 @@ public class GoToSupport { +@@ -662,7 +662,7 @@ public void run() { Token t = ts.token(); @@ -480,10 +552,10 @@ index 8a60126a72..3e8b08e4e7 100644 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 91c22e9338..6c86a21dd3 100644 +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 -@@ -1318,6 +1318,7 @@ public class JavaCompletionCollector implements CompletionCollector { +@@ -1319,6 +1319,7 @@ private static TokenSequence findLastNonWhitespaceToken(TokenSequen case LINE_COMMENT: case BLOCK_COMMENT: case JAVADOC_COMMENT: @@ -492,10 +564,10 @@ index 91c22e9338..6c86a21dd3 100644 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 733ba9ad32..47fbd2803f 100644 +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 @@ public abstract class JavaCompletionItem implements CompletionItem { +@@ -4617,6 +4617,7 @@ private static TokenSequence findLastNonWhitespaceToken(TokenSequen case LINE_COMMENT: case BLOCK_COMMENT: case JAVADOC_COMMENT: @@ -504,10 +576,10 @@ index 733ba9ad32..47fbd2803f 100644 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 5ac389c224..adefaee33d 100644 +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 class JavaKit extends NbEditorKit { +@@ -458,6 +458,7 @@ public void insert(MutableContext context) throws BadLocationException { if (isJavadocTouched) { blockCommentComplete(doc, dotPos, context); } @@ -516,10 +588,10 @@ index 5ac389c224..adefaee33d 100644 } 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 5f2172b49d..0682f52f5f 100644 +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 @@ class TypingCompletion { +@@ -563,6 +563,21 @@ private static boolean isClosedBlockComment(CharSequence txt, int pos) { return false; } @@ -542,10 +614,10 @@ index 5f2172b49d..0682f52f5f 100644 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 da31e094c5..6ccb51a51e 100644 +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 final class Utilities { +@@ -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: @@ -554,10 +626,10 @@ index da31e094c5..6ccb51a51e 100644 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 eac6a5607c..c328d4ae41 100644 +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.lang.model.util.Types; +@@ -64,6 +64,7 @@ import javax.swing.text.Document; import org.netbeans.api.annotations.common.NonNull; import org.netbeans.api.annotations.common.NullAllowed; @@ -565,7 +637,7 @@ index eac6a5607c..c328d4ae41 100644 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.ResultIterator; +@@ -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; @@ -573,7 +645,7 @@ index eac6a5607c..c328d4ae41 100644 public class JavadocCompletionTask extends UserTask { -@@ -188,16 +190,20 @@ public class JavadocCompletionTask extends UserTask { +@@ -188,16 +190,20 @@ private void analyzeContext(JavadocContext jdctx) { return; } jdts.move(this.caretOffset); @@ -600,7 +672,7 @@ index eac6a5607c..c328d4ae41 100644 case TAG: resolveTagToken(jdctx); break; -@@ -265,7 +271,9 @@ public class JavadocCompletionTask extends UserTask { +@@ -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()) { @@ -611,7 +683,7 @@ index eac6a5607c..c328d4ae41 100644 } do { Token t = jdctx.jdts.token(); -@@ -415,13 +423,13 @@ public class JavadocCompletionTask extends UserTask { +@@ -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; @@ -627,7 +699,7 @@ index eac6a5607c..c328d4ae41 100644 // not java reference return; } else if (jdts.moveNext()) { -@@ -519,7 +527,7 @@ public class JavadocCompletionTask extends UserTask { +@@ -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; @@ -636,7 +708,7 @@ index eac6a5607c..c328d4ae41 100644 // none prefix anchorOffset = caretOffset; completeParamName(tag, "", caretOffset, jdctx); // NOI18N -@@ -1226,11 +1234,10 @@ public class JavadocCompletionTask extends UserTask { +@@ -1226,11 +1234,10 @@ private boolean startsWith(String theString, String prefix) { void resolveOtherText(JavadocContext jdctx, TokenSequence jdts) { Token token = jdts.token(); @@ -652,7 +724,7 @@ index eac6a5607c..c328d4ae41 100644 if (pos > 0 && pos <= text.length() && text.charAt(pos - 1) == '{') { if (tag != null && !JavadocCompletionUtils.isBlockTag(tag)) { -@@ -1244,10 +1251,10 @@ public class JavadocCompletionTask extends UserTask { +@@ -1244,10 +1251,10 @@ void resolveOtherText(JavadocContext jdctx, TokenSequence jdts) } if (tag != null) { insideTag(tag, jdctx); @@ -665,7 +737,7 @@ index eac6a5607c..c328d4ae41 100644 resolveBlockTag(null, jdctx); } } -@@ -1336,6 +1343,7 @@ public class JavadocCompletionTask extends UserTask { +@@ -1336,6 +1343,7 @@ private static class JavadocContext { private DocCommentTree comment; private DocSourcePositions positions; private TokenSequence jdts; @@ -674,21 +746,23 @@ index eac6a5607c..c328d4ae41 100644 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 8deac6817b..5e143d4358 100644 +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,67 @@ public class GoToSupportTest extends NbTestCase { +@@ -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;\n" + -+ "/**\n" + -+ " * @see Obj|ect\n" + -+ " */\n" + -+ "public class Test {\n" + -+ "}\n"; ++ final String code = """ ++ package test; ++ /** ++ * @see Obj|ect ++ */ ++ public class Test { ++ } ++ """; + + performTest(code, new UiUtilsCaller() { + @Override public boolean open(FileObject fo, int pos) { @@ -715,10 +789,12 @@ index 8deac6817b..5e143d4358 100644 + public void testMarkdownJavadoc() throws Exception { + final boolean[] wasCalled = new boolean[1]; + this.sourceLevel = "23"; -+ final String code = "package test;\n" + -+ "///@see Obj|ect\n" + -+ "public class Test {\n" + -+ "}\n"; ++ final String code = """ ++ package test; ++ ///@see Obj|ect ++ public class Test { ++ } ++ """; + + performTest(code, new UiUtilsCaller() { + @Override public boolean open(FileObject fo, int pos) { @@ -746,36 +822,37 @@ index 8deac6817b..5e143d4358 100644 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 a0713d608f..0525fc6a68 100644 +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,21 @@ public void testPositionInTextBlock() throws Exception { +@@ -1419,6 +1419,22 @@ public void testX() throws Exception { ctx.assertDocumentTextEquals("{"); } + public void testJavadocLineRun() { + Context ctx = new Context(new JavaKit(), -+ "class Test {\n" + -+ " ///|\n" + -+ "}\n" -+ ); ++ """ ++ class Test { ++ ///| ++ } ++ """); + ctx.typeChar('\n'); -+ ctx.assertDocumentTextEquals( -+ "class Test {\n" + -+ " ///\n" + -+ " ///|\n" + -+ "}\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 8bf90d619f..884aba40be 100644 +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.ArrayList; +@@ -23,10 +23,14 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -790,7 +867,16 @@ index 8bf90d619f..884aba40be 100644 /** * -@@ -601,8 +605,212 @@ public class JavadocCompletionQueryTest extends JavadocTestSupport { +@@ -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:"); } @@ -799,242 +885,290 @@ index 8bf90d619f..884aba40be 100644 + + public void testBlockTagsCompletionInMarkdown() throws Exception { + String code = -+ "package p;\n" + -+ "class Clazz {\n" + -+ " ///\n" + -+ " /// |\n" + -+ " ///\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ 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;\n" + -+ "class Clazz {\n" + -+ " ///\n" + -+ " ///|\n" + -+ " ///\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ 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;\n" + -+ "class Clazz {\n" + -+ " ///\n" + -+ " ///| \n" + -+ " ///\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ package p; ++ class Clazz { ++ /// ++ ///|\s ++ /// ++ void method(int p1, int p2) { ++ } ++ } ++ """; + + performCompletionTest(code, "@deprecated:", "@exception:", "@hidden:", "@param:", "@return:", "@see:", "@serialData:", "@since:", "@throws:"); + } + -+ public void XXX_testBlockTagsCompletionInMarkdownStart() throws Exception { ++ public void testBlockTagsCompletionInMarkdownStart() throws Exception { + String code = -+ "package p;\n" + -+ "class Clazz {\n" + -+ " ///|\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ 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;\n" + -+ "class Clazz {\n" + -+ " ///\n" + -+ " /// @see CharSequence#le|\n" + -+ " ///\n" + -+ " Clazz() {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ package p; ++ class Clazz { ++ /// ++ /// @see CharSequence#le| ++ /// ++ Clazz() { ++ } ++ } ++ """; + + performCompletionTest(code, "public abstract int length()"); + } + + public void testSeeMarkdown2() throws Exception { + String code = -+ "package p;\n" + -+ "class Clazz {\n" + -+ " ///\n" + -+ " /// @see |\n" + -+ " ///\n" + -+ " Clazz() {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ package p; ++ class Clazz { ++ /// ++ /// @see | ++ /// ++ Clazz() { ++ } ++ } ++ """; + + performCompletionTest(code, null, "String", "Clazz"); + } + + public void testSeeMarkdown3() throws Exception { + String code = -+ "package p;\n" + -+ "class Clazz {\n" + -+ " ///@param i i\n" + -+ " ///@see |\n" + -+ " ///\n" + -+ " Clazz(int i) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ 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;\n" + -+ "class Clazz {\n" + -+ " ///\n" + -+ " /// @param |\n" + -+ " ///\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ package p; ++ class Clazz { ++ /// ++ /// @param | ++ /// ++ void method(int p1, int p2) { ++ } ++ } ++ """; + + performCompletionTest(code, "p1:", "p2:"); + } + + public void testJavadocOldStart1() throws Exception { + String code = -+ "package p;\n" + -+ "class Clazz {\n" + -+ " /**| */\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ 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;\n" + -+ "class Clazz {\n" + -+ " /**@s| */\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ 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;\n" + -+ "class Clazz {\n" + -+ " /**@param | */\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ package p; ++ class Clazz { ++ /**@param | */ ++ void method(int p1, int p2) { ++ } ++ } ++ """; + + performCompletionTest(code, "p1:", "p2:"); + } + + public void testJavadocOldStart4() throws Exception { + String code = -+ "package p;\n" + -+ "class Clazz {\n" + -+ " /**@see | */\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ 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;\n" + -+ "class Clazz {\n" + -+ " ///|\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ 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;\n" + -+ "class Clazz {\n" + -+ " ///@s|\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ 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;\n" + -+ "class Clazz {\n" + -+ " ///@param |\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ package p; ++ class Clazz { ++ ///@param | ++ void method(int p1, int p2) { ++ } ++ } ++ """; + + performCompletionTest(code, "p1:", "p2:"); + } + + public void testJavadocMarkdownStart4() throws Exception { + String code = -+ "package p;\n" + -+ "class Clazz {\n" + -+ " ///@see |\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ 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 +855,27 @@ public class JavadocCompletionQueryTest extends JavadocTestSupport { +@@ -647,4 +887,30 @@ private void performCompletionTest(String code, String... golden) throws Excepti assertEquals(goldenList, resultStrings); } } + -+ @ServiceProvider(service=SourceLevelQueryImplementation2.class) -+ public static final class SourceLevelQueryImpl implements SourceLevelQueryImplementation2 { ++ @Override ++ protected Object[] additionalServices() { ++ return new Object[] { ++ new SourceLevelQueryImplementation2() { + -+ @Override -+ public Result getSourceLevel(FileObject javaFile) { -+ return new Result() { + @Override -+ public String getSourceLevel() { -+ return "23"; -+ } ++ public Result getSourceLevel(FileObject javaFile) { ++ return new Result() { ++ @Override ++ public String getSourceLevel() { ++ return "23"; ++ } + -+ @Override -+ public void addChangeListener(ChangeListener listener) { -+ } ++ @Override ++ public void addChangeListener(ChangeListener listener) { ++ } + -+ @Override -+ public void removeChangeListener(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 17602a5e40..25738a8da1 100644 +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 { @@ -1045,7 +1179,7 @@ index 17602a5e40..25738a8da1 100644 // Errors INVALID_COMMENT_END("*/", "error"), -@@ -262,6 +263,9 @@ public enum JavaTokenId implements TokenId { +@@ -262,6 +263,9 @@ protected LanguageEmbedding embedding( case JAVADOC_COMMENT: return LanguageEmbedding.create(JavadocTokenId.language(), 3, (token.partType() == PartType.COMPLETE) ? 2 : 0); @@ -1056,10 +1190,10 @@ index 17602a5e40..25738a8da1 100644 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 49c32619b4..438b8b84ff 100644 +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 class JavaLexer implements Lexer { +@@ -321,6 +321,13 @@ public Token nextToken() { case '/': switch (nextChar()) { case '/': // in single-line comment @@ -1073,7 +1207,7 @@ index 49c32619b4..438b8b84ff 100644 while (true) switch (nextChar()) { case '\r': consumeNewline(); -@@ -1428,6 +1435,34 @@ public class JavaLexer implements Lexer { +@@ -1428,6 +1435,34 @@ private Token finishFloatExponent() { return token(JavaTokenId.DOUBLE_LITERAL); } } @@ -1109,10 +1243,10 @@ index 49c32619b4..438b8b84ff 100644 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 fa7189b483..c6ab507fee 100644 +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 @@ public class JavadocLexer implements Lexer { +@@ -194,6 +194,20 @@ private Token otherText(int ch) { leftbr = false; newline = false; break; @@ -1134,21 +1268,23 @@ index fa7189b483..c6ab507fee 100644 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 6402fc5c34..6ea9909143 100644 +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,78 @@ public class JavaLexerBatchTest extends TestCase { +@@ -812,4 +812,83 @@ public void testTemplates2() { assertFalse(ts.moveNext()); } + public void testMarkdown1() { -+ String text = "///test\n" + -+ "///@see second line\n" + -+ "///third\n" + -+ "\n" + -+ "///another run\n" + -+ "///another line\n" + -+ "\n"; ++ 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(); @@ -1168,12 +1304,14 @@ index 6402fc5c34..6ea9909143 100644 + } + + public void testMarkdown2() { -+ String text = "///test\n" + -+ "///@see second line\n" + -+ "///third\n" + -+ "\n" + -+ "///another run\n" + -+ "///another line\n"; ++ 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(); @@ -1192,12 +1330,13 @@ index 6402fc5c34..6ea9909143 100644 + } + + public void testMarkdown3() { -+ String text = "///test\n" + -+ "///@see second line\n" + -+ "///third\n" + -+ "\n" + -+ "///another run\n" + -+ "///another line"; ++ 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(); @@ -1217,10 +1356,10 @@ index 6402fc5c34..6ea9909143 100644 + } 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 72668f24a6..c0859ba2f8 100644 +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 class JavadocLexerTest extends NbTestCase { +@@ -143,6 +143,20 @@ public void test233097b() { LexerTestUtilities.assertNextTokenEquals(ts, JavadocTokenId.OTHER_TEXT, "}"); } @@ -1242,7 +1381,7 @@ index 72668f24a6..c0859ba2f8 100644 // 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 fc390b64bb..001c83187e 100644 +index fc390b64bbc9..001c83187ebd 100644 --- a/java/java.lsp.server/licenseinfo.xml +++ b/java/java.lsp.server/licenseinfo.xml @@ -50,4 +50,9 @@ @@ -1257,7 +1396,7 @@ index fc390b64bb..001c83187e 100644 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 0000000000..c6b69e5336 +index 000000000000..c6b69e53360e --- /dev/null +++ b/java/java.lsp.server/vscode/language-configuration.json @@ -0,0 +1,111 @@ @@ -1373,7 +1512,7 @@ index 0000000000..c6b69e5336 + ] +} diff --git a/java/java.lsp.server/vscode/package.json b/java/java.lsp.server/vscode/package.json -index 07c3269640..a341dad480 100644 +index cad97381ef73..60795163b16a 100644 --- a/java/java.lsp.server/vscode/package.json +++ b/java/java.lsp.server/vscode/package.json @@ -35,6 +35,18 @@ @@ -1396,7 +1535,7 @@ index 07c3269640..a341dad480 100644 "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 1357aff68d..d990bf88fd 100644 +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 @@ @@ -1464,10 +1603,10 @@ index 1357aff68d..d990bf88fd 100644 ] }, 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 a7453c4477..bca4cbd46a 100644 +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 final class TreeUtilities { +@@ -1302,7 +1302,11 @@ public int[] findNameSpan(DocCommentTree docTree, ReferenceTree ref) { tokenSequence.move(pos); @@ -1481,7 +1620,7 @@ index a7453c4477..bca4cbd46a 100644 TokenSequence jdocTS = tokenSequence.embedded(JavadocTokenId.language()); diff --git a/java/java.sourceui/nbproject/project.xml b/java/java.sourceui/nbproject/project.xml -index 9ac80a3c09..1f7e220870 100644 +index 9ac80a3c0929..f1bb6375971b 100644 --- a/java/java.sourceui/nbproject/project.xml +++ b/java/java.sourceui/nbproject/project.xml @@ -86,6 +86,14 @@ @@ -1493,17 +1632,25 @@ index 9ac80a3c09..1f7e220870 100644 + + + -+ 1.17 ++ 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 fdabe50444..0ce9410f9e 100644 +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 -@@ -119,6 +119,11 @@ import javax.tools.SimpleJavaFileObject; +@@ -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; @@ -1515,7 +1662,7 @@ index fdabe50444..0ce9410f9e 100644 import javax.lang.model.element.RecordComponentElement; import org.netbeans.api.java.queries.SourceLevelQuery; import org.netbeans.api.java.queries.SourceLevelQuery.Profile; -@@ -1275,7 +1280,7 @@ public class ElementJavadoc { +@@ -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; @@ -1524,13 +1671,23 @@ index fdabe50444..0ce9410f9e 100644 switch (tag.getKind()) { case REFERENCE: ReferenceTree refTag = (ReferenceTree)tag; -@@ -1395,6 +1400,53 @@ public class ElementJavadoc { +@@ -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 -> "MARKDOWN".equals(t.getKind().name()))) { ++ if (tags.stream().noneMatch(t -> t.getKind() == DocTree.Kind.MARKDOWN)) { + return tags; + } + @@ -1538,8 +1695,8 @@ index fdabe50444..0ce9410f9e 100644 + List replacements = new ArrayList<>(); + + for (DocTree t : tags) { -+ if ("MARKDOWN".equals(t.getKind().name())) { -+ markdownSource.append(t.toString()); //TODO: should avoid toString() ++ if (t.getKind() == DocTree.Kind.MARKDOWN) { ++ markdownSource.append(((RawTextTree) t).getContent()); + } else { + markdownSource.append(REPLACEMENT); + replacements.add(t); @@ -1580,10 +1737,10 @@ index fdabe50444..0ce9410f9e 100644 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 0000000000..9aaa49ecb5 +index 000000000000..13bee62039d3 --- /dev/null +++ b/java/java.sourceui/test/unit/src/org/netbeans/api/java/source/ui/ElementJavadocTest.java -@@ -0,0 +1,149 @@ +@@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file @@ -1732,4 +1889,19 @@ index 0000000000..9aaa49ecb5 + 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/7497.diff b/patches/7497.diff deleted file mode 100644 index 87962363..00000000 --- a/patches/7497.diff +++ /dev/null @@ -1,305 +0,0 @@ -diff --git a/java/java.lsp.server/nbcode/integration/nbproject/project.xml b/java/java.lsp.server/nbcode/integration/nbproject/project.xml -index 3cee4feb1698..70102d62f3a3 100644 ---- a/java/java.lsp.server/nbcode/integration/nbproject/project.xml -+++ b/java/java.lsp.server/nbcode/integration/nbproject/project.xml -@@ -118,6 +118,15 @@ - - - -+ -+ org.netbeans.modules.java.platform -+ -+ -+ -+ 1 -+ 1.66 -+ -+ - - org.netbeans.modules.parsing.api - -diff --git a/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspJavaPlatformProviderOverride.java b/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspJavaPlatformProviderOverride.java -new file mode 100644 -index 000000000000..31066657e35d ---- /dev/null -+++ b/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspJavaPlatformProviderOverride.java -@@ -0,0 +1,31 @@ -+/* -+ * 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.nbcode.integration; -+ -+import org.netbeans.modules.java.lsp.server.ui.AbstractJavaPlatformProviderOverride; -+import org.netbeans.modules.java.platform.implspi.JavaPlatformProvider; -+import org.openide.util.lookup.ServiceProvider; -+ -+/** -+ * -+ * @author sdedic -+ */ -+@ServiceProvider(service = JavaPlatformProvider.class, position = 10_000) -+public class LspJavaPlatformProviderOverride extends AbstractJavaPlatformProviderOverride { -+} -diff --git a/java/java.lsp.server/nbproject/project.xml b/java/java.lsp.server/nbproject/project.xml -index 2b5122a231bb..cea7ea966aae 100644 ---- a/java/java.lsp.server/nbproject/project.xml -+++ b/java/java.lsp.server/nbproject/project.xml -@@ -400,6 +400,15 @@ - 0.3 - - -+ -+ org.netbeans.modules.java.platform -+ -+ -+ -+ 1 -+ 1.66 -+ -+ - - org.netbeans.modules.java.project - -@@ -697,12 +706,6 @@ - 9.24 - - -- -- com.google.guava -- -- 27.16 -- -- - - - -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java -index f42d7ee24809..2b4565d80dd5 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java -@@ -45,6 +45,7 @@ - import java.util.logging.Logger; - import com.google.gson.InstanceCreator; - import com.google.gson.JsonObject; -+import com.google.gson.JsonPrimitive; - import java.util.prefs.Preferences; - import java.util.LinkedHashSet; - import java.util.Objects; -@@ -382,6 +383,7 @@ public static class LanguageServerImpl implements LanguageServer, LanguageClient - - private static final String NETBEANS_FORMAT = "format"; - private static final String NETBEANS_JAVA_IMPORTS = "java.imports"; -+ private static final String NETBEANS_PROJECT_JDKHOME = "project.jdkhome"; - private static final String NETBEANS_JAVA_HINTS = "hints"; - - // change to a greater throughput if the initialization waits on more processes than just (serialized) project open. -@@ -1020,6 +1022,7 @@ private void collectProjectCandidates(FileObject fo, List candidates - private void initializeOptions() { - getWorkspaceProjects().thenAccept(projects -> { - ConfigurationItem item = new ConfigurationItem(); -+ // PENDING: what about doing just one roundtrip to the client- we may request multiple ConfiguratonItems in one message ? - item.setSection(client.getNbCodeCapabilities().getConfigurationPrefix() + NETBEANS_JAVA_HINTS); - client.configuration(new ConfigurationParams(Collections.singletonList(item))).thenAccept(c -> { - if (c != null && !c.isEmpty() && c.get(0) instanceof JsonObject) { -@@ -1030,6 +1033,16 @@ private void initializeOptions() { - textDocumentService.reRunDiagnostics(); - } - }); -+ item.setSection(client.getNbCodeCapabilities().getConfigurationPrefix() + NETBEANS_PROJECT_JDKHOME); -+ client.configuration(new ConfigurationParams(Collections.singletonList(item))).thenAccept(c -> { -+ JsonPrimitive newProjectJDKHomePath = null; -+ -+ if (c != null && !c.isEmpty() && c.get(0) instanceof JsonPrimitive) { -+ newProjectJDKHomePath = (JsonPrimitive) c.get(0); -+ } else { -+ } -+ textDocumentService.updateProjectJDKHome(newProjectJDKHomePath); -+ }); - if (projects != null && projects.length > 0) { - FileObject fo = projects[0].getProjectDirectory(); - item.setScopeUri(Utils.toUri(fo)); -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 ad0d82fa448e..c112b4eb73d7 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 -@@ -240,6 +240,7 @@ - import org.netbeans.api.lsp.StructureElement; - import org.netbeans.modules.editor.indent.api.Reformat; - import org.netbeans.modules.java.lsp.server.URITranslator; -+import org.netbeans.modules.java.lsp.server.ui.AbstractJavaPlatformProviderOverride; - import org.netbeans.modules.parsing.impl.SourceAccessor; - import org.netbeans.spi.editor.hints.ErrorDescription; - import org.netbeans.spi.editor.hints.Fix; -@@ -2086,6 +2087,15 @@ void updateJavaHintPreferences(JsonObject configuration) { - reRunDiagnostics(); - } - -+ void updateProjectJDKHome(JsonPrimitive configuration) { -+ if (configuration == null) { -+ client.logMessage(new MessageParams(MessageType.Log,"Project runtime JDK unset, defaults to NBLS JDK")); -+ } else { -+ client.logMessage(new MessageParams(MessageType.Log, "Project runtime JDK set to " + configuration.getAsString())); -+ } -+ AbstractJavaPlatformProviderOverride.setDefaultPlatformOverride(configuration != null ? configuration.getAsString() : null); -+ } -+ - private String key(ErrorProvider.Kind errorKind) { - return errorKind.name().toLowerCase(Locale.ROOT); - } -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java -index d3027a2eb620..6e0755bfb6e6 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java -@@ -1347,7 +1347,9 @@ public void didChangeConfiguration(DidChangeConfigurationParams params) { - String fullConfigPrefix = client.getNbCodeCapabilities().getConfigurationPrefix(); - String configPrefix = fullConfigPrefix.substring(0, fullConfigPrefix.length() - 1); - server.openedProjects().thenAccept(projects -> { -+ // PENDING: invent a pluggable mechanism for this, this does not scale and the typecast to serviceImpl is ugly - ((TextDocumentServiceImpl)server.getTextDocumentService()).updateJavaHintPreferences(((JsonObject) params.getSettings()).getAsJsonObject(configPrefix).getAsJsonObject(NETBEANS_JAVA_HINTS)); -+ ((TextDocumentServiceImpl)server.getTextDocumentService()).updateProjectJDKHome(((JsonObject) params.getSettings()).getAsJsonObject(configPrefix).getAsJsonObject("project").getAsJsonPrimitive("jdkhome")); - if (projects != null && projects.length > 0) { - updateJavaFormatPreferences(projects[0].getProjectDirectory(), ((JsonObject) params.getSettings()).getAsJsonObject(configPrefix).getAsJsonObject("format")); - updateJavaImportPreferences(projects[0].getProjectDirectory(), ((JsonObject) params.getSettings()).getAsJsonObject(configPrefix).getAsJsonObject("java").getAsJsonObject("imports")); -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/AbstractJavaPlatformProviderOverride.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/AbstractJavaPlatformProviderOverride.java -new file mode 100644 -index 000000000000..df668954dd94 ---- /dev/null -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/AbstractJavaPlatformProviderOverride.java -@@ -0,0 +1,122 @@ -+/* -+ * 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.lsp.server.ui; -+ -+import java.beans.PropertyChangeListener; -+import java.beans.PropertyChangeSupport; -+import java.io.File; -+import java.io.IOException; -+import java.util.HashSet; -+import java.util.Set; -+import org.netbeans.api.java.platform.JavaPlatform; -+import org.netbeans.api.java.platform.JavaPlatformManager; -+import org.netbeans.modules.java.platform.implspi.JavaPlatformProvider; -+import org.netbeans.spi.java.platform.JavaPlatformFactory; -+import org.openide.filesystems.FileObject; -+import org.openide.filesystems.FileUtil; -+import org.openide.util.Exceptions; -+import org.openide.util.Lookup; -+ -+public abstract class AbstractJavaPlatformProviderOverride implements JavaPlatformProvider { -+ -+ private static final JavaPlatform[] NO_PLATFORMS = new JavaPlatform[0]; -+ private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); -+ private FileObject defaultPlatformOverride; -+ -+ @Override -+ @SuppressWarnings("ReturnOfCollectionOrArrayField") -+ public JavaPlatform[] getInstalledPlatforms() { -+ return NO_PLATFORMS; -+ } -+ -+ @Override -+ public JavaPlatform getDefaultPlatform() { -+ FileObject override; -+ -+ synchronized (this) { -+ override = defaultPlatformOverride; -+ } -+ -+ if (override == null) { -+ return null; -+ } -+ -+ Set existingNames = new HashSet<>(); -+ JavaPlatform found = null; -+ -+ for (JavaPlatform platform : JavaPlatformManager.getDefault().getInstalledPlatforms()) { -+ if (platform.getInstallFolders().stream().anyMatch(folder -> folder.equals(override))) { -+ found = platform; -+ break; -+ } -+ existingNames.add(platform.getDisplayName()); -+ } -+ -+ if (found == null ){ -+ String newName = defaultPlatformOverride.getPath(); -+ -+ while (existingNames.contains(newName)) { -+ newName += "1"; -+ } -+ -+ for (JavaPlatformFactory.Provider provider : Lookup.getDefault().lookupAll(JavaPlatformFactory.Provider.class)) { -+ JavaPlatformFactory factory = provider.forType("j2se"); -+ if (factory != null) { -+ try { -+ found = factory.create(override, newName, true); -+ } catch (IOException ex) { -+ Exceptions.printStackTrace(ex); -+ } -+ } -+ } -+ } -+ -+ return found; -+ } -+ -+ @Override -+ public void addPropertyChangeListener(PropertyChangeListener listener) { -+ pcs.addPropertyChangeListener(listener); -+ } -+ -+ @Override -+ public void removePropertyChangeListener(PropertyChangeListener listener) { -+ pcs.removePropertyChangeListener(listener); -+ } -+ -+ private void dosetDefaultPlatformOverride(String defaultPlatformOverride) { -+ FileObject override = defaultPlatformOverride != null ? FileUtil.toFileObject(new File(defaultPlatformOverride)) -+ : null; -+ -+ synchronized (this) { -+ this.defaultPlatformOverride = override; -+ } -+ -+ pcs.firePropertyChange(null, null, null); -+ } -+ -+ public static void setDefaultPlatformOverride(String defaultPlatformOverride) { -+ for (JavaPlatformProvider p : Lookup.getDefault().lookupAll(JavaPlatformProvider.class)) { -+ if (p instanceof AbstractJavaPlatformProviderOverride) { -+ ((AbstractJavaPlatformProviderOverride) p).dosetDefaultPlatformOverride(defaultPlatformOverride); -+ } -+ } -+ } -+ -+} diff --git a/patches/7548_source-1.8.diff b/patches/7548_source-1.8.diff deleted file mode 100644 index fc7c7191..00000000 --- a/patches/7548_source-1.8.diff +++ /dev/null @@ -1,73 +0,0 @@ -diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Unused.java b/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Unused.java -index e681ed7b97..0d0c3c4c77 100644 ---- a/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Unused.java -+++ b/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Unused.java -@@ -20,6 +20,7 @@ package org.netbeans.modules.java.hints.bugs; - - import com.sun.source.tree.Tree.Kind; - import java.util.ArrayList; -+import java.util.Arrays; - import java.util.List; - import javax.lang.model.element.ElementKind; - import org.netbeans.modules.java.editor.base.semantic.UnusedDetector; -@@ -34,6 +35,7 @@ import org.netbeans.spi.java.hints.JavaFixUtilities; - import org.netbeans.spi.java.hints.TriggerTreeKind; - import org.openide.util.NbBundle.Messages; - -+import static org.netbeans.api.java.source.CompilationInfo.CacheClearPolicy.ON_TASK_END; - /** - * - * @author lahvac -@@ -52,25 +54,47 @@ public class Unused { - @BooleanOption(displayName="#LBL_UnusedPackagePrivate", tooltip="#TP_UnusedPackagePrivate", defaultValue=DETECT_UNUSED_PACKAGE_PRIVATE_DEFAULT) - public static final String DETECT_UNUSED_PACKAGE_PRIVATE = "detect.unused.package.private"; - -- @TriggerTreeKind(Kind.COMPILATION_UNIT) -+ @TriggerTreeKind({ -+ //class-like kinds: -+ Kind.ANNOTATION_TYPE, Kind.CLASS, Kind.ENUM, Kind.INTERFACE, Kind.RECORD, -+ Kind.VARIABLE, -+ Kind.METHOD -+ }) - public static List unused(HintContext ctx) { - List unused = UnusedDetector.findUnused(ctx.getInfo(), () -> ctx.isCanceled()); -- List result = new ArrayList<>(unused.size()); -- boolean detectUnusedPackagePrivate = ctx.getPreferences().getBoolean(DETECT_UNUSED_PACKAGE_PRIVATE, DETECT_UNUSED_PACKAGE_PRIVATE_DEFAULT); -+ if (unused.isEmpty()) { -+ return null; -+ } -+ boolean detectUnusedPackagePrivate = getTaskCachedBoolean(ctx, DETECT_UNUSED_PACKAGE_PRIVATE, DETECT_UNUSED_PACKAGE_PRIVATE_DEFAULT); - for (UnusedDescription ud : unused) { - if (ctx.isCanceled()) { - break; - } -+ if (ud.unusedElementPath.getLeaf() != ctx.getPath().getLeaf()) { -+ continue; -+ } - if (!detectUnusedPackagePrivate && ud.packagePrivate) { - continue; - } - ErrorDescription err = convertUnused(ctx, ud); - if (err != null) { -- result.add(err); -+ return Arrays.asList(err); - } -+ break; - } -- return result; -+ return null; - } -+ -+ // reading from AuxiliaryConfigBasedPreferences in inner loops is not cheap since it needs a mutex -+ private static boolean getTaskCachedBoolean(HintContext ctx, String key, boolean defaultVal) { -+ Object cached = ctx.getInfo().getCachedValue(key); -+ if (cached instanceof Boolean) { -+ return (Boolean)cached; -+ } -+ boolean fromPrefs = ctx.getPreferences().getBoolean(key, defaultVal); -+ ctx.getInfo().putCachedValue(key, fromPrefs, ON_TASK_END); -+ return fromPrefs; -+ } - - @Messages({ - "# {0} - variable name", diff --git a/patches/7583_source-1.8.diff b/patches/7583_source-1.8.diff deleted file mode 100644 index d8250b61..00000000 --- a/patches/7583_source-1.8.diff +++ /dev/null @@ -1,80 +0,0 @@ -diff --git a/extide/gradle/src/org/netbeans/modules/gradle/api/execute/GradleDistributionManager.java b/extide/gradle/src/org/netbeans/modules/gradle/api/execute/GradleDistributionManager.java -index 024f95a670..0a106ffbb6 100644 ---- a/extide/gradle/src/org/netbeans/modules/gradle/api/execute/GradleDistributionManager.java -+++ b/extide/gradle/src/org/netbeans/modules/gradle/api/execute/GradleDistributionManager.java -@@ -101,9 +101,10 @@ public final class GradleDistributionManager { - GradleVersion.version("7.6"), // JDK-19 - GradleVersion.version("8.3"), // JDK-20 - GradleVersion.version("8.5"), // JDK-21 -+ GradleVersion.version("8.8"), // JDK-22 - }; - -- private static final GradleVersion LAST_KNOWN_GRADLE = GradleVersion.version("8.7"); //NOI18N -+ private static final GradleVersion LAST_KNOWN_GRADLE = GradleVersion.version("8.9"); //NOI18N - - final File gradleUserHome; - -diff --git a/extide/libs.gradle/external/binaries-list b/extide/libs.gradle/external/binaries-list -index 2e58f89b85fd..dff2c2265b37 100644 ---- a/extide/libs.gradle/external/binaries-list -+++ b/extide/libs.gradle/external/binaries-list -@@ -15,4 +15,4 @@ - # specific language governing permissions and limitations - # under the License. - --5F48B9BB9099B900FC33864A3794F31C439D9F73 https://repo.gradle.org/artifactory/libs-releases/org/gradle/gradle-tooling-api/8.7/gradle-tooling-api-8.7.jar gradle-tooling-api-8.7.jar -+7BCC4423C529A42ECA9D0CE5B5275369EF4DF55A https://repo.gradle.org/artifactory/libs-releases/org/gradle/gradle-tooling-api/8.9/gradle-tooling-api-8.9.jar gradle-tooling-api-8.9.jar -diff --git a/extide/libs.gradle/external/gradle-tooling-api-8.7-license.txt b/extide/libs.gradle/external/gradle-tooling-api-8.9-license.txt -similarity index 99% -rename from extide/libs.gradle/external/gradle-tooling-api-8.7-license.txt -rename to extide/libs.gradle/external/gradle-tooling-api-8.9-license.txt -index 84a9de902f75..74cb1addb8d6 100644 ---- a/extide/libs.gradle/external/gradle-tooling-api-8.7-license.txt -+++ b/extide/libs.gradle/external/gradle-tooling-api-8.9-license.txt -@@ -1,7 +1,7 @@ - Name: Gradle Tooling API - Description: Gradle Tooling API --Version: 8.7 --Files: gradle-tooling-api-8.7.jar -+Version: 8.9 -+Files: gradle-tooling-api-8.9.jar - License: Apache-2.0 - Origin: Gradle Inc. - URL: https://gradle.org/ -diff --git a/extide/libs.gradle/external/gradle-tooling-api-8.7-notice.txt b/extide/libs.gradle/external/gradle-tooling-api-8.9-notice.txt -similarity index 100% -rename from extide/libs.gradle/external/gradle-tooling-api-8.7-notice.txt -rename to extide/libs.gradle/external/gradle-tooling-api-8.9-notice.txt -diff --git a/extide/libs.gradle/manifest.mf b/extide/libs.gradle/manifest.mf -index 64b5cf508e40..a489976e11ed 100644 ---- a/extide/libs.gradle/manifest.mf -+++ b/extide/libs.gradle/manifest.mf -@@ -2,4 +2,4 @@ Manifest-Version: 1.0 - AutoUpdate-Show-In-Client: false - OpenIDE-Module: org.netbeans.modules.libs.gradle/8 - OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/libs/gradle/Bundle.properties --OpenIDE-Module-Specification-Version: 8.7 -+OpenIDE-Module-Specification-Version: 8.9 -diff --git a/extide/libs.gradle/nbproject/project.properties b/extide/libs.gradle/nbproject/project.properties -index 6cd0e22d8315..6e4605fe4922 100644 ---- a/extide/libs.gradle/nbproject/project.properties -+++ b/extide/libs.gradle/nbproject/project.properties -@@ -22,4 +22,4 @@ javac.compilerargs=-Xlint -Xlint:-serial - # Sigtest fails to read the classes in the gradle-tooling-api - sigtest.skip.gen=true - --release.external/gradle-tooling-api-8.7.jar=modules/gradle/gradle-tooling-api.jar -+release.external/gradle-tooling-api-8.9.jar=modules/gradle/gradle-tooling-api.jar -diff --git a/extide/libs.gradle/nbproject/project.xml b/extide/libs.gradle/nbproject/project.xml -index dc58e80a4500..d82027b5e615 100644 ---- a/extide/libs.gradle/nbproject/project.xml -+++ b/extide/libs.gradle/nbproject/project.xml -@@ -39,7 +39,7 @@ - - - gradle/gradle-tooling-api.jar -- external/gradle-tooling-api-8.7.jar -+ external/gradle-tooling-api-8.9.jar - - - diff --git a/patches/7621.diff b/patches/7621.diff deleted file mode 100644 index ea5e3e64..00000000 --- a/patches/7621.diff +++ /dev/null @@ -1,125 +0,0 @@ -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ConstructorGenerator.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ConstructorGenerator.java -index 583f90585472..c4602b78e2d6 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ConstructorGenerator.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ConstructorGenerator.java -@@ -188,7 +188,7 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat - data.put(OFFSET, startOffset); - data.put(CONSTRUCTORS, constructors); - data.put(FIELDS, fields); -- return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateConstructor(), isSource ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, null, "nbls.generate.code", GENERATE_CONSTRUCTOR, data)); -+ return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateConstructor(), isSource ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_CONSTRUCTOR, client.getNbCodeCapabilities()), data)); - } - - @Override -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/DelegateMethodGenerator.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/DelegateMethodGenerator.java -index 74c4523b85be..b7eae2e038e4 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/DelegateMethodGenerator.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/DelegateMethodGenerator.java -@@ -137,7 +137,7 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat - data.put(OFFSET, offset); - data.put(TYPE, typeItem); - data.put(FIELDS, fields); -- return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateDelegateMethod(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_DELEGATE_METHOD, data)); -+ return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateDelegateMethod(), CODE_GENERATOR_KIND, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_DELEGATE_METHOD, client.getNbCodeCapabilities()), data)); - } - - @Override -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/EqualsHashCodeGenerator.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/EqualsHashCodeGenerator.java -index 4e9b4f747a59..7f2a8084d80f 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/EqualsHashCodeGenerator.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/EqualsHashCodeGenerator.java -@@ -115,11 +115,11 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat - String uri = Utils.toUri(info.getFileObject()); - if (equalsHashCode[0] == null) { - if (equalsHashCode[1] == null) { -- return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateEqualsHashCode(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_EQUALS_HASHCODE, data(0, uri, offset, fields))); -+ return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateEqualsHashCode(), CODE_GENERATOR_KIND, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_EQUALS_HASHCODE, client.getNbCodeCapabilities()), data(0, uri, offset, fields))); - } -- return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateEquals(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_EQUALS_HASHCODE, data(EQUALS_ONLY, uri, offset, fields))); -+ return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateEquals(), CODE_GENERATOR_KIND, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_EQUALS_HASHCODE, client.getNbCodeCapabilities()), data(EQUALS_ONLY, uri, offset, fields))); - } -- return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateHashCode(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_EQUALS_HASHCODE, data(HASH_CODE_ONLY, uri, offset, fields))); -+ return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateHashCode(), CODE_GENERATOR_KIND, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_EQUALS_HASHCODE, client.getNbCodeCapabilities()), data(HASH_CODE_ONLY, uri, offset, fields))); - } - - @Override -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/GetterSetterGenerator.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/GetterSetterGenerator.java -index fb85050e92be..0a96c15dcea8 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/GetterSetterGenerator.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/GetterSetterGenerator.java -@@ -103,7 +103,7 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat - List result = new ArrayList<>(); - if (missingGetters) { - String name = pair.first().size() == 1 ? Bundle.DN_GenerateGetterFor(pair.first().iterator().next().getSimpleName().toString()) : Bundle.DN_GenerateGetters(); -- result.add(createCodeAction(client, name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, null, "nbls.generate.code", GENERATE_GETTER_SETTER, data(GeneratorUtils.GETTERS_ONLY, uri, offset, all, pair.first().stream().map(variableElement -> { -+ result.add(createCodeAction(client, name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_GETTER_SETTER, client.getNbCodeCapabilities()), data(GeneratorUtils.GETTERS_ONLY, uri, offset, all, pair.first().stream().map(variableElement -> { - QuickPickItem item = new QuickPickItem(createLabel(info, variableElement)); - item.setUserData(new ElementData(variableElement)); - return item; -@@ -111,7 +111,7 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat - } - if (missingSetters) { - String name = pair.second().size() == 1 ? Bundle.DN_GenerateSetterFor(pair.second().iterator().next().getSimpleName().toString()) : Bundle.DN_GenerateSetters(); -- result.add(createCodeAction(client, name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, null, "nbls.generate.code", GENERATE_GETTER_SETTER, data(GeneratorUtils.SETTERS_ONLY, uri, offset, all, pair.second().stream().map(variableElement -> { -+ result.add(createCodeAction(client, name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_GETTER_SETTER, client.getNbCodeCapabilities()), data(GeneratorUtils.SETTERS_ONLY, uri, offset, all, pair.second().stream().map(variableElement -> { - QuickPickItem item = new QuickPickItem(createLabel(info, variableElement)); - item.setUserData(new ElementData(variableElement)); - return item; -@@ -120,7 +120,7 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat - if (missingGetters && missingSetters) { - pair.first().retainAll(pair.second()); - String name = pair.first().size() == 1 ? Bundle.DN_GenerateGetterSetterFor(pair.first().iterator().next().getSimpleName().toString()) : Bundle.DN_GenerateGettersSetters(); -- result.add(createCodeAction(client, name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, null, "nbls.generate.code", GENERATE_GETTER_SETTER, data(0, uri, offset, all, pair.first().stream().map(variableElement -> { -+ result.add(createCodeAction(client, name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_GETTER_SETTER, client.getNbCodeCapabilities()), data(0, uri, offset, all, pair.first().stream().map(variableElement -> { - QuickPickItem item = new QuickPickItem(createLabel(info, variableElement)); - item.setUserData(new ElementData(variableElement)); - return item; -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ImplementOverrideMethodGenerator.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ImplementOverrideMethodGenerator.java -index c839e8e60dde..4aca8de3ca64 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ImplementOverrideMethodGenerator.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ImplementOverrideMethodGenerator.java -@@ -109,7 +109,7 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat - implementMethods.add(new QuickPickItem(createLabel(info, method), enclosingTypeName, null, mustImplement, new ElementData(method))); - } - if (!implementMethods.isEmpty()) { -- result.add(createCodeAction(client, Bundle.DN_GenerateImplementMethod(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_IMPLEMENT_OVERRIDE, data(uri, offset, true, implementMethods))); -+ result.add(createCodeAction(client, Bundle.DN_GenerateImplementMethod(), CODE_GENERATOR_KIND, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_IMPLEMENT_OVERRIDE, client.getNbCodeCapabilities()), data(uri, offset, true, implementMethods))); - } - } - if (typeElement.getKind().isClass() || typeElement.getKind().isInterface()) { -@@ -125,7 +125,7 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat - overrideMethods.add(item); - } - if (!overrideMethods.isEmpty()) { -- result.add(createCodeAction(client, Bundle.DN_GenerateOverrideMethod(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_IMPLEMENT_OVERRIDE, data(uri, offset, false, overrideMethods))); -+ result.add(createCodeAction(client, Bundle.DN_GenerateOverrideMethod(), CODE_GENERATOR_KIND, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_IMPLEMENT_OVERRIDE, client.getNbCodeCapabilities()), data(uri, offset, false, overrideMethods))); - } - } - return result; -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LoggerGenerator.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LoggerGenerator.java -index 4c92f0975fd2..46afb67b3541 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LoggerGenerator.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LoggerGenerator.java -@@ -108,7 +108,7 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat - Map data = new HashMap<>(); - data.put(URI, uri); - data.put(OFFSET, offset); -- return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateLogger(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_LOGGER, data)); -+ return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateLogger(), CODE_GENERATOR_KIND, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_LOGGER, client.getNbCodeCapabilities()), data)); - } - - @Override -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ToStringGenerator.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ToStringGenerator.java -index b88b31213572..9391da00bffa 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ToStringGenerator.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ToStringGenerator.java -@@ -116,7 +116,7 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat - data.put(URI, uri); - data.put(OFFSET, offset); - data.put(FIELDS, fields); -- return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateToString(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_TO_STRING, data)); -+ return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateToString(), CODE_GENERATOR_KIND, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_TO_STRING, client.getNbCodeCapabilities()), data)); - } - - @Override - diff --git a/patches/7670.diff b/patches/7670.diff new file mode 100644 index 00000000..486c231c --- /dev/null +++ b/patches/7670.diff @@ -0,0 +1,2037 @@ +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/7733.diff b/patches/7733.diff new file mode 100644 index 00000000..70f1d590 --- /dev/null +++ b/patches/7733.diff @@ -0,0 +1,116 @@ +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..a7c6ec974b20 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 +@@ -94,16 +94,29 @@ public class MultiSourceRootProvider implements ClassPathProvider { + 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; ++ } ++ ++ for (FileObject existingRoot : root2SourceCP.keySet()) { ++ 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) { +@@ -355,7 +368,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)); +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..7d3ab2d57a4c 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,7 +19,9 @@ + 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; +@@ -254,9 +256,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 +267,36 @@ 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;"); ++ } ++ ++ 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)); ++ 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(); diff --git a/patches/remove-db.diff b/patches/remove-db.diff index 844ce8b4..3ee12a3d 100644 --- a/patches/remove-db.diff +++ b/patches/remove-db.diff @@ -457,222 +457,6 @@ index e3a89225a1..0000000000 - return CompletableFuture.completedFuture(em); - } -} -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/db/DBConnectionProvider.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/db/DBConnectionProvider.java -deleted file mode 100644 -index 9da0614d30..0000000000 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/db/DBConnectionProvider.java -+++ /dev/null -@@ -1,210 +0,0 @@ --/* -- * 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.lsp.server.db; -- --import java.io.FileWriter; --import java.io.IOException; --import java.io.Writer; --import java.nio.charset.Charset; --import java.nio.file.DirectoryStream; --import java.nio.file.FileSystems; --import java.nio.file.Files; --import java.nio.file.LinkOption; --import java.nio.file.Path; --import java.nio.file.attribute.AclEntry; --import java.nio.file.attribute.AclEntryPermission; --import static java.nio.file.attribute.AclEntryPermission.*; --import java.nio.file.attribute.AclFileAttributeView; --import java.nio.file.attribute.DosFileAttributeView; --import java.nio.file.attribute.FileAttribute; --import java.nio.file.attribute.PosixFileAttributeView; --import java.nio.file.attribute.PosixFilePermission; --import static java.nio.file.attribute.PosixFilePermission.*; --import java.nio.file.attribute.PosixFilePermissions; --import java.util.Collections; --import java.util.EnumSet; --import java.util.HashMap; --import java.util.List; --import java.util.Map; --import java.util.Properties; --import java.util.Set; --import java.util.concurrent.CompletableFuture; --import java.util.logging.Level; --import java.util.logging.Logger; --import org.netbeans.api.db.explorer.ConnectionManager; --import org.netbeans.api.db.explorer.DatabaseConnection; --import org.netbeans.spi.lsp.CommandProvider; --import org.openide.util.lookup.ServiceProvider; -- --/** -- * -- * @author Jan Horvath -- */ --@ServiceProvider(service = CommandProvider.class) --public class DBConnectionProvider implements CommandProvider { -- private static final Logger LOG = Logger.getLogger(DBConnectionProvider.class.getName()); -- private static final String GET_DB_CONNECTION = "nbls.db.connection"; //NOI18N -- -- private static final boolean POSIX = FileSystems.getDefault().supportedFileAttributeViews().contains("posix"); // NOI18N -- private static final EnumSet readWritePosix = EnumSet.of(OWNER_READ, OWNER_WRITE); -- private static final EnumSet readOnlyAcl = EnumSet.of(READ_ACL, READ_ATTRIBUTES, WRITE_ATTRIBUTES, READ_DATA, READ_NAMED_ATTRS, DELETE, SYNCHRONIZE); -- -- // temporary directory location -- private static final Path tmpdir = Path.of(System.getProperty("java.io.tmpdir")); // NOI18N -- -- public DBConnectionProvider() { -- try { -- deleteOldFiles(generateDirPath()); -- } catch (IOException ex) { -- LOG.log(Level.SEVERE, "deleteOldFiles", ex); -- } -- } -- -- @Override -- public CompletableFuture runCommand(String command, List arguments) { -- Map result = new HashMap<> (); -- CompletableFuture ret = new CompletableFuture(); -- Properties dbProps = new Properties(); -- DatabaseConnection conn = ConnectionManager.getDefault().getPreferredConnection(true); -- -- if (conn != null) { -- Path temp = null; -- Path dir = generateDirPath(); -- -- try { -- if (!Files.isDirectory(dir, LinkOption.NOFOLLOW_LINKS)) { -- Files.createDirectory(dir); -- } -- if (POSIX) { -- FileAttribute readWriteAttribs = PosixFilePermissions.asFileAttribute(readWritePosix); --// FileAttribute readWriteAttribs = PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rw-------")); -- temp = Files.createTempFile(dir, "db-", ".properties", readWriteAttribs); // NOI18N -- } else { -- temp = Files.createTempFile(dir, "db-", ".properties"); // NOI18N -- AclFileAttributeView acl = Files.getFileAttributeView(temp, AclFileAttributeView.class); -- AclEntry ownerEntry = null; -- for(AclEntry e : acl.getAcl()) { -- if (e.principal().equals(acl.getOwner())) { -- ownerEntry = e; -- break; -- } -- } -- if (ownerEntry != null) { -- acl.setAcl(Collections.singletonList(ownerEntry)); -- } else { -- deleteTempFile(temp); -- ret.completeExceptionally(new IOException("Owner missing, file:"+temp.toString())); // NOI18N -- return ret; -- } -- } -- } catch (IOException ex) { -- deleteTempFile(temp); -- ret.completeExceptionally(ex); -- return ret; -- } -- -- try (Writer writer = new FileWriter(temp.toFile(), Charset.defaultCharset());) { -- dbProps.put("datasources.default.url", conn.getDatabaseURL()); //NOI18N -- dbProps.put("datasources.default.username", conn.getUser()); //NOI18N -- dbProps.put("datasources.default.password", conn.getPassword()); //NOI18N -- dbProps.put("datasources.default.driverClassName", conn.getDriverClass()); //NOI18N -- String ocid = (String) conn.getConnectionProperties().get("OCID"); //NOI18N -- if (ocid != null && !ocid.isEmpty()) { -- dbProps.put("datasources.default.ocid", ocid); //NOI18N -- } -- dbProps.store(writer, ""); -- if (POSIX) { -- PosixFileAttributeView attribs = Files.getFileAttributeView(temp, PosixFileAttributeView.class); -- attribs.setPermissions(EnumSet.of(OWNER_READ)); -- } else { -- DosFileAttributeView attribs = Files.getFileAttributeView(temp, DosFileAttributeView.class); -- attribs.setReadOnly(true); -- AclFileAttributeView acl = Files.getFileAttributeView(temp, AclFileAttributeView.class); -- AclEntry ownerEntry = null; -- if (acl.getAcl().size() != 1) { -- deleteTempFile(temp); -- ret.completeExceptionally(new IOException("Too many Acls, file:"+temp.toString())); // NOI18N -- return ret; -- } -- for(AclEntry e : acl.getAcl()) { -- if (e.principal().equals(acl.getOwner())) { -- ownerEntry = e; -- break; -- } -- } -- if (ownerEntry != null) { -- AclEntry readOnly = AclEntry.newBuilder(ownerEntry).setPermissions(readOnlyAcl).build(); -- acl.setAcl(Collections.singletonList(readOnly)); -- } else { -- deleteTempFile(temp); -- ret.completeExceptionally(new IOException("Owner missing, file:"+temp.toString())); // NOI18N -- return ret; -- } -- } -- temp.toFile().deleteOnExit(); -- result.put("MICRONAUT_CONFIG_FILES", temp.toAbsolutePath().toString()); // NOI18N -- } catch (IOException ex) { -- deleteTempFile(temp); -- ret.completeExceptionally(ex); -- return ret; -- } -- } -- -- ret.complete(result); -- return ret; -- } -- -- @Override -- public Set getCommands() { -- return Collections.singleton(GET_DB_CONNECTION); -- } -- -- private static Path generateDirPath() { -- String s = GET_DB_CONNECTION + "_" + System.getProperty("user.name"); // NOI18N -- Path name = tmpdir.getFileSystem().getPath(s); -- return tmpdir.resolve(name); -- } -- -- private static void deleteOldFiles(Path dir) throws IOException { -- if (Files.isDirectory(dir, LinkOption.NOFOLLOW_LINKS)) { -- try (DirectoryStream stream = Files.newDirectoryStream(dir)) { -- for (Path f : stream) { -- deleteTempFile(f); -- } -- } -- } -- } -- -- private static void deleteTempFile(Path temp) { -- if (temp != null && Files.isRegularFile(temp, LinkOption.NOFOLLOW_LINKS)) { -- try { -- if (POSIX) { -- PosixFileAttributeView attribs = Files.getFileAttributeView(temp, PosixFileAttributeView.class); -- attribs.setPermissions(readWritePosix); -- } else { -- DosFileAttributeView attribs = Files.getFileAttributeView(temp, DosFileAttributeView.class); -- attribs.setReadOnly(false); -- } -- Files.delete(temp); -- } catch (IOException ex) { -- LOG.log(Level.WARNING, "deleteTempFile", ex); -- } -- } -- } --} diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/db/DBDecorationProvider.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/db/DBDecorationProvider.java deleted file mode 100644 index e057526173..0000000000 diff --git a/vscode/package-lock.json b/vscode/package-lock.json index 7d9ceb47..6f1725a6 100644 --- a/vscode/package-lock.json +++ b/vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "oracle-java", - "version": "1.0.0", + "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "oracle-java", - "version": "1.0.0", + "version": "0.1.0", "license": "Apache 2.0", "dependencies": { "@vscode/debugadapter": "^1.65.0", diff --git a/vscode/package.json b/vscode/package.json index fbe9e64d..c1dd85a8 100644 --- a/vscode/package.json +++ b/vscode/package.json @@ -4,7 +4,7 @@ "description": "Java Platform Extension for Visual Studio Code", "author": "Oracle", "license": "Apache 2.0", - "version": "1.0.0", + "version": "0.1.0", "preview": false, "repository": { "type": "git", @@ -771,7 +771,7 @@ "nbjavac": "node ./out/nbcode.js -J-Dnetbeans.close=true --modules --install .*nbjavac.*", "apisupport": "node ./out/nbcode.js -J-Dnetbeans.close=true --modules --install '(org.netbeans.libs.xerces|org.netbeans.modules.editor.structure|org.netbeans.modules.xml|org.netbeans.modules.xml.axi|org.netbeans.modules.xml.retriever|org.netbeans.modules.xml.schema.model|org.netbeans.modules.xml.tax|org.netbeans.modules.xml.text|org.netbeans.modules.ant.browsetask|.*apisupport.*|org.netbeans.modules.debugger.jpda.ant)' && node ./out/nbcode.js -J-Dnetbeans.close=true --modules --enable .*apisupport.ant", "artifactory:check": "node ./esbuild.js --artifactory-check" - }, + }, "devDependencies": { "@types/glob": "^7.1.1", "@types/mocha": "^9.0.0", diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index 7cbd0611..5b0acf8b 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -391,7 +391,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { }); // register commands - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.workspace.new', async (ctx) => { + context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.workspace.new', async (ctx, template) => { let c : LanguageClient = await client; const commands = await vscode.commands.getCommands(); if (commands.includes(COMMAND_PREFIX + '.new.from.template')) { @@ -415,9 +415,14 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { return; } - // first give the context, then the open-file hint in the case the context is not specific enough - const res = await vscode.commands.executeCommand(COMMAND_PREFIX + '.new.from.template', contextUri(ctx)?.toString(), vscode.window.activeTextEditor?.document?.uri?.toString()); - + // first give the template (if present), then the context, and then the open-file hint in the case the context is not specific enough + const params = []; + if (typeof template === 'string') { + params.push(template); + } + params.push(contextUri(ctx)?.toString(), vscode.window.activeTextEditor?.document?.uri?.toString()); + const res = await vscode.commands.executeCommand(COMMAND_PREFIX + '.new.from.template', ...params); + if (typeof res === 'string') { let newFile = vscode.Uri.parse(res as string); await vscode.window.showTextDocument(newFile, { preview: false }); @@ -641,7 +646,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { name: "Java Single Debug", request: "launch" }; - if (!methodName) { + if (methodName) { debugConfig['methodName'] = methodName; } if (launchConfiguration == '') { From b1240948adda5849284920ab680374dfe81c18c0 Mon Sep 17 00:00:00 2001 From: Narendra Kumar Balyam Muralidhar Date: Fri, 6 Sep 2024 18:42:25 +0530 Subject: [PATCH 06/18] [JAVSCODE-253] localisation support - adding japanese and simplified chinese (#254) --- .gitignore | 1 + THIRD_PARTY_LICENSES.txt | 30 ++++++ build.xml | 1 + patches/l10n-liscence.diff | 27 ++++++ vscode/l10n/bundle.l10n.en.json | 92 ++++++++++++++++++ vscode/l10n/bundle.l10n.ja.json | 76 +++++++++++++++ vscode/l10n/bundle.l10n.zh-cn.json | 73 ++++++++++++++ vscode/package-lock.json | 12 +++ vscode/package.json | 148 +++++++++++++++-------------- vscode/package.nls.ja.json | 65 +++++++++++++ vscode/package.nls.json | 68 +++++++++++++ vscode/package.nls.zh-cn.json | 65 +++++++++++++ vscode/src/constants.ts | 1 + vscode/src/explorer.ts | 6 +- vscode/src/extension.ts | 94 ++++++++++-------- vscode/src/jdkDownloader.ts | 134 +++++++++++++++----------- vscode/src/launchConfigurations.ts | 7 +- vscode/src/localiser.ts | 35 +++++++ vscode/src/runConfiguration.ts | 13 +-- 19 files changed, 769 insertions(+), 179 deletions(-) create mode 100644 patches/l10n-liscence.diff create mode 100644 vscode/l10n/bundle.l10n.en.json create mode 100755 vscode/l10n/bundle.l10n.ja.json create mode 100755 vscode/l10n/bundle.l10n.zh-cn.json create mode 100755 vscode/package.nls.ja.json create mode 100644 vscode/package.nls.json create mode 100755 vscode/package.nls.zh-cn.json create mode 100644 vscode/src/localiser.ts diff --git a/.gitignore b/.gitignore index a96bb610..1d613e2e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build/ netbeans/ .DS_STORE +.idea/ diff --git a/THIRD_PARTY_LICENSES.txt b/THIRD_PARTY_LICENSES.txt index da054705..6f5884d3 100644 --- a/THIRD_PARTY_LICENSES.txt +++ b/THIRD_PARTY_LICENSES.txt @@ -10396,6 +10396,36 @@ SOFTWARE. ------------------ END OF DEPENDENCY LICENSE -------------------- +Dependency: VS Code tooling for localizing Visual Studio Code extensions +======================================================================== + +------------------ START OF DEPENDENCY LICENCE -------------------- +- @vscode/l10n + + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE + +------------------ END OF DEPENDENCY LICENCE -------------------- + Dependency: VS Code Debug Protocol and Debug Adapter ==================================================== diff --git a/build.xml b/build.xml index 03916758..3cb3cfcc 100644 --- a/build.xml +++ b/build.xml @@ -55,6 +55,7 @@ patches/rename-debugger.diff patches/remove-db.diff patches/nbjavac-not-required.diff + patches/l10n-liscence.diff diff --git a/patches/l10n-liscence.diff b/patches/l10n-liscence.diff new file mode 100644 index 00000000..bbffc65a --- /dev/null +++ b/patches/l10n-liscence.diff @@ -0,0 +1,27 @@ +diff --git a/nbbuild/misc/prepare-bundles/src/main/resources/org/netbeans/prepare/bundles/@vscode/l10n-0.0.18-license b/nbbuild/misc/prepare-bundles/src/main/resources/org/netbeans/prepare/bundles/@vscode/l10n-0.0.18-license +new file mode 100644 +index 0000000000..9e841e7a26 +--- /dev/null ++++ b/nbbuild/misc/prepare-bundles/src/main/resources/org/netbeans/prepare/bundles/@vscode/l10n-0.0.18-license +@@ -0,0 +1,21 @@ ++ MIT License ++ ++ Copyright (c) Microsoft Corporation. ++ ++ Permission is hereby granted, free of charge, to any person obtaining a copy ++ of this software and associated documentation files (the "Software"), to deal ++ in the Software without restriction, including without limitation the rights ++ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ copies of the Software, and to permit persons to whom the Software is ++ furnished to do so, subject to the following conditions: ++ ++ The above copyright notice and this permission notice shall be included in all ++ copies or substantial portions of the Software. ++ ++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ++ SOFTWARE diff --git a/vscode/l10n/bundle.l10n.en.json b/vscode/l10n/bundle.l10n.en.json new file mode 100644 index 00000000..8e48f790 --- /dev/null +++ b/vscode/l10n/bundle.l10n.en.json @@ -0,0 +1,92 @@ +{ + + "jdk.downloader.heading": "JDK Downloader", + + "jdk.downloader.html.details":"

This tool enables you to download either the latest Oracle Java SE JDK with Oracle No-Fee Terms and Conditions or the Oracle OpenJDK builds under the GNU Public License with ClassPath Exception

It will then handle the installation and configuration on your behalf.

This enables you to take full advantage of all the features offered by this extension.

", + + "jdk.downloader.button.label.oracleJdk": "Download Oracle Java SE JDK", + "jdk.downloader.label.or": "or", + "jdk.downloader.button.label.openJdk": "Download Oracle OpenJDK", + "jdk.downloader.button.label.selectJdkFromSystem": "Select installed JDK from my system", + "jdk.downloader.label.selectOracleJdkVersion": "Select Oracle Java SE Version", + "jdk.downloader.label.detectedOs": "Detected OS", + "jdk.downloader.label.windows": "Microsoft Windows", + "jdk.downloader.label.mac": "macOS", + "jdk.downloader.label.linux": "Linux", + "jdk.downloader.label.detectedMachineArchitecture": "Detected Machine Architecture", + "jdk.downloader.button.label.downloadAndInstall": "Install and start setup", + "jdk.downloader.label.selectOpenJdkVersion": "Select Oracle OpenJDK Version", + "jdk.downloader.message.downloadingAndCompletingSetup": "Downloading and completing setup of {jdkType} {jdkVersion}...", + "jdk.downloader.message.downloadCompleted":"{jdkType} {jdkVersion} for {osType} download completed!", + "jdk.downloader.error_message.whileSavingFile": "Error while saving the file: {error}", + "jdk.downloader.error_message.whileDownloading": "Error while downloading the : {jdkType} {error}", + "jdk.downloader.message.downloadFailed": "{jdkType} {jdkVersion} for {osType} download failed, wrong checksum.", + "jdk.downloader.error_message.downloadFailedHttpError": "HTTP Error {statusCode} - {statusMessage}", + "jdk.downloader.error_message.jdkExtractionError": "Error: {error}", + "jdk.downloader.error_message.jdkNewDirectoryIssueCannotInstall":"Cannot install {jdkType} {jdkVersion}. Cannot delete {newDirName}", + "jdk.downloader.message.confirmation.directoryExistsStillWantToDelete":"{name} is already present. Do you want to delete it and create with new contents?", + "jdk.downloader.message.confirmation.yes":"Yes", + "jdk.downloader.message.confirmation.no":"No", + "jdk.downloader.label.selectJdk":"Select installed JDK", + "jdk.downloader.label.installJdk": "Install in selected location", + "jdk.downloader.message.noLocationSelected": "No location selected.", + "jdk.downloader.message.completedInstallingJdk": "Completed installing JDK. Please reload Visual Studio Code to enable it.", + "jdk.downloader.message.addedJdkPath": "Added JDK Path to the configuration settings. Please reload Visual Studio Code to enable it.", + "jdk.downloader.message.reload": "Reload now", + "jdk.explorer.error_message":"Cannot delete node {label}", + "jdk.extension.label.openInNewWindow": "Open in new window", + "jdk.extension.label.addToWorkSpace": "Add to current workspace", + "jdk.extension.message.newProjectCreated": "New project created", + "jdk.extension.fileSelector.label.selectFiles": "Select files to open", + "jdk.extension.fileSelector.label.testFilesOrSourceFiles": "Test files or source files associated to each other", + "jdk.extension.fileSelector.label.noFileSelected": "No file selected", + "jdk.extension.fileSelector.label.noTestFound": "No Test or Tested class found", + "jdk.extension.cache.message.confirmToDeleteCache": "Are you sure you want to delete cache for this workspace?", + "jdk.extension.cache.label.confirmation.yes":"Yes", + "jdk.extension.cache.label.confirmation.cancel":"Cancel", + "jdk.extension.cache.message.cacheCleared":"Cache cleared successfully for this workspace", + "jdk.extension.cache.label.reloadWindow":"Reload window", + "jdk.extension.cache.message.noUserDir":"Cannot find userdir path", + "jdk.extension.command.progress.compilingWorkSpace": "Compiling workspace...", + "jdk.extension.command.progress.compilingProject": "Compiling...", + "jdk.extension.command.progress.cleaningWorkSpace": "Cleaning workspace...", + "jdk.extension.command.progress.cleaningProject": "Cleaning...", + "jdk.extension.command.progress.quickOpen": "Opening type...", + "jdk.extension.command.quickPick.placeholder.surroundWith": "Surround with ...", + "jdk.extension.command.statusBar.message.restartingServer": "Restarting {SERVER_NAME}", + "jdk.extension.lspServer.statusBar.message.launching": "Launching {SERVER_NAME} with {requiredJdk} and userdir {userdir}", + "jdk.extension.lspServer.warning_message.serverExited": "{SERVER_NAME} exited with {code}", + "jdk.extension.lspServer.message.noJdkFound": "No JDK found!", + "jdk.extension.lspServer.label.downloadAndSetup": "Download JDK and setup automatically", + "jdk.extension.lspServer.error_message": "Error initializing {reason}", + "jdk.extension.nbjavac.message.supportedVersionRequired": "Supported version of javac needed. Please either enable the nb-javac library, or use JDK 22+", + "jdk.extension.nbjavac.label.enableNbjavac": "Enable the nb-javac library", + "jdk.extension.nbjavac.label.openSettings": "Open settings", + "jdk.extension.javaSupport.label.installGpl": "Install GPLv2+CPEx code", + "jdk.extension.javaSupport.message.needAdditionalSupport": "Additional Java Support is needed", + "jdk.extension.runConfig.label.updateExistingLaunchJson": "Update the existing launch.json file(s)", + "jdk.extension.runConfig.warning_message.renamedDebugConfig": "Java 8+ debug configuration has been renamed to Java+", + "jdk.extension.runConfig.default.label":"", + "jdk.extension.runConfig.example.label":"Example: {data}", + "jdk.extension.runConfig.arguments.label": "Arguments:", + "jdk.extension.runConfig.arguments.prompt": "Customize arguments", + "jdk.extension.runConfig.vmoptions.label": "VM Options:", + "jdk.extension.runConfig.vmoptions.prompt": "Customize VM options", + "jdk.extension.runConfig.env.label": "Environment:", + "jdk.extension.runConfig.env.prompt": "Customize environment variables", + "jdk.extension.runConfig.wrkdir.label": "Working Dir:", + "jdk.extension.runConfig.wrkdir.prompt": "Customize working directory", + "jdk.extenstion.notInstalled.label":"Extension not installed.", + "jdk.extenstion.error_msg.clientNotAvailable":"Client not available", + "jdk.extenstion.progressBar.error_msg.cannotRun":"cannot run ${lsCommand}; client is ${client}", + "jdk.extenstion.error_msg.doesntSupportNewTeamplate":"Client ${client} doesn't support new from template", + "jdk.extenstion.error_msg.doesntSupportNewProject":"Client ${client} doesn't support new project", + "jdk.extenstion.error_msg.doesntSupportGoToTest":"Client ${client} doesn't support go to test", + "jdk.extenstion.error_msg.noSuperImpl":"No super implementation found", + "jdk.extenstion.error_msg.cacheDeletionError":"Error deleting the cache", + "jdk.extenstion.message.cacheDeleted":"Cache deleted successfully", + "jdk.extenstion.cache.error_msg.cannotFindWrkSpacePath":"Cannot find workspace path", + "jdk.extenstion.debugger.error_msg.debugAdapterNotInitialized":"Oracle Java SE Debug Server Adapter not yet initialized. Please wait for a while and try again.", + "jdk.workspace.new.prompt": "Input the directory path where the new file will be generated" + + } diff --git a/vscode/l10n/bundle.l10n.ja.json b/vscode/l10n/bundle.l10n.ja.json new file mode 100755 index 00000000..e51ff887 --- /dev/null +++ b/vscode/l10n/bundle.l10n.ja.json @@ -0,0 +1,76 @@ +{ + + "jdk.downloader.heading": "JDKダウンローダ", + + "jdk.downloader.html.details":"

このツールは、Oracle No-Fee Terms and Conditionsの最新のOracle Java SE JDKまたは、クラスパス例外付きGNU Public Licenseに基づいたOracle OpenJDKビルドのいずれかをダウンロードできます

次に、インストールおよび構成をかわりに処理します。

これにより、この拡張によって提供されたすべての機能を最大限活用できます。

", + + "jdk.downloader.button.label.oracleJdk": "Oracle Java SE JDKのダウンロード", + "jdk.downloader.label.or": "または", + "jdk.downloader.button.label.openJdk": "Oracle OpenJDKのダウンロード", + "jdk.downloader.button.label.selectJdkFromSystem": "システムからインストール済JDKの選択", + "jdk.downloader.label.selectOracleJdkVersion": "Oracle Java SEバージョンの選択", + "jdk.downloader.label.detectedOs": "検出済OS", + "jdk.downloader.label.windows": "Microsoft Windows", + "jdk.downloader.label.mac": "macOS", + "jdk.downloader.label.linux": "Linux", + "jdk.downloader.label.detectedMachineArchitecture": "検出済マシン・アーキテクチャ", + "jdk.downloader.button.label.downloadAndInstall": "インストールおよび設定の開始", + "jdk.downloader.label.selectOpenJdkVersion": "Oracle OpenJDKバージョンの選択", + "jdk.downloader.message.downloadingAndCompletingSetup": "{jdkType} {jdkVersion}の設定をダウンロードおよび完了しています...", + "jdk.downloader.error_message.whileSavingFile": "ファイルの保存中にエラーが発生しました:{error}", + "jdk.downloader.error_message.whileDownloading": "ダウンロード中にエラーが発生しました: {jdkType} {error}", + "jdk.downloader.message.downloadFailed": "{osType}用の{jdkType} {jdkVersion}のダウンロードに失敗しました。間違ったチェックサム。", + "jdk.downloader.error_message.downloadFailedHttpError": "HTTPエラー{statusCode} - {statusMessage}", + "jdk.downloader.error_message.jdkExtractionError": "エラー: {error}", + "jdk.downloader.error_message.jdkNewDirectoryIssueCannotInstall":"{jdkType} {jdkVersion}をインストールできません。{newDirName}を削除できません", + "jdk.downloader.message.confirmation.directoryExistsStillWantToDelete":"{name}はすでに存在しています。削除して新しいコンテンツを作成しますか。", + "jdk.downloader.message.confirmation.yes":"はい", + "jdk.downloader.message.confirmation.no":"いいえ", + "jdk.downloader.label.selectJdk":"インストール済JDKの選択", + "jdk.downloader.label.installJdk": "選択した場所にインストール", + "jdk.downloader.message.noLocationSelected": "場所が選択されていません", + "jdk.downloader.message.completedInstallingJdk": "JDKのインストールが完了しました。有効にするには、Visual Studio Codeをリロードしてください。", + "jdk.downloader.message.addedJdkPath": "構成設定にJDKパスを追加しました。有効にするには、Visual Studio Codeをリロードしてください。", + "jdk.downloader.message.reload": "すぐにリロード", + "jdk.explorer.error_message":"ノード{label}を削除できません", + "jdk.extension.label.openInNewWindow": "新規ウィンドウで開く", + "jdk.extension.label.addToWorkSpace": "現在のワークスペースに追加", + "jdk.extension.message.newProjectCreated": "作成された新規プロジェクト", + "jdk.extension.fileSelector.label.selectFiles": "開くファイルを選択", + "jdk.extension.fileSelector.label.testFilesOrSourceFiles": "互いに関連付けられているテスト・ファイルまたはソース・ファイル", + "jdk.extension.fileSelector.label.noFileSelected": "ファイルが選択されていません", + "jdk.extension.fileSelector.label.noTestFound": "テストまたはテスト済のクラスが見つかりません", + "jdk.extension.cache.message.confirmToDeleteCache": "このワークスペースのキャッシュを削除しますか。", + "jdk.extension.cache.label.confirmation.yes":"はい", + "jdk.extension.cache.label.confirmation.cancel":"取消", + "jdk.extension.cache.message.cacheCleared":"このワークスペースのキャッシュは正常に消去されました", + "jdk.extension.cache.label.reloadWindow":"ウィンドウのリロード", + "jdk.extension.cache.message.noUserDir":"userdirパスが見つかりません", + "jdk.extension.command.progress.compilingWorkSpace": "ワークスペースのコンパイル中...", + "jdk.extension.command.progress.compilingProject": "コンパイル中...", + "jdk.extension.command.progress.cleaningWorkSpace": "ワークスペースのクリーニング...", + "jdk.extension.command.progress.cleaningProject": "クリーニング...", + "jdk.extension.command.progress.quickOpen": "タイプを開く...", + "jdk.extension.command.quickPick.placeholder.surroundWith": "囲む...", + "jdk.extension.command.statusBar.message.restartingServer": "{SERVER_NAME}の再起動中", + "jdk.extension.lspServer.statusBar.message.launching": "{requiredJdk}およびuserdir {userdir}を使用して{SERVER_NAME}を起動", + "jdk.extension.lspServer.warning_message.serverExited": "{SERVER_NAME}は{code}で終了しました", + "jdk.extension.lspServer.message.noJdkFound": "JDKが見つかりません!", + "jdk.extension.lspServer.label.downloadAndSetup": "JDKをダウンロードして自動的に設定", + "jdk.extension.lspServer.error_message": "初期化中にエラーが発生しました {reason}", + "jdk.extension.nbjavac.message.supportedVersionRequired": "サポートされているバージョンのjavacが必要です。nb-javacライブラリを有効にするか、JDK 22+を使用してください", + "jdk.extension.nbjavac.label.enableNbjavac": "nb-javacライブラリの有効化", + "jdk.extension.nbjavac.label.openSettings": "設定を開く", + "jdk.extension.javaSupport.label.installGpl": "GPLv2+CPExコードのインストール", + "jdk.extension.javaSupport.message.needAdditionalSupport": "追加のJavaサポートが必要です", + "jdk.extension.runConfig.label.updateExistingLaunchJson": "既存のlaunch.jsonファイルの更新", + "jdk.extension.runConfig.warning_message.renamedDebugConfig": "Java 8+デバッグ構成はJava+に名前変更されています", + "jdk.extension.runConfig.arguments.label": "引数:", + "jdk.extension.runConfig.arguments.prompt": "引数のカスタマイズ", + "jdk.extension.runConfig.vmoptions.label": "VMオプション:", + "jdk.extension.runConfig.vmoptions.prompt": "VMオプションのカスタマイズ", + "jdk.extension.runConfig.env.label": "環境:", + "jdk.extension.runConfig.env.prompt": "環境変数のカスタマイズ", + "jdk.extension.runConfig.wrkdir.label": "作業ディレクトリ:", + "jdk.extension.runConfig.wrkdir.prompt": "作業ディレクトリのカスタマイズ" + } \ No newline at end of file diff --git a/vscode/l10n/bundle.l10n.zh-cn.json b/vscode/l10n/bundle.l10n.zh-cn.json new file mode 100755 index 00000000..f11a93f2 --- /dev/null +++ b/vscode/l10n/bundle.l10n.zh-cn.json @@ -0,0 +1,73 @@ +{ + "jdk.downloader.heading": "JDK 下载程序", + "jdk.downloader.html.details":"

使用此工具,您可以遵循 Oracle 免费条款和条件下载最新的 Oracle Java SE JDK,或者依照 GNU 公共许可证(包含 ClassPath 例外条款)下载 Oracle OpenJDK 工作版本

之后,它将代表您处理安装和配置。

这样,您可以充分利用此扩展提供的所有功能。

", + "jdk.downloader.button.label.oracleJdk": "下载 Oracle Java SE JDK", + "jdk.downloader.label.or": "或", + "jdk.downloader.button.label.openJdk": "下载 Oracle OpenJDK", + "jdk.downloader.button.label.selectJdkFromSystem": "从我的系统选择安装的 JDK", + "jdk.downloader.label.selectOracleJdkVersion": "选择 Oracle Java SE 版本", + "jdk.downloader.label.detectedOs": "检测到的操作系统", + "jdk.downloader.label.windows": "Microsoft Windows", + "jdk.downloader.label.mac": "macOS", + "jdk.downloader.label.linux": "Linux", + "jdk.downloader.label.detectedMachineArchitecture": "检测到的计算机体系结构", + "jdk.downloader.button.label.downloadAndInstall": "安装并启动设置", + "jdk.downloader.label.selectOpenJdkVersion": "选择 Oracle OpenJDK 版本", + "jdk.downloader.message.downloadingAndCompletingSetup": "正在下载 {jdkType} {jdkVersion} 并完成其设置...", + "jdk.downloader.error_message.whileSavingFile": "保存文件时出错:{error}", + "jdk.downloader.error_message.whileDownloading": "下载 {jdkType} 时出错 {error}", + "jdk.downloader.message.downloadFailed": "适用于 {osType} 的 {jdkType} {jdkVersion} 下载失败,校验和错误。", + "jdk.downloader.error_message.downloadFailedHttpError": "HTTP 错误 {statusCode} - {statusMessage}", + "jdk.downloader.error_message.jdkExtractionError": "错误:{error}", + "jdk.downloader.error_message.jdkNewDirectoryIssueCannotInstall":"无法安装 {jdkType} {jdkVersion}。无法删除 {newDirName}", + "jdk.downloader.message.confirmation.directoryExistsStillWantToDelete":"{name} 已存在。是否要将其删除并使用新内容创建?", + "jdk.downloader.message.confirmation.yes":"是", + "jdk.downloader.message.confirmation.no":"否", + "jdk.downloader.label.selectJdk":"选择安装的 JDK", + "jdk.downloader.label.installJdk": "安装在所选位置", + "jdk.downloader.message.noLocationSelected": "未选择位置。", + "jdk.downloader.message.completedInstallingJdk": "已完成 JDK 安装。请重新加载 Visual Studio Code 来启用它。", + "jdk.downloader.message.addedJdkPath": "已将 JDK 路径添加到配置设置。请重新加载 Visual Studio Code 来启用它。", + "jdk.downloader.message.reload": "立即重新加载", + "jdk.explorer.error_message":"无法删除节点 {label}", + "jdk.extension.label.openInNewWindow": "在新窗口中打开", + "jdk.extension.label.addToWorkSpace": "添加到当前工作区", + "jdk.extension.message.newProjectCreated": "已创建新项目", + "jdk.extension.fileSelector.label.selectFiles": "选择要打开的文件", + "jdk.extension.fileSelector.label.testFilesOrSourceFiles": "相互关联的测试文件或源文件", + "jdk.extension.fileSelector.label.noFileSelected": "未选择任何文件", + "jdk.extension.fileSelector.label.noTestFound": "找不到测试或测试的类", + "jdk.extension.cache.message.confirmToDeleteCache": "是否确实要删除此工作区的高速缓存?", + "jdk.extension.cache.label.confirmation.yes":"是", + "jdk.extension.cache.label.confirmation.cancel":"取消", + "jdk.extension.cache.message.cacheCleared":"已成功清除此工作区的高速缓存", + "jdk.extension.cache.label.reloadWindow":"重新加载窗口", + "jdk.extension.cache.message.noUserDir":"找不到 userdir 路径", + "jdk.extension.command.progress.compilingWorkSpace": "正在编译工作区...", + "jdk.extension.command.progress.compilingProject": "正在编译...", + "jdk.extension.command.progress.cleaningWorkSpace": "正在清除工作区...", + "jdk.extension.command.progress.cleaningProject": "正在清除...", + "jdk.extension.command.progress.quickOpen": "正在打开类型...", + "jdk.extension.command.quickPick.placeholder.surroundWith": "包含方式...", + "jdk.extension.command.statusBar.message.restartingServer": "正在重新启动 {SERVER_NAME}", + "jdk.extension.lspServer.statusBar.message.launching": "正在启动 {SERVER_NAME}(包含 {requiredJdk},userdir 为 {userdir})", + "jdk.extension.lspServer.warning_message.serverExited": "已退出 {SERVER_NAME},代码为 {code}", + "jdk.extension.lspServer.message.noJdkFound": "未找到 JDK!", + "jdk.extension.lspServer.label.downloadAndSetup": "自动下载 JDK 并进行设置", + "jdk.extension.lspServer.error_message": "初始化时出错:{reason}", + "jdk.extension.nbjavac.message.supportedVersionRequired": "需要支持的 javac 版本。请启用 nb-javac 库或使用 JDK 22+", + "jdk.extension.nbjavac.label.enableNbjavac": "启用 nb-javac 库", + "jdk.extension.nbjavac.label.openSettings": "打开设置", + "jdk.extension.javaSupport.label.installGpl": "安装 GPLv2+CPEx 代码", + "jdk.extension.javaSupport.message.needAdditionalSupport": "需要其他 Java 支持", + "jdk.extension.runConfig.label.updateExistingLaunchJson": "更新现有 launch.json 文件", + "jdk.extension.runConfig.warning_message.renamedDebugConfig": "Java 8+ 调试配置已重命名为 Java+", + "jdk.extension.runConfig.arguments.label": "参数:", + "jdk.extension.runConfig.arguments.prompt": "定制参数", + "jdk.extension.runConfig.vmoptions.label": "VM 选项:", + "jdk.extension.runConfig.vmoptions.prompt": "定制 VM 选项", + "jdk.extension.runConfig.env.label": "环境:", + "jdk.extension.runConfig.env.prompt": "定制环境变量", + "jdk.extension.runConfig.wrkdir.label": "工作目录:", + "jdk.extension.runConfig.wrkdir.prompt": "定制工作目录" + } \ No newline at end of file diff --git a/vscode/package-lock.json b/vscode/package-lock.json index 7d9ceb47..b6e16164 100644 --- a/vscode/package-lock.json +++ b/vscode/package-lock.json @@ -10,6 +10,7 @@ "license": "Apache 2.0", "dependencies": { "@vscode/debugadapter": "^1.65.0", + "@vscode/l10n": "^0.0.18", "@vscode/webview-ui-toolkit": "^1.2.2", "axios": "^1.7.4", "jsonc-parser": "3.3.1", @@ -490,6 +491,12 @@ "resolved": "https://registry.npmjs.org/@vscode/debugprotocol/-/debugprotocol-1.65.0.tgz", "integrity": "sha512-ejerrPMBXzYms6Ks+Gb7cdXtdncmT0xwIKNsc0c/SxhEa0HVY5jdvLUegYE91p7CQJpCnXOD/r2CvViN8txLLA==" }, + "node_modules/@vscode/l10n": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz", + "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==", + "license": "MIT" + }, "node_modules/@vscode/webview-ui-toolkit": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@vscode/webview-ui-toolkit/-/webview-ui-toolkit-1.2.2.tgz", @@ -1988,6 +1995,11 @@ "resolved": "https://registry.npmjs.org/@vscode/debugprotocol/-/debugprotocol-1.65.0.tgz", "integrity": "sha512-ejerrPMBXzYms6Ks+Gb7cdXtdncmT0xwIKNsc0c/SxhEa0HVY5jdvLUegYE91p7CQJpCnXOD/r2CvViN8txLLA==" }, + "@vscode/l10n": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz", + "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==" + }, "@vscode/webview-ui-toolkit": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@vscode/webview-ui-toolkit/-/webview-ui-toolkit-1.2.2.tgz", diff --git a/vscode/package.json b/vscode/package.json index fbe9e64d..a5d9689f 100644 --- a/vscode/package.json +++ b/vscode/package.json @@ -4,7 +4,7 @@ "description": "Java Platform Extension for Visual Studio Code", "author": "Oracle", "license": "Apache 2.0", - "version": "1.0.0", + "version": "0.1.0", "preview": false, "repository": { "type": "git", @@ -43,6 +43,7 @@ "onDebugDynamicConfigurations" ], "main": "./out/extension.js", + "l10n": "./l10n", "contributes": { "languages": [ { @@ -95,13 +96,13 @@ "explorer": [ { "id": "foundProjects", - "name": "Projects", + "name": "%jdk.views.explorer.projects%", "when": "nbJdkReady" }, { "id": "run-config", - "name": "Run Configuration", - "contextualTitle": "Run Configuration", + "name": "%jdk.views.run.config%", + "contextualTitle": "%jdk.views.run.config%", "when": "runConfigurationInitialized" } ] @@ -116,7 +117,7 @@ "null" ], "default": null, - "description": "Specifies the JDK on which the Oracle Visual Studio Code Extension is run", + "description": "%jdk.configuration.specifyJdk.description%", "scope": "machine-overridable" }, "jdk.project.jdkhome": { @@ -125,61 +126,61 @@ "null" ], "default": null, - "description": "Specifies the JDK on which user's project will be run. Defaults to the value of jdk.jdkhome", + "description": "%jdk.configuration.specifyProjectJdk.description%", "scope": "machine-overridable" }, "jdk.verbose": { "type": "boolean", "default": false, - "description": "Enables verbose messages from the Oracle Visual Studio Code Extension" + "description": "%jdk.configuration.verbose.description%" }, "jdk.userdir": { - "description": "Keep settings and caches as 'global' or 'local' per workspace?", + "description": "%jdk.configuration.userdir.description%", "type": "string", "enum": [ "global", "local" ], "enumDescriptions": [ - "Share data between all workspaces (more effective)", - "Each workspace has its own data (more isolated)" + "%jdk.configuration.userdir.description.shareData%", + "%jdk.configuration.userdir.description.isolated%" ], "default": "local", - "deprecationMessage": "Separate userdir for each workspace is the default behaviour" + "deprecationMessage": "%jdk.configuration.userdir.deprecationMessage%" }, "jdk.revealActiveInProjects": { "type": "boolean", "default": true, - "description": "Reveals active text editor in Projects view" + "description": "%jdk.configuration.revealActiveInProjects.description%" }, "jdk.test.editor.enableShortcuts": { "type": "boolean", "default": false, - "description": "Enable Run/Debug test in editor" + "description": "%jdk.configuration.testEditor.description%" }, "jdk.javadoc.load.timeout": { "type": "integer", "default": 100, - "description": "Timeout (in milliseconds) for loading Javadoc in code completion (-1 for unlimited)" + "description": "%jdk.configuration.javadoc.timeout.description%" }, "jdk.format.settingsPath": { "type": "string", - "description": "Path to the file containing exported formatter settings", + "description": "%jdk.configuration.formatterSettings.description%", "default": null }, "jdk.hints.preferences": { "type": "string", - "description": "Path to the file containing exported hints preferences", + "description": "%jdk.configuration.hints.preferences.description%", "default": null }, "jdk.java.onSave.organizeImports": { "type": "boolean", "default": true, - "description": "Enable organize imports action on a document save" + "description": "%jdk.configuration.organiseImports.description%" }, "jdk.java.imports.groups": { "type": "array", - "description": "Groups of import statements (specified by their package prefixes) and their sorting order. Import statements within a group are ordered alphabetically", + "description": "%jdk.configuration.organiseImports.sortingOrder.description%", "default": [ "java", "javax", @@ -190,46 +191,46 @@ }, "jdk.java.imports.countForUsingStarImport": { "type": "integer", - "description": "Class count to use a star-import", + "description": "%jdk.configuration.countForUsingStarImport.description%", "default": 999, "minimum": 1 }, "jdk.java.imports.countForUsingStaticStarImport": { "type": "integer", - "description": "Members count to use a static star-import", + "description": "%jdk.configuration.countForUsingStaticStarImport.description%", "default": 999, "minimum": 1 }, "jdk.runConfig.arguments": { "type": "string", "default": "", - "description": "Arguments" + "description": "%jdk.configuration.arguments.description%" }, "jdk.runConfig.vmOptions": { "type": "string", "default": "", - "description": "VM options" + "description": "%jdk.configuration.vmOptions.description%" }, "jdk.serverVmOptions": { "type": "array", "default": [], - "description": "Specifies extra VM arguments used to launch the Java Language Server", + "description": "%jdk.configuration.serverVmOptions.description%", "scope": "machine-overridable" }, "jdk.runConfig.env": { "type": "string", "default": "", - "description": "Environment variables" + "description": "%jdk.configuration.runConfig.env.description%" }, "jdk.runConfig.cwd": { "type": "string", "default": "", - "description": "Working directory" + "description": "%jdk.configuration.runConfig.cwd.description%" }, "jdk.advanced.disable.nbjavac": { "type": "boolean", "default": false, - "description": "Advanced option: disable nb-javac library, javac from the selected JDK will be used. The selected JDK must be at least JDK 22." + "description": "%jdk.configuration.disableNbJavac.description%" } } }, @@ -254,7 +255,7 @@ "properties": { "mainClass": { "type": "string", - "description": "Absolute path to the program main class.", + "description": "%jdk.debugger.configuration.mainClass.description%", "default": "${file}" }, "classPaths": { @@ -262,7 +263,7 @@ "items": { "type": "string" }, - "description": "The classpaths for launching the JVM.", + "description": "%jdk.debugger.configuration.classPaths.description%", "default": [] }, "console": { @@ -270,7 +271,7 @@ "enum": [ "internalConsole" ], - "description": "The specified console to launch the program.", + "description": "%jdk.debugger.configuration.console.description%", "default": "internalConsole" }, "args": { @@ -278,7 +279,7 @@ "string", "null" ], - "description": "Arguments for the executed class", + "description": "%jdk.debugger.configuration.args.description%", "default": null }, "vmArgs": { @@ -286,7 +287,7 @@ "string", "null" ], - "description": "Arguments for the Java VM", + "description": "%jdk.debugger.configuration.vmArgs.description%", "default": null }, "cwd": { @@ -294,14 +295,14 @@ "string", "null" ], - "description": "Working directory for the program execution", + "description": "%jdk.debugger.configuration.cwd.description%", "default": null }, "env": { "type": [ "object" ], - "description": "Environment variables for the program execution", + "description": "%jdk.debugger.configuration.env.description%", "default": {} }, "launchConfiguration": { @@ -309,7 +310,7 @@ "string", "null" ], - "description": "Mode and default behaviour for launch" + "description": "%jdk.debugger.configuration.launchConfiguration.description%" } } }, @@ -318,31 +319,31 @@ "hostName": { "type": "string", "default": "localhost", - "description": "Host name or IP address to which to attach" + "description": "%jdk.debugger.configuration.attach.hostName.description%" }, "port": { "type": "string", "default": "8000", - "description": "Port number to which to attach" + "description": "%jdk.debugger.configuration.attach.port.description%" }, "sharedMemoryName": { "type": "string", - "description": "Shared memory name of the debuggee" + "description": "%jdk.debugger.configuration.attach.sharedMemoryName.description%" }, "processId": { "type": "string", "default": "${command:jdk.java.attachDebugger.pickProcess}", - "description": "Process Id of the debuggee" + "description": "%jdk.debugger.configuration.attach.processId.description%" }, "listen": { "type": "string", "default": "false", - "description": "Listen for the debuggee to attach" + "description": "%jdk.debugger.configuration.attach.listen.description%" }, "timeout": { "type": "string", "default": "30000", - "description": "Timeout while waiting to attach" + "description": "%jdk.debugger.configuration.attach.timeout.description%" } } } @@ -351,17 +352,17 @@ { "type": "jdk", "request": "launch", - "name": "Launch Java App" + "name": "%jdk.initialConfigurations.launchJavaApp.name%" } ], "configurationSnippets": [ { - "label": "Java+: Launch Java Application", - "description": "Launch a Java Application in debug mode", + "label": "%jdk.configurationSnippets.label%", + "description": "%jdk.configurationSnippets.description%", "body": { "type": "jdk", "request": "launch", - "name": "Launch Java App" + "name": "%jdk.initialConfigurations.launchJavaApp.name%" } } ] @@ -370,132 +371,132 @@ "commands": [ { "command": "jdk.node.properties.edit", - "title": "Properties" + "title": "%jdk.node.properties.edit%" }, { "command": "jdk.workspace.compile", - "title": "Compile Workspace", + "title": "%jdk.workspace.compile%", "category": "Java" }, { "command": "jdk.workspace.clean", - "title": "Clean Workspace", + "title": "%jdk.workspace.clean%", "category": "Java" }, { "command": "jdk.workspace.new", - "title": "New from Template...", + "title": "%jdk.workspace.new%", "category": "Java", "icon": "$(new-file)" }, { "command": "jdk.workspace.newproject", - "title": "New Project...", + "title": "%jdk.workspace.newproject%", "category": "Java", "icon": "$(new-folder)" }, { "command": "jdk.java.goto.super.implementation", - "title": "Go to Super Implementation", + "title": "%jdk.java.goto.super.implementation%", "category": "Java" }, { "command": "jdk.open.type", - "title": "Open Type...", + "title": "%jdk.open.type%", "category": "Java" }, { "command": "jdk.foundProjects.deleteEntry", - "title": "Delete" + "title": "%jdk.foundProjects.deleteEntry%" }, { "command": "jdk:Edit:org.openide.actions.DeleteAction", - "title": "Delete" + "title": "%jdk.Edit.org.openide.actions.DeleteAction%" }, { "command": "workbench.action.debug.run", - "title": "Run Without Debugging", + "title": "%workbench.action.debug.run%", "icon": "$(run)" }, { "command": "workbench.action.debug.start", - "title": "Start Debugging", + "title": "%workbench.action.debug.start%", "icon": "$(debug-alt)" }, { "command": "jdk.project.run", + "title": "%jdk.project.run%", "category": "Project", - "title": "Run Project Without Debugging", "icon": "$(run)" }, { "command": "jdk.project.debug", + "title": "%jdk.project.debug%", "category": "Project", - "title": "Debug Project", "icon": "$(debug-alt)" }, { "command": "jdk.project.test", + "title": "%jdk.project.test%", "category": "Project", - "title": "Test Project", "icon": "$(testing-run-all-icon)" }, { "command": "jdk.project.compile", - "category": "Project", - "title": "Compile Project" + "title": "%jdk.project.compile%", + "category": "Project" }, { "command": "jdk.project.clean", - "category": "Project", - "title": "Clean Project" + "title": "%jdk.project.clean%", + "category": "Project" }, { "command": "jdk.workspace.configureRunSettings", - "title": "Edit", + "title": "%jdk.workspace.configureRunSettings%", "icon": "$(edit)" }, { "command": "jdk.select.editor.projects", - "title": "Reveal active editor in Projects", + "title": "%jdk.select.editor.projects%", "category": "Project" }, { "command": "workbench.action.debug.run", - "title": "Run Without Debugging", + "title": "%workbench.action.debug.run%", "icon": "$(run)" }, { "command": "workbench.action.debug.start", - "title": "Start Debugging", + "title": "%workbench.action.debug.start%", "icon": "$(debug-alt)" }, { "command": "testing.runAll", - "title": "Run All Tests", + "title": "%testing.runAll%", "category": "Test" }, { "command": "jdk.addEventListener", - "title": "Add event listener" + "title": "%jdk.addEventListener%" }, { "command": "jdk.select.editor.projects", - "title": "Reveal active editor in Projects", + "title": "%jdk.select.editor.projects%", "category": "Project" }, { "command": "jdk.download.jdk", - "title": "Download, install and use JDK" + "title": "%jdk.download.jdk%" }, { "command": "jdk.open.test", - "title": "Go To Test/Tested class...", + "title": "%jdk.open.test%", "category": "Java" }, { "command": "jdk.delete.cache", - "title": "Delete Oracle Java extension cache for this workspace" + "title": "%jdk.delete.cache%" } ], "keybindings": [ @@ -771,7 +772,7 @@ "nbjavac": "node ./out/nbcode.js -J-Dnetbeans.close=true --modules --install .*nbjavac.*", "apisupport": "node ./out/nbcode.js -J-Dnetbeans.close=true --modules --install '(org.netbeans.libs.xerces|org.netbeans.modules.editor.structure|org.netbeans.modules.xml|org.netbeans.modules.xml.axi|org.netbeans.modules.xml.retriever|org.netbeans.modules.xml.schema.model|org.netbeans.modules.xml.tax|org.netbeans.modules.xml.text|org.netbeans.modules.ant.browsetask|.*apisupport.*|org.netbeans.modules.debugger.jpda.ant)' && node ./out/nbcode.js -J-Dnetbeans.close=true --modules --enable .*apisupport.ant", "artifactory:check": "node ./esbuild.js --artifactory-check" - }, + }, "devDependencies": { "@types/glob": "^7.1.1", "@types/mocha": "^9.0.0", @@ -789,6 +790,7 @@ }, "dependencies": { "@vscode/debugadapter": "^1.65.0", + "@vscode/l10n": "^0.0.18", "@vscode/webview-ui-toolkit": "^1.2.2", "axios": "^1.7.4", "jsonc-parser": "3.3.1", diff --git a/vscode/package.nls.ja.json b/vscode/package.nls.ja.json new file mode 100755 index 00000000..4a2ff124 --- /dev/null +++ b/vscode/package.nls.ja.json @@ -0,0 +1,65 @@ +{ + "jdk.node.properties.edit": "プロパティ", + "jdk.views.run.config": "実行構成", + "jdk.workspace.compile": "ワークスペースのコンパイル", + "jdk.workspace.clean": "ワークスペースの消去", + "jdk.workspace.new": "テンプレートから新規作成...", + "jdk.workspace.newproject": "新規プロジェクト...", + "jdk.java.goto.super.implementation": "スーパークラスの実装へ移動", + "jdk.open.type": "タイプを開く...", + "jdk.foundProjects.deleteEntry": "削除", + "jdk.Edit.org.openide.actions.DeleteAction": "削除", + "workbench.action.debug.run": "デバッグなしで実行", + "workbench.action.debug.start": "デバッグの開始", + "jdk.project.run": "デバッグなしでプロジェクトの実行", + "jdk.project.debug": "プロジェクトのデバッグ", + "jdk.project.test": "プロジェクトのテスト", + "jdk.project.compile": "プロジェクトのコンパイル", + "jdk.project.clean": "プロジェクトの消去", + "jdk.workspace.configureRunSettings": "編集", + "jdk.select.editor.projects": "プロジェクトのアクティブ・エディタを表示", + "testing.runAll": "すべてのテストの実行", + "jdk.addEventListener": "イベント・リスナーの追加", + "jdk.download.jdk": "JDKのダウンロード、インストールおよび使用", + "jdk.open.test": "テスト/テスト済のクラスへ移動...", + "jdk.delete.cache": "このワークスペースのOracle Java拡張キャッシュの削除", + "jdk.configuration.specifyJdk.description": "Oracle Visual Studio Code拡張機能のJDKを指定します", + "jdk.configuration.verbose.description": "Oracle Visual Studio Code拡張機能からの詳細メッセージを有効化します", + "jdk.configuration.userdir.description": "設定およびキャッシュをワークスペースごとにグローバルまたはローカルとして保持しますか。", + "jdk.configuration.userdir.description.shareData": "すべてのワークスペース間でデータを共有します(より効果的)", + "jdk.configuration.userdir.description.isolated": "各ワークスペースで独自のデータを保持します(より分離されている)", + "jdk.configuration.userdir.deprecationMessage": "各ワークスペースに個別のuserdirがデフォルト動作です", + "jdk.configuration.revealActiveInProjects.description": "プロジェクト・ビューにアクティブなテキスト・エディタを表示", + "jdk.configuration.testEditor.description": "エディタで実行/デバッグ・テストを有効にします", + "jdk.configuration.javadoc.timeout.description": "コード補完でのJavadocのロードのタイムアウト(ミリ秒)(無制限は-1)", + "jdk.configuration.formatterSettings.description": "エクスポート済のフォーマッタ設定を含むファイルへのパス", + "jdk.configuration.hints.preferences.description": "エクスポート済のヒント・プリファレンスを含むファイルへのパス", + "jdk.configuration.organiseImports.description": "ドキュメントの保存でインポートの編成アクションを有効化します", + "jdk.configuration.organiseImports.sortingOrder.description": "import文のグループ(パッケージ接頭辞により指定)およびソート順。グループ内のImport文はアルファベット順です", + "jdk.configuration.countForUsingStarImport.description": "スター・インポートを使用するクラス数", + "jdk.configuration.countForUsingStaticStarImport.description": "静的スター・インポートを使用するメンバー数", + "jdk.configuration.arguments.description": "引数", + "jdk.configuration.vmOptions.description": "VMオプション", + "jdk.configuration.serverVmOptions.description": "Java言語サーバーの起動に使用されるその他のVM引数を指定します", + "jdk.configuration.runConfig.env.description": "環境変数", + "jdk.configuration.runConfig.cwd.description": "作業ディレクトリ", + "jdk.configuration.disableNbJavac.description": "拡張オプション: nb-javacライブラリを無効化すると、選択したJDKからのjavacが使用されます。選択したJDKは少なくともJDK 22である必要があります。", + "jdk.debugger.configuration.mainClass.description": "プログラムのメイン・クラスへの絶対パス。", + "jdk.debugger.configuration.classPaths.description": "JVMの起動のためのクラスパス。", + "jdk.debugger.configuration.console.description": "プログラムを起動する指定されたコンソール。", + "jdk.debugger.configuration.args.description": "実行クラスの引数", + "jdk.debugger.configuration.vmArgs.description": "Java VMの引数", + "jdk.debugger.configuration.cwd.description": "プログラム実行の作業ディレクトリ", + "jdk.debugger.configuration.env.description": "プログラム実行の環境変数", + "jdk.debugger.configuration.launchConfiguration.description": "起動のモードおよびデフォルト動作", + "jdk.debugger.configuration.attach.hostName.description": "接続するホスト名またはIPアドレス", + "jdk.debugger.configuration.attach.port.description": "接続するポート番号", + "jdk.debugger.configuration.attach.sharedMemoryName.description": "デバッグ対象の共有メモリー名", + "jdk.debugger.configuration.attach.processId.description": "デバッグ対象のプロセスID", + "jdk.debugger.configuration.attach.listen.description": "接続するデバッグ対象をリスニング", + "jdk.debugger.configuration.attach.timeout.description": "接続を待つ間のタイムアウト", + "jdk.initialConfigurations.launchJavaApp.name": "Javaアプリケーションの起動", + "jdk.configurationSnippets.name": "Javaアプリケーションの起動", + "jdk.configurationSnippets.label": "Java+: Javaアプリケーションの起動", + "jdk.configurationSnippets.description": "デバッグ・モードでのJavaアプリケーションの起動" +} \ No newline at end of file diff --git a/vscode/package.nls.json b/vscode/package.nls.json new file mode 100644 index 00000000..e66bc8a1 --- /dev/null +++ b/vscode/package.nls.json @@ -0,0 +1,68 @@ +{ + "jdk.node.properties.edit": "Properties", + "jdk.views.run.config": "Run Configuration", + "jdk.views.explorer.projects": "Projects", + "jdk.workspace.compile": "Compile Workspace", + "jdk.workspace.clean": "Clean Workspace", + "jdk.workspace.new": "New from Template...", + "jdk.workspace.newproject": "New Project...", + "jdk.java.goto.super.implementation": "Go to Super Implementation", + "jdk.open.type": "Open Type...", + "jdk.foundProjects.deleteEntry": "Delete", + "jdk.Edit.org.openide.actions.DeleteAction": "Delete", + "workbench.action.debug.run": "Run Without Debugging", + "workbench.action.debug.start": "Start Debugging", + "jdk.project.run": "Run Project Without Debugging", + "jdk.project.debug": "Debug Project", + "jdk.project.test": "Test Project", + "jdk.project.compile": "Compile Project", + "jdk.project.clean": "Clean Project", + "jdk.workspace.configureRunSettings": "Edit", + "jdk.select.editor.projects": "Reveal active editor in Projects", + "testing.runAll": "Run All Tests", + "jdk.addEventListener": "Add event listener", + "jdk.download.jdk": "Download, install and use JDK", + "jdk.open.test": "Go To Test/Tested class...", + "jdk.delete.cache": "Delete Oracle Java extension cache for this workspace", + "jdk.configuration.specifyJdk.description": "Specifies JDK for the Oracle Visual Studio Code Extension", + "jdk.configuration.specifyProjectJdk.description": "Specifies the JDK on which user's project will be run. Defaults to the value of jdk.jdkhome", + "jdk.configuration.verbose.description": "Enables verbose messages from the Oracle Visual Studio Code Extension", + "jdk.configuration.userdir.description": "Keep settings and caches as 'global' or 'local' per workspace?", + "jdk.configuration.userdir.description.shareData": "Share data between all workspaces (more effective)", + "jdk.configuration.userdir.description.isolated": "Each workspace has its own data (more isolated)", + "jdk.configuration.userdir.deprecationMessage": "Separate userdir for each workspace is the default behaviour", + "jdk.configuration.revealActiveInProjects.description": "Reveals active text editor in Projects view", + "jdk.configuration.testEditor.description": "Enable Run/Debug test in editor", + "jdk.configuration.javadoc.timeout.description": "Timeout (in milliseconds) for loading Javadoc in code completion (-1 for unlimited)", + "jdk.configuration.formatterSettings.description": "Path to the file containing exported formatter settings", + "jdk.configuration.hints.preferences.description": "Path to the file containing exported hints preferences", + "jdk.configuration.organiseImports.description": "Enable organize imports action on a document save", + "jdk.configuration.organiseImports.sortingOrder.description": "Groups of import statements (specified by their package prefixes) and their sorting order. Import statements within a group are ordered alphabetically", + "jdk.configuration.countForUsingStarImport.description": "Class count to use a star-import", + "jdk.configuration.countForUsingStaticStarImport.description": "Members count to use a static star-import", + "jdk.configuration.arguments.description": "Arguments", + "jdk.configuration.vmOptions.description": "VM options", + "jdk.configuration.serverVmOptions.description": "Specifies extra VM arguments used to launch the Java Language Server", + "jdk.configuration.runConfig.env.description": "Environment variables", + "jdk.configuration.runConfig.cwd.description": "Working directory", + "jdk.configuration.disableNbJavac.description": "Advanced option: disable nb-javac library, javac from the selected JDK will be used. The selected JDK must be at least JDK 22.", + "jdk.debugger.configuration.mainClass.description": "Absolute path to the program main class.", + "jdk.debugger.configuration.classPaths.description": "The classpaths for launching the JVM.", + "jdk.debugger.configuration.console.description": "The specified console to launch the program.", + "jdk.debugger.configuration.args.description": "Arguments for the executed class", + "jdk.debugger.configuration.vmArgs.description": "Arguments for the Java VM", + "jdk.debugger.configuration.cwd.description": "Working directory for the program execution", + "jdk.debugger.configuration.env.description": "Environment variables for the program execution", + "jdk.debugger.configuration.launchConfiguration.description": "Mode and default behaviour for launch", + "jdk.debugger.configuration.attach.hostName.description": "Host name or IP address to which to attach", + "jdk.debugger.configuration.attach.port.description": "Port number to which to attach", + "jdk.debugger.configuration.attach.sharedMemoryName.description": "Shared memory name of the debuggee", + "jdk.debugger.configuration.attach.processId.description": "Process Id of the debuggee", + "jdk.debugger.configuration.attach.listen.description": "Listen for the debuggee to attach", + "jdk.debugger.configuration.attach.timeout.description": "Timeout while waiting to attach", + "jdk.initialConfigurations.launchJavaApp.name": "Launch Java App", + "jdk.configurationSnippets.name": "Launch Java App", + "jdk.configurationSnippets.label": "Java+: Launch Java Application", + "jdk.configurationSnippets.description": "Launch a Java Application in debug mode" + +} diff --git a/vscode/package.nls.zh-cn.json b/vscode/package.nls.zh-cn.json new file mode 100755 index 00000000..8489277f --- /dev/null +++ b/vscode/package.nls.zh-cn.json @@ -0,0 +1,65 @@ +{ + "jdk.node.properties.edit": "属性", + "jdk.views.run.config": "运行配置", + "jdk.workspace.compile": "编译工作区", + "jdk.workspace.clean": "清除工作区", + "jdk.workspace.new": "从模板新建...", + "jdk.workspace.newproject": "新建项目...", + "jdk.java.goto.super.implementation": "转至超级实现", + "jdk.open.type": "打开类型...", + "jdk.foundProjects.deleteEntry": "删除", + "jdk.Edit.org.openide.actions.DeleteAction": "删除", + "workbench.action.debug.run": "运行但不调试", + "workbench.action.debug.start": "启动调试", + "jdk.project.run": "运行项目但不调试", + "jdk.project.debug": "调试项目", + "jdk.project.test": "测试项目", + "jdk.project.compile": "编译项目", + "jdk.project.clean": "清除项目", + "jdk.workspace.configureRunSettings": "编辑", + "jdk.select.editor.projects": "在项目中显示活动编辑器", + "testing.runAll": "运行所有测试", + "jdk.addEventListener": "添加事件监听程序", + "jdk.download.jdk": "下载、安装和使用 JDK", + "jdk.open.test": "转至测试/测试的类...", + "jdk.delete.cache": "删除此工作区的 Oracle Java 扩展高速缓存", + "jdk.configuration.specifyJdk.description": "指定适用于 Oracle Visual Studio Code 扩展的 JDK", + "jdk.configuration.verbose.description": "启用来自 Oracle Visual Studio Code 扩展的详细消息", + "jdk.configuration.userdir.description": "将设置和高速缓存保留为“全局”还是按工作区保留为“本地”?", + "jdk.configuration.userdir.description.shareData": "在所有工作区之间共享数据(更有效)", + "jdk.configuration.userdir.description.isolated": "每个工作区都有自己的数据(更具隔离性)", + "jdk.configuration.userdir.deprecationMessage": "默认行为是每个工作区有单独的 userdir", + "jdk.configuration.revealActiveInProjects.description": "在项目视图中显示活动文本编辑器", + "jdk.configuration.testEditor.description": "在编辑器中启用运行/调试测试", + "jdk.configuration.javadoc.timeout.description": "在代码完成时加载 Javadoc 的超时(毫秒)(-1 表示无限制)", + "jdk.configuration.formatterSettings.description": "包含导出的格式化程序设置的文件的路径", + "jdk.configuration.hints.preferences.description": "包含导出的提示首选项的文件的路径", + "jdk.configuration.organiseImports.description": "允许在保存文档时进行组织导入操作", + "jdk.configuration.organiseImports.sortingOrder.description": "包含导入语句的组(由其程序包前缀指定)及其排序顺序。组中的导入语句按字母顺序排序", + "jdk.configuration.countForUsingStarImport.description": "使用星型导入的类计数", + "jdk.configuration.countForUsingStaticStarImport.description": "使用静态星型导入的成员计数", + "jdk.configuration.arguments.description": "参数", + "jdk.configuration.vmOptions.description": "VM 选项", + "jdk.configuration.serverVmOptions.description": "指定用于启动 Java Language Server 的额外 VM 参数", + "jdk.configuration.runConfig.env.description": "环境变量", + "jdk.configuration.runConfig.cwd.description": "工作目录", + "jdk.configuration.disableNbJavac.description": "高级选项:禁用 nb-javac 库,将使用来自所选 JDK 的 javac。所选 JDK 必须至少为 JDK 22。", + "jdk.debugger.configuration.mainClass.description": "程序主类的绝对路径。", + "jdk.debugger.configuration.classPaths.description": "用于启动 JVM 的类路径。", + "jdk.debugger.configuration.console.description": "用于启动程序的指定控制台。", + "jdk.debugger.configuration.args.description": "所执行类的参数", + "jdk.debugger.configuration.vmArgs.description": "Java VM 的参数", + "jdk.debugger.configuration.cwd.description": "程序执行的工作目录", + "jdk.debugger.configuration.env.description": "程序执行的环境变量", + "jdk.debugger.configuration.launchConfiguration.description": "启动的模式和默认行为", + "jdk.debugger.configuration.attach.hostName.description": "要附加到的主机名或 IP 地址", + "jdk.debugger.configuration.attach.port.description": "要附加到的端口号", + "jdk.debugger.configuration.attach.sharedMemoryName.description": "被调试程序的共享内存名称", + "jdk.debugger.configuration.attach.processId.description": "被调试程序的进程 ID", + "jdk.debugger.configuration.attach.listen.description": "监听要附加的被调试程序", + "jdk.debugger.configuration.attach.timeout.description": "等待附加操作时的超时", + "jdk.initialConfigurations.launchJavaApp.name": "启动 Java 应用程序", + "jdk.configurationSnippets.name": "启动 Java 应用程序", + "jdk.configurationSnippets.label": "Java+:启动 Java 应用程序", + "jdk.configurationSnippets.description": "以调试模式启动 Java 应用程序" +} \ No newline at end of file diff --git a/vscode/src/constants.ts b/vscode/src/constants.ts index 3fa5e83f..3d69516d 100644 --- a/vscode/src/constants.ts +++ b/vscode/src/constants.ts @@ -25,4 +25,5 @@ export const OPEN_JDK_VERSION_DOWNLOAD_LINKS: { [key: string]: string } = { "21": "https://download.java.net/java/GA/jdk21.0.2/f2283984656d49d69e91c558476027ac/13/GPL/openjdk-21.0.2" }; +export const ORACLE_VSCODE_EXTENSION_ID = 'oracle.oracle-java'; export const NODE_WINDOWS_LABEL = "Windows_NT"; diff --git a/vscode/src/explorer.ts b/vscode/src/explorer.ts index 96b3ea96..9d6b6137 100644 --- a/vscode/src/explorer.ts +++ b/vscode/src/explorer.ts @@ -21,7 +21,7 @@ import { ThemeIcon } from 'vscode'; import { LanguageClient } from 'vscode-languageclient/node'; import { NbLanguageClient } from './extension'; import { NodeChangedParams, NodeInfoNotification, NodeInfoRequest, GetResourceParams, NodeChangeType, NodeChangesParams } from './protocol'; - +import { l10n } from './localiser'; const doLog : boolean = false; const EmptyIcon = "EMPTY_ICON"; @@ -907,7 +907,9 @@ export function createTreeViewService(log : vscode.OutputChannel, c : NbLanguage let v = args as Visualizer; let ok = await c.sendRequest(NodeInfoRequest.destroy, { nodeId : v.data.id }); if (!ok) { - vscode.window.showErrorMessage('Cannot delete node ' + v.label); + vscode.window.showErrorMessage(l10n.value("jdk.explorer.error_message.cannotDeleteNode" , { + label:v.label + })); } }); const ts : TreeViewService = new TreeViewService(log, c, [ d ]); diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index 7cbd0611..7817bbc8 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -67,7 +67,8 @@ import { initializeRunConfiguration, runConfigurationProvider, runConfigurationN import { InputStep, MultiStepInput } from './utils'; import { PropertiesView } from './propertiesView/propertiesView'; import { openJDKSelectionView } from './jdkDownloader'; -import { NODE_WINDOWS_LABEL } from './constants'; +import { l10n } from './localiser'; +import { ORACLE_VSCODE_EXTENSION_ID,NODE_WINDOWS_LABEL } from './constants'; const API_VERSION : string = "1.0"; const SERVER_NAME : string = "Oracle Java SE Language Server"; export const COMMAND_PREFIX : string = "jdk"; @@ -151,13 +152,13 @@ export function awaitClient() : Promise { if (c && !(c instanceof InitialPromise)) { return c; } - let nbcode = vscode.extensions.getExtension('oracle.oracle-java'); + let nbcode = vscode.extensions.getExtension(ORACLE_VSCODE_EXTENSION_ID); if (!nbcode) { - return Promise.reject(new Error("Extension not installed.")); + return Promise.reject(new Error(l10n.value("jdk.extenstion.notInstalled.label"))); } const t : Thenable = nbcode.activate().then(nc => { if (client === undefined || client instanceof InitialPromise) { - throw new Error("Client not available"); + throw new Error(l10n.value("jdk.extenstion.error_msg.clientNotAvailable")); } else { return client; } @@ -312,7 +313,7 @@ function wrapCommandWithProgress(lsCommand : string, title : string, log? : vsco } } } else { - reject(`cannot run ${lsCommand}; client is ${c}`); + reject(l10n.value("jdk.extenstion.progressBar.error_msg.cannotRun",{lsCommand:lsCommand,client:c})); } }); }); @@ -400,7 +401,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { if(!workspaces) { const userHomeDir = os.homedir(); const folderPath = await vscode.window.showInputBox({ - prompt: "Input the directory path where the new file will be generated", + prompt: l10n.value('jdk.workspace.new.prompt'), value: `${userHomeDir}` }); if(!folderPath?.trim()) return; @@ -430,7 +431,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { } } } else { - throw `Client ${c} doesn't support new from template`; + throw l10n.value("jdk.extenstion.error_msg.doesntSupportNewTeamplate",{client:c}); } })); context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.workspace.newproject', async (ctx) => { @@ -441,10 +442,10 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { if (typeof res === 'string') { let newProject = vscode.Uri.parse(res as string); - const OPEN_IN_NEW_WINDOW = 'Open in new window'; - const ADD_TO_CURRENT_WORKSPACE = 'Add to current workspace'; + const OPEN_IN_NEW_WINDOW = l10n.value("jdk.extension.label.openInNewWindow"); + const ADD_TO_CURRENT_WORKSPACE = l10n.value("jdk.extension.label.addToWorkSpace"); - const value = await vscode.window.showInformationMessage('New project created', OPEN_IN_NEW_WINDOW, ADD_TO_CURRENT_WORKSPACE); + const value = await vscode.window.showInformationMessage(l10n.value("jdk.extension.message.newProjectCreated"), OPEN_IN_NEW_WINDOW, ADD_TO_CURRENT_WORKSPACE); if (value === OPEN_IN_NEW_WINDOW) { await vscode.commands.executeCommand('vscode.openFolder', newProject, true); } else if (value === ADD_TO_CURRENT_WORKSPACE) { @@ -452,7 +453,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { } } } else { - throw `Client ${c} doesn't support new project`; + throw l10n.value("jdk.extenstion.error_msg.doesntSupportNewProject",{client,c}); } })); @@ -489,8 +490,8 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { namePathMapping[fileName] = fp.file }); const selected = await window.showQuickPick(Object.keys(namePathMapping), { - title: 'Select files to open', - placeHolder: 'Test files or source files associated to each other', + title: l10n.value("jdk.extension.fileSelector.label.selectFiles"), + placeHolder: l10n.value("jdk.extension.fileSelector.label.testFilesOrSourceFiles"), canPickMany: true }); if (selected) { @@ -499,62 +500,65 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { await vscode.window.showTextDocument(file, { preview: false }); } } else { - vscode.window.showInformationMessage("No file selected"); + vscode.window.showInformationMessage(l10n.value("jdk.extension.fileSelector.label.noFileSelected")); } } } } catch (err:any) { - vscode.window.showInformationMessage(err?.message || "No Test or Tested class found"); + vscode.window.showInformationMessage(err?.message || l10n.value("jdk.extension.fileSelector.label.noTestFound")); } } else { - throw `Client ${c} doesn't support go to test`; + throw l10n.value("jdk.extenstion.error_msg.doesntSupportGoToTest",{client:c}); } })); context.subscriptions.push(vscode.commands.registerCommand(COMMAND_PREFIX + ".delete.cache", async () => { const storagePath = context.storageUri?.fsPath; if (!storagePath) { - vscode.window.showErrorMessage('Cannot find workspace path'); + vscode.window.showErrorMessage(l10n.value("jdk.extenstion.cache.error_msg.cannotFindWrkSpacePath")); return; } const userDir = path.join(storagePath, "userdir"); if (userDir && fs.existsSync(userDir)) { + const yes = l10n.value("jdk.extension.cache.label.confirmation.yes") + const cancel = l10n.value("jdk.extension.cache.label.confirmation.cancel") const confirmation = await vscode.window.showInformationMessage('Are you sure you want to delete cache for this workspace and reload the window ?', - 'Yes', 'Cancel'); - if (confirmation === 'Yes') { + yes, cancel); + if (confirmation === yes) { + const reloadWindowActionLabel = l10n.value("jdk.extension.cache.label.reloadWindow"); try { await stopClient(client); deactivated = true; await killNbProcess(false, log); await fs.promises.rmdir(userDir, { recursive: true }); - await vscode.window.showInformationMessage("Cache deleted successfully", 'Reload window'); + await vscode.window.showInformationMessage(l10n.value("jdk.extenstion.message.cacheDeleted"), reloadWindowActionLabel); } catch (err) { - await vscode.window.showErrorMessage('Error deleting the cache', 'Reload window'); + await vscode.window.showErrorMessage(l10n.value("jdk.extenstion.error_msg.cacheDeletionError"), reloadWindowActionLabel); } finally { vscode.commands.executeCommand("workbench.action.reloadWindow"); } } } else { - vscode.window.showErrorMessage('Cannot find userdir path'); + vscode.window.showErrorMessage(l10n.value("jdk.extension.cache.message.noUserDir")); } })); context.subscriptions.push(vscode.commands.registerCommand(COMMAND_PREFIX + ".download.jdk", async () => { openJDKSelectionView(log); })); context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.workspace.compile', () => - wrapCommandWithProgress(COMMAND_PREFIX + '.build.workspace', 'Compiling workspace...', log, true) + wrapCommandWithProgress(COMMAND_PREFIX + '.build.workspace', l10n.value('jdk.extension.command.progress.compilingWorkSpace'), log, true) )); context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.workspace.clean', () => - wrapCommandWithProgress(COMMAND_PREFIX + '.clean.workspace', 'Cleaning workspace...', log, true) + wrapCommandWithProgress(COMMAND_PREFIX + '.clean.workspace',l10n.value('jdk.extension.command.progress.cleaningWorkSpace'), log, true) )); context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.project.compile', (args) => { - wrapProjectActionWithProgress('build', undefined, 'Compiling...', log, true, args); + wrapProjectActionWithProgress('build', undefined, l10n.value('jdk.extension.command.progress.compilingProject'), log, true, args); })); context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.project.clean', (args) => { - wrapProjectActionWithProgress('clean', undefined, 'Cleaning...', log, true, args); + wrapProjectActionWithProgress('clean', undefined, l10n.value('jdk.extension.command.progress.cleaningProject'), log, true, args); })); context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.open.type', () => { - wrapCommandWithProgress(COMMAND_PREFIX + '.quick.open', 'Opening type...', log, true).then(() => { + wrapCommandWithProgress(COMMAND_PREFIX + '.quick.open', l10n.value('jdk.extension.command.progress.quickOpen'), log, true).then(() => { commands.executeCommand('workbench.action.focusActiveEditorGroup'); }); })); @@ -567,7 +571,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { const locations: any[] = await vscode.commands.executeCommand(COMMAND_PREFIX + '.java.super.implementation', uri.toString(), position) || []; return vscode.commands.executeCommand('editor.action.goToLocations', window.activeTextEditor.document.uri, position, locations.map(location => new vscode.Location(vscode.Uri.parse(location.uri), new vscode.Range(location.range.start.line, location.range.start.character, location.range.end.line, location.range.end.character))), - 'peek', 'No super implementation found'); + 'peek', l10n.value('jdk.extenstion.error_msg.noSuperImpl')); })); context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.rename.element.at', async (offset) => { const editor = window.activeTextEditor; @@ -579,7 +583,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { } })); context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.surround.with', async (items) => { - const selected: any = await window.showQuickPick(items, { placeHolder: 'Surround with ...' }); + const selected: any = await window.showQuickPick(items, { placeHolder: l10n.value('jdk.extension.command.quickPick.placeholder.surroundWith') }); if (selected) { if (selected.userData.edit) { const edit = await (await client).protocol2CodeConverter.asWorkspaceEdit(selected.userData.edit as ls.WorkspaceEdit); @@ -812,7 +816,7 @@ function killNbProcess(notifyKill : boolean, log : vscode.OutputChannel, specPro handleLog(log, "Request to kill LSP server."); if (p && (!specProcess || specProcess == p)) { if (notifyKill) { - vscode.window.setStatusBarMessage(`Restarting ${SERVER_NAME}.`, 2000); + vscode.window.setStatusBarMessage(l10n.value("jdk.extension.command.statusBar.message.restartingServer",{SERVER_NAME:SERVER_NAME}), 2000); } return new Promise((resolve, reject) => { nbProcess = null; @@ -919,7 +923,12 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex enableModules : enableModules, }; - let launchMsg = `Launching ${SERVER_NAME} with ${specifiedJDK ? specifiedJDK : 'default system JDK'} and userdir ${userdir}`; + const requiredJdk = specifiedJDK ? specifiedJDK : 'default system JDK'; + let launchMsg = l10n.value("jdk.extension.lspServer.statusBar.message.launching",{ + SERVER_NAME:SERVER_NAME, + requiredJdk:requiredJdk, + userdir:userdir + }); handleLog(log, launchMsg); vscode.window.setStatusBarMessage(launchMsg, 2000); @@ -965,14 +974,15 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex nbProcess = null; } if (p == nbProcess && code != 0 && code) { - vscode.window.showWarningMessage(`${SERVER_NAME} exited with ` + code); + vscode.window.showWarningMessage(l10n.value("jdk.extension.lspServer.warning_message.serverExited",{SERVER_NAME:SERVER_NAME,code:code})); } if (stdErr?.match(/Cannot find java/) || (os.type() === NODE_WINDOWS_LABEL && !deactivated) ) { + const downloadAndSetupActionLabel = l10n.value("jdk.extension.lspServer.label.downloadAndSetup") vscode.window.showInformationMessage( - "No JDK found!", - "Download JDK and setup automatically" + l10n.value("jdk.extension.lspServer.message.noJdkFound"), + downloadAndSetupActionLabel ).then( selection => { - if (selection === 'Download JDK and setup automatically') { + if (selection === downloadAndSetupActionLabel) { openJDKSelectionView(log); } }); @@ -1255,7 +1265,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex }).catch((reason) => { activationPending = false; handleLog(log, reason); - window.showErrorMessage('Error initializing ' + reason); + window.showErrorMessage(l10n.value("jdk.extension.lspServer.error_message",{reason:reason})); }); async function createProjectView(ctx : ExtensionContext, client : NbLanguageClient) { @@ -1380,9 +1390,9 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex const NO_JAVA_SUPPORT = "Cannot initialize Java support"; if (msg.startsWith(NO_JAVA_SUPPORT)) { if (isNbJavacDisabled()) { - const message = "Supported version of javac needed. Please either enable the nb-javac library, or use JDK 22+"; - const enable = "Enable the nb-javac library"; - const settings = "Open settings"; + const message = l10n.value("jdk.extension.nbjavac.message.supportedVersionRequired"); + const enable = l10n.value("jdk.extension.nbjavac.label.enableNbjavac"); + const settings = l10n.value("jdk.extension.nbjavac.label.openSettings"); window.showErrorMessage(message, enable, settings).then(reply => { if (enable === reply) { workspace.getConfiguration().update('jdk.advanced.disable.nbjavac', false); @@ -1391,8 +1401,8 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex } }); } else { - const yes = "Install GPLv2+CPEx code"; - window.showErrorMessage("Additional Java Support is needed", yes).then(reply => { + const yes = l10n.value("jdk.extension.javaSupport.label.installGpl"); + window.showErrorMessage(l10n.value("jdk.extension.javaSupport.message.needAdditionalSupport"), yes).then(reply => { if (yes === reply) { vscode.window.setStatusBarMessage(`Preparing ${SERVER_NAME} for additional installation`, 2000); restartWithJDKLater = function() { @@ -1480,7 +1490,7 @@ class NetBeansDebugAdapterDescriptionFactory implements vscode.DebugAdapterDescr if (cnt-- > 0) { setTimeout(fnc, 1000); } else { - reject(new Error('Oracle Java SE Debug Server Adapter not yet initialized. Please wait for a while and try again.')); + reject(new Error(l10n.value('jdk.extenstion.debugger.error_msg.debugAdapterNotInitialized'))); } } else { // resolve(new vscode.DebugAdapterServer(debugPort)); diff --git a/vscode/src/jdkDownloader.ts b/vscode/src/jdkDownloader.ts index 6f39b25c..644b1698 100644 --- a/vscode/src/jdkDownloader.ts +++ b/vscode/src/jdkDownloader.ts @@ -25,6 +25,7 @@ import * as crypto from 'crypto'; import { OPEN_JDK_VERSION_DOWNLOAD_LINKS, ORACLE_JDK_BASE_DOWNLOAD_URL, ORACLE_JDK_DOWNLOAD_VERSIONS } from './constants'; import { handleLog } from './extension'; import { promisify } from 'util'; +import { l10n } from './localiser'; let customView: vscode.WebviewPanel; let logger: vscode.OutputChannel; @@ -81,7 +82,7 @@ export async function openJDKSelectionView(log: vscode.OutputChannel) { // Create JDK Downloader view customView = vscode.window.createWebviewPanel( 'jdkDownloader', - 'JDK Downloader', + l10n.value("jdk.downloader.heading"), vscode.ViewColumn.One, { enableScripts: true @@ -103,31 +104,34 @@ export async function openJDKSelectionView(log: vscode.OutputChannel) { return; } - vscode.window.showInformationMessage(`Downloading and completing setup of ${jdkType} ${jdkVersion}...`); + vscode.window.showInformationMessage(l10n.value("jdk.downloader.message.downloadingAndCompletingSetup",{ + jdkType:jdkType, + jdkVersion: jdkVersion + })); JDKDownloader(jdkType, jdkOS, jdkArch, jdkVersion, installationPath); } } }); } -export function JDKDownloader(JDKType: string, osType: string, osArchitecture: string, JDKVersion: string, installationPath: string): void { +export function JDKDownloader(jdkType: string, osType: string, osArchitecture: string, jdkVersion: string, installationPath: string): void { let downloadUrl: string = ''; // Generate download url on the basis of the jdk type chosen - if (JDKType === 'OpenJDK') { + if (jdkType === 'OpenJDK') { if (osType === 'windows') { - downloadUrl = `${OPEN_JDK_VERSION_DOWNLOAD_LINKS[`${JDKVersion}`]}_${osType.toLowerCase()}-${osArchitecture}_bin.zip`; + downloadUrl = `${OPEN_JDK_VERSION_DOWNLOAD_LINKS[`${jdkVersion}`]}_${osType.toLowerCase()}-${osArchitecture}_bin.zip`; } else { - downloadUrl = `${OPEN_JDK_VERSION_DOWNLOAD_LINKS[`${JDKVersion}`]}_${osType.toLowerCase()}-${osArchitecture}_bin.tar.gz`; + downloadUrl = `${OPEN_JDK_VERSION_DOWNLOAD_LINKS[`${jdkVersion}`]}_${osType.toLowerCase()}-${osArchitecture}_bin.tar.gz`; } } - else if (JDKType === 'Oracle JDK') { + else if (jdkType === 'Oracle JDK') { if (osType === 'windows') { - downloadUrl = `${ORACLE_JDK_BASE_DOWNLOAD_URL}/${JDKVersion}/latest/jdk-${JDKVersion}_${osType.toLowerCase()}-${osArchitecture}_bin.zip`; + downloadUrl = `${ORACLE_JDK_BASE_DOWNLOAD_URL}/${jdkVersion}/latest/jdk-${jdkVersion}_${osType.toLowerCase()}-${osArchitecture}_bin.zip`; } else { - downloadUrl = `${ORACLE_JDK_BASE_DOWNLOAD_URL}/${JDKVersion}/latest/jdk-${JDKVersion}_${osType.toLowerCase()}-${osArchitecture}_bin.tar.gz`; + downloadUrl = `${ORACLE_JDK_BASE_DOWNLOAD_URL}/${jdkVersion}/latest/jdk-${jdkVersion}_${osType.toLowerCase()}-${osArchitecture}_bin.tar.gz`; } } @@ -135,10 +139,10 @@ export function JDKDownloader(JDKType: string, osType: string, osArchitecture: s const targetDirectory = path.join(__dirname, 'jdk_downloads'); let fileName = ''; if (osType === 'windows') { - fileName = `${JDKType}-${JDKVersion}_${osType}-${osArchitecture}_bin.zip`; + fileName = `${jdkType}-${jdkVersion}_${osType}-${osArchitecture}_bin.zip`; } else { - fileName = `${JDKType}-${JDKVersion}_${osType}-${osArchitecture}_bin.tar.gz`; + fileName = `${jdkType}-${jdkVersion}_${osType}-${osArchitecture}_bin.tar.gz`; } // Create the target directory if it doesn't exist @@ -152,7 +156,10 @@ export function JDKDownloader(JDKType: string, osType: string, osArchitecture: s // Downloading the file using https modules const request = https.get(downloadUrl, response => { if (response.statusCode !== 200) { - vscode.window.showErrorMessage(`HTTP Error ${response.statusCode} - ${response.statusMessage}`); + vscode.window.showErrorMessage(l10n.value("jdk.downloader.error_message.downloadFailedHttpError", { + statusCode:response.statusCode, + statusMessage:response.statusMessage + })); return; } @@ -163,22 +170,34 @@ export function JDKDownloader(JDKType: string, osType: string, osArchitecture: s const checkSumObtained = await calculateChecksum(filePath); const checkSumExpected = (await axios.get(`${downloadUrl}.sha256`)).data; if (checkSumExpected === checkSumObtained) { - vscode.window.showInformationMessage(`${JDKType} ${JDKVersion} for ${osType} download completed!`); - await extractJDK(filePath, installationPath, JDKVersion, osType, JDKType); + const message = l10n.value("jdk.downloader.message.downloadCompleted",{ + jdkType: jdkType, + jdkVersion: jdkVersion, + osType: osType + }); + vscode.window.showInformationMessage(message); + await extractJDK(filePath, installationPath, jdkVersion, osType, jdkType); } else { handleLog(logger, `Checksums don't match! \n Expected: ${checkSumExpected} \n Obtained: ${checkSumObtained}`); - vscode.window.showErrorMessage(`"${JDKType} ${JDKVersion} for ${osType} download failed, wrong checksum."`); + vscode.window.showErrorMessage(l10n.value("jdk.downloader.message.downloadFailed",{ + "jdkType":jdkType, + "jdkVersion":jdkVersion, + "osType":osType + })); } }); writeStream.on('error', error => { - vscode.window.showErrorMessage('Error while saving the file:' + error); + vscode.window.showErrorMessage(l10n.value("jdk.downloader.error_message.whileSavingFile",{error:error}) ); }); }); request.on('error', error => { - vscode.window.showErrorMessage('Error while downloading the : ' + JDKType + ' ' + error); + vscode.window.showErrorMessage(l10n.value( "jdk.downloader.error_message.whileDownloading",{ + "jdkType":jdkType, + "error":error + })); }); request.end(); @@ -201,7 +220,7 @@ export async function extractJDK(jdkTarballPath: string, extractionTarget: strin child_process.exec(extractCommand, async (error) => { if (error) { - vscode.window.showErrorMessage('Error: ' + error); + vscode.window.showErrorMessage(l10n.value("jdk.downloader.error_message.jdkExtractionError",{error:error})); } else { const dirsPresent = await fs.promises.readdir(downloadedDir); const matchingJdkDir = dirsPresent.filter(file => file.startsWith(`jdk-${jdkVersion}`)); @@ -211,7 +230,11 @@ export async function extractJDK(jdkTarballPath: string, extractionTarget: strin const newDirName = `${jdkType.split(' ').join('_')}-${jdkVersion}`; newDirectoryPath = await handleJdkPaths(newDirName, extractionTarget, osType); if (newDirectoryPath === null) { - vscode.window.showInformationMessage(`Cannot install ${jdkType} ${jdkVersion}. Cannot delete ${newDirName}`); + vscode.window.showInformationMessage(l10n.value("jdk.downloader.error_message.jdkNewDirectoryIssueCannotInstall",{ + jdkType:jdkType, + jdkVersion:jdkVersion, + newDirName:newDirName + })); } else { // If user agrees for deleting the directory then delete it and move the temp directory to the user selected location await fs.promises.rename(tempDirectoryPath, newDirectoryPath); @@ -246,12 +269,16 @@ const handleJdkPaths = async (directoryName: string, parentPath: string, osType: } const directoryPath = path.join(parentPath, name); if (fs.existsSync(directoryPath)) { - const CONFIRMATION_MESSAGE = `${name} is already present. Do you want to delete it and create with new contents?`; - const selected = await vscode.window.showInformationMessage(CONFIRMATION_MESSAGE, "Yes", "No"); - if (selected === "Yes") { + const CONFIRMATION_MESSAGE = l10n.value("jdk.downloader.message.confirmation.directoryExistsStillWantToDelete",{ + name:name + }); + const yes = l10n.value("jdk.downloader.message.confirmation.yes"); + const no = l10n.value("jdk.downloader.message.confirmation.no"); + const selected = await vscode.window.showInformationMessage(CONFIRMATION_MESSAGE, yes, no); + if (selected === yes) { await fs.promises.rmdir(directoryPath, { recursive: true }); } - else if (selected === "No") { + else if (selected === no) { return null; } } @@ -264,7 +291,7 @@ const selectPath = async (installType: string): Promise => { canSelectFiles: false, canSelectFolders: true, canSelectMany: false, - openLabel: installType === 'manual' ? 'Select installed JDK' : 'Install in selected location' + openLabel: installType === 'manual' ? l10n.value("jdk.downloader.label.selectJdk") : l10n.value("jdk.downloader.label.installJdk") }; const selectedFolders = await vscode.window.showOpenDialog(options); @@ -273,7 +300,7 @@ const selectPath = async (installType: string): Promise => { const selectedFolder = selectedFolders[0]; return selectedFolder.fsPath; } else { - vscode.window.showInformationMessage('No location selected.'); + vscode.window.showInformationMessage(l10n.value("jdk.downloader.message.noLocationSelected")); return null; } } @@ -281,21 +308,24 @@ const selectPath = async (installType: string): Promise => { const installationCompletion = async (installType: string) => { let dialogBoxMessage: string; if (installType === "automatic") { - dialogBoxMessage = `Completed installing JDK. Please reload Visual Studio Code to enable it.`; + dialogBoxMessage = l10n.value("jdk.downloader.message.completedInstallingJdk"); } else { - dialogBoxMessage = `Added JDK Path to the configuration settings. Please reload Visual Studio Code to enable it.`; + dialogBoxMessage = l10n.value("jdk.downloader.message.addedJdkPath"); } - const selected = await vscode.window.showInformationMessage(dialogBoxMessage, "Reload now"); - if (selected === "Reload now") { + const reloadNow:string = l10n.value("jdk.downloader.message.reload"); + const selected = await vscode.window.showInformationMessage(dialogBoxMessage, reloadNow); + if (selected === reloadNow) { await customView.dispose(); await vscode.commands.executeCommand('workbench.action.reloadWindow'); } } export const fetchJDKDownloadView = (machineArch: string, osType: string, versions: Array): string => { + + let downloader_title = l10n.value("jdk.downloader.heading") return ` - JDK Downloader + ${downloader_title} -

JDK Downloader

-

This tool enables you to download either the latest Oracle Java SE JDK with Oracle No-Fee Terms and Conditions or the Oracle OpenJDK builds under the GNU Public License with ClassPath Exception. - It will then handle the installation and configuration on your behalf.

-

This enables you to take full advantage of all the features offered by this extension.

+

${downloader_title}

+ ${l10n.value("jdk.downloader.html.details")}
- - OR - - OR - + + ${l10n.value("jdk.downloader.label.or")} + + ${l10n.value("jdk.downloader.label.or")} +

- +
- - - + + +
- +
@@ -469,18 +497,18 @@ export const fetchJDKDownloadView = (machineArch: string, osType: string, versio
- +
- +
- ${versions.map((el, index) => { - if (index === 0) { - return `` - } - return `` - })} - -
-
-
- -
-
- -
-
-
- -
-
- -
-
-
- -
-
-
-
- -
-
- -
-
-
- -
-
- -
-
-
- -
-
- -
-
-
- -
-
- - + + ` + } + + private getJdkVersionsHtml = (jdkVersions: String[]) => { + let htmlStr = ""; + jdkVersions.forEach((el: String, index: number) => { + if (index === 0) { + htmlStr += `\n`; + } + else { + htmlStr += `\n`; + } + }); + + return htmlStr; + } + + private getOsTypeHtml = () => { + return ` + + ` + } + + private getMachineArchHtml = () => { + return ` + ` + } + + private getScriptJs = () => { + return `const vscode = acquireVsCodeApi(); + let activeButton = null; + const oracleJdkButtonId = 'oracleJDK'; + const openJdkButtonId = 'openJDK'; + + document.getElementById("openJDK")?.addEventListener('click', event => { + hideOrDisplayDivs(event); + }); + + document.getElementById("oracleJDK")?.addEventListener('click', event => { + hideOrDisplayDivs(event); + }); + + document.getElementById("addJDKPathManually")?.addEventListener('click', event => { + vscode.postMessage({ + command: "${JdkDownloaderView.DOWNLOAD_CMD_LABEL}", + installType: "${JdkDownloaderAction.MANUAL_INSTALLATION_TYPE}", + }); + }); + + document.getElementById("openJDKDownloadButton")?.addEventListener('click', event => { + triggerJDKDownload(event); + }); + + document.getElementById("oracleJDKDownloadButton")?.addEventListener('click', event => { + triggerJDKDownload(event); + }); + + const hideOrDisplayDivs = (e) => { + const { id } = e.target; + if(activeButton){ + activeButton.classList.remove("active"); + const activeButtonDiv = document.getElementById(activeButton.id+'Div'); + activeButtonDiv.style.display ='none'; + } + + if(activeButton?.id !== id){ + activeButton = e.target; + activeButton.classList.add("active"); + document.getElementById(id+'Div').style.display ='flex'; + } else{ + activeButton = null; + } + }; + + const triggerJDKDownload = (e) => { + const { id } = e.target; + const jdkType = id === openJdkButtonId+'DownloadButton' ? "${JdkDownloaderView.OPEN_JDK_LABEL}" : "${JdkDownloaderView.ORACLE_JDK_LABEL}"; + vscode.postMessage({ + command: "${JdkDownloaderView.DOWNLOAD_CMD_LABEL}", + id: jdkType, + installType: "${JdkDownloaderAction.AUTO_INSTALLATION_TYPE}", + jdkVersion: document.getElementById(activeButton.id+'VersionDropdown').value, + jdkOS: document.getElementById(activeButton.id+'OsTypeDropdown').value, + jdkArch: document.getElementById(activeButton.id+'MachineArchDropdown').value + }); + } + ` + } + +} \ No newline at end of file diff --git a/vscode/src/utils.ts b/vscode/src/utils.ts index 29f2009e..e578913e 100644 --- a/vscode/src/utils.ts +++ b/vscode/src/utils.ts @@ -18,6 +18,11 @@ */ import * as vscode from 'vscode'; +import * as https from 'https'; +import * as fs from 'fs'; +import { promisify } from "util"; +import * as crypto from 'crypto'; +import { l10n } from './localiser'; class InputFlowAction { static back = new InputFlowAction(); @@ -197,3 +202,77 @@ export class MultiStepInput { } } } + +export function httpsGet(url: string) { + return new Promise((resolve, reject) => { + https.get(url, (res) => { + if (res.statusCode !== 200) { + return reject(new Error(l10n.value("jdk.extension.utils.error_message.failedHttpsRequest", { + url, + statusCode: res.statusCode + }))); + } + let data = ''; + + res.on('data', (chunk) => { + data += chunk; + }); + + res.on('end', () => { + resolve(data); + }); + }).on('error', (e) => { + reject(e); + }); + }); +} + +export function downloadFileWithProgressBar(downloadUrl: string, downloadLocation: string, message: string) { + return vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, cancellable: false }, p => { + return new Promise((resolve, reject) => { + const file = fs.createWriteStream(downloadLocation); + https.get(downloadUrl, (response) => { + if (response.statusCode !== 200) { + return reject(new Error(l10n.value("jdk.extension.utils.error_message.failedHttpsRequest", { + url: downloadUrl, + statusCode: response.statusCode + }))); + } + + const totalSize = parseInt(response.headers['content-length'] || '0'); + let downloadedSize = 0; + response.pipe(file); + + response.on('data', (chunk) => { + downloadedSize += chunk.length; + if (totalSize) { + const increment = parseFloat(((chunk.length / totalSize) * 100).toFixed(2)); + const progress = parseFloat(((downloadedSize / totalSize) * 100).toFixed(2)); + p.report({ increment, message: `${message}: ${progress} %` }); + } + }); + + file.on('finish', () => { + file.close(); + resolve(); + }); + }).on('error', (err) => { + fs.unlink(downloadLocation, () => reject(err)); + }); + }); + }); +} + +export const calculateChecksum = async (filePath: string, algorithm: string = 'sha256'): Promise => { + const hash = crypto.createHash(algorithm); + const pipeline = promisify(require('stream').pipeline); + const readStream = fs.createReadStream(filePath); + + await pipeline( + readStream, + hash + ); + + const checksum = hash.digest('hex'); + return checksum; +} \ No newline at end of file From d7f7d5df6435d17612ab5a6cce8ed99ab4b1d8e1 Mon Sep 17 00:00:00 2001 From: Achal Talati Date: Tue, 10 Sep 2024 14:31:37 +0530 Subject: [PATCH 10/18] Remove unused images --- vscode/images/SourceCodeFormatter.png | Bin 24531 -> 0 bytes vscode/images/cloud-explorer.png | Bin 37604 -> 0 bytes vscode/images/database-explorer.png | Bin 40394 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 vscode/images/SourceCodeFormatter.png delete mode 100644 vscode/images/cloud-explorer.png delete mode 100644 vscode/images/database-explorer.png diff --git a/vscode/images/SourceCodeFormatter.png b/vscode/images/SourceCodeFormatter.png deleted file mode 100644 index af4e0d1c577364d280ab4ed90e06a3024f347e6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24531 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 diff --git a/vscode/images/cloud-explorer.png b/vscode/images/cloud-explorer.png deleted file mode 100644 index b9ab9a8a9b6f6b9a8158e0e462f2b0dd38564e64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37604 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 VY$>bmO1%aCR1`GjOJyyB{|DK+#l8Rl From 2165638bd07ea2800d201a404b948c1a946c81cf Mon Sep 17 00:00:00 2001 From: Subhash Arabhi Date: Tue, 10 Sep 2024 14:07:43 +0530 Subject: [PATCH 11/18] Patch for unwanted imports bug from NB Signed-off-by: Subhash Arabhi --- build.xml | 1 + patches/7699.diff | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 patches/7699.diff diff --git a/build.xml b/build.xml index 987d5382..75f17a98 100644 --- a/build.xml +++ b/build.xml @@ -50,6 +50,7 @@ patches/7641.diff patches/7654.diff patches/7690.diff + patches/7699.diff patches/7722.diff patches/mvn-sh.diff patches/generate-dependencies.diff diff --git a/patches/7699.diff b/patches/7699.diff new file mode 100644 index 00000000..b508a11c --- /dev/null +++ b/patches/7699.diff @@ -0,0 +1,36 @@ +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() { From fee381ca58216508cf15ecb060b9029414b4076c Mon Sep 17 00:00:00 2001 From: Subhash Arabhi Date: Tue, 10 Sep 2024 12:48:01 +0530 Subject: [PATCH 12/18] Patch for renaming comments bug Signed-off-by: Subhash Arabhi --- build.xml | 1 + patches/7724.diff | 88 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 patches/7724.diff diff --git a/build.xml b/build.xml index 75f17a98..52e8ad3c 100644 --- a/build.xml +++ b/build.xml @@ -52,6 +52,7 @@ patches/7690.diff patches/7699.diff patches/7722.diff + patches/7724.diff patches/mvn-sh.diff patches/generate-dependencies.diff patches/rename-debugger.diff diff --git a/patches/7724.diff b/patches/7724.diff new file mode 100644 index 00000000..4b45d216 --- /dev/null +++ b/patches/7724.diff @@ -0,0 +1,88 @@ +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 { From 6240a1c2211954608de80c6a6beeb89b52d876c4 Mon Sep 17 00:00:00 2001 From: Balyam muralidhar narendra kumar Date: Tue, 10 Sep 2024 16:01:04 +0530 Subject: [PATCH 13/18] [JAVSCODE-253] adding/updating few language keys --- vscode/l10n/bundle.l10n.en.json | 24 ++++++++++++++++-------- vscode/src/extension.ts | 2 +- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/vscode/l10n/bundle.l10n.en.json b/vscode/l10n/bundle.l10n.en.json index bb0cff69..b41c7984 100644 --- a/vscode/l10n/bundle.l10n.en.json +++ b/vscode/l10n/bundle.l10n.en.json @@ -1,9 +1,7 @@ { "jdk.downloader.heading": "JDK Downloader", - "jdk.downloader.html.details":"

It will then handle the installation and configuration on your behalf.

This enables you to take full advantage of all the features offered by this extension.

", - "jdk.downloader.button.label.oracleJdk": "Download Oracle Java SE JDK", "jdk.downloader.label.or": "or", "jdk.downloader.button.label.openJdk": "Download Oracle OpenJDK", @@ -85,15 +83,25 @@ "jdk.extension.runConfig.wrkdir.prompt": "Customize working directory", "jdk.extenstion.notInstalled.label":"Extension not installed.", "jdk.extenstion.error_msg.clientNotAvailable":"Client not available", - "jdk.extenstion.progressBar.error_msg.cannotRun":"cannot run ${lsCommand}; client is ${client}", - "jdk.extenstion.error_msg.doesntSupportNewTeamplate":"Client ${client} doesn't support new from template", - "jdk.extenstion.error_msg.doesntSupportNewProject":"Client ${client} doesn't support new project", - "jdk.extenstion.error_msg.doesntSupportGoToTest":"Client ${client} doesn't support go to test", + "jdk.extenstion.progressBar.error_msg.cannotRun":"cannot run {lsCommand}; client is {client}", + "jdk.extenstion.error_msg.doesntSupportNewTeamplate":"Client {client} doesn't support new from template", + "jdk.extenstion.error_msg.doesntSupportNewProject":"Client {client} doesn't support new project", + "jdk.extenstion.error_msg.doesntSupportGoToTest":"Client {client} doesn't support go to test", "jdk.extenstion.error_msg.noSuperImpl":"No super implementation found", "jdk.extenstion.error_msg.cacheDeletionError":"Error deleting the cache", "jdk.extenstion.message.cacheDeleted":"Cache deleted successfully", "jdk.extenstion.cache.error_msg.cannotFindWrkSpacePath":"Cannot find workspace path", "jdk.extenstion.debugger.error_msg.debugAdapterNotInitialized":"Oracle Java SE Debug Server Adapter not yet initialized. Please wait for a while and try again.", "jdk.workspace.new.prompt": "Input the directory path where the new file will be generated", - "jdk.extension.utils.error_message.failedHttpsRequest": "Failed to get {url} ({statusCode})" - } + "jdk.extension.utils.error_message.failedHttpsRequest": "Failed to get {url} ({statusCode})", + "jdk.extension.error_msg.notEnabled": "{SERVER_NAME} not enabled", + "LBL_Run": "Run {methodName}", + "LBL_Debug": "Debug {methodName}", + "LBL_RunWith": "Run {methodName} with {config}", + "LBL_DebugWith": "Debug {methodName} with {config}", + "LBL_TestMethod":"Test {methodName}", + "LBL_ProfileMethod":"Profile {methodName}", + "LBL_Clean":"Clean", + "LBL_Build":"Build", + "LBL_ContinuousMode":"Continuous Mode" +} diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index 41a5e8c4..c81648f2 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -1028,7 +1028,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex handleLog(log, `Please refer to troubleshooting section for more info: https://github.com/oracle/javavscode/blob/main/README.md#troubleshooting`); log.show(false); killNbProcess(false, log, p); - reject(`${SERVER_NAME} not enabled!`); + reject(l10n.value("jdk.extension.error_msg.notEnabled",{SERVER_NAME})); } else { handleLog(log, "LSP server " + p.pid + " terminated with " + code); handleLog(log, "Exit code " + code); From f3e9959f3ed188d7ceff86e310c97c41e91aecfa Mon Sep 17 00:00:00 2001 From: Achal Talati Date: Sun, 8 Sep 2024 12:45:14 +0530 Subject: [PATCH 14/18] Upgrade to nb-23-rc3 --- .github/workflows/main.yml | 2 +- BUILD.md | 2 +- THIRD_PARTY_LICENSES.txt | 150 +- build.xml | 16 +- .../modules/java/testrunner/Bundle.properties | 21 + .../modules/nbcode/integration/layer.xml | 19 + .../integration/maven-actions-override.xml | 130 ++ patches/7001.diff | 264 --- patches/7271.diff | 31 - patches/7353.diff | 16 - patches/7368.diff | 507 ---- patches/7370.diff | 135 -- patches/7382.diff | 1611 ------------- patches/7390.diff | 13 - patches/{7491-preliminary.diff => 7491.diff} | 758 +++--- patches/7497.diff | 305 --- patches/7548_source-1.8.diff | 73 - patches/7583_source-1.8.diff | 80 - patches/7621.diff | 125 - patches/7670.diff | 2037 +++++++++++++++++ patches/7690.diff | 83 - patches/7733.diff | 614 +++++ patches/remove-db.diff | 216 -- vscode/package-lock.json | 4 +- vscode/src/extension.ts | 15 +- 25 files changed, 3375 insertions(+), 3852 deletions(-) create mode 100644 nbcode/branding/modules/org-netbeans-modules-java-testrunner.jar/org/netbeans/modules/java/testrunner/Bundle.properties create mode 100644 nbcode/integration/src/org/netbeans/modules/nbcode/integration/maven-actions-override.xml delete mode 100644 patches/7001.diff delete mode 100644 patches/7271.diff delete mode 100644 patches/7353.diff delete mode 100644 patches/7368.diff delete mode 100644 patches/7370.diff delete mode 100644 patches/7382.diff delete mode 100644 patches/7390.diff rename patches/{7491-preliminary.diff => 7491.diff} (76%) delete mode 100644 patches/7497.diff delete mode 100644 patches/7548_source-1.8.diff delete mode 100644 patches/7583_source-1.8.diff delete mode 100644 patches/7621.diff create mode 100644 patches/7670.diff delete mode 100644 patches/7690.diff create mode 100644 patches/7733.diff diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5d8185b7..cfd13cc6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -62,7 +62,7 @@ jobs: with: repository: apache/netbeans path: netbeans - ref: 22 + ref: 23-rc3 - name: Apply NetBeans patches run: ant apply-patches diff --git a/BUILD.md b/BUILD.md index 579ab882..d9d6b772 100644 --- a/BUILD.md +++ b/BUILD.md @@ -39,7 +39,7 @@ $ git clone https://github.com/oracle/javavscode.git $ cd javavscode/ $ git clone https://github.com/apache/netbeans.git $ cd netbeans/ -$ git checkout 22 +$ git checkout 23-rc3 $ cd .. # the following target requires git executable to be on PATH: $ ant apply-patches diff --git a/THIRD_PARTY_LICENSES.txt b/THIRD_PARTY_LICENSES.txt index cca01611..5e3bb7dc 100644 --- a/THIRD_PARTY_LICENSES.txt +++ b/THIRD_PARTY_LICENSES.txt @@ -298,7 +298,7 @@ ide/modules/com-googlecode-javaewah-JavaEWAH.jar Apache-2.0 ide/modules/ext/antlr-runtime-3.5.3.jar BSD-antlr-runtime3 ide/modules/ext/antlr4-runtime-4.13.1.jar BSD-antlr-runtime4-3 ide/modules/ext/collections-24.0.0.jar UPL -ide/modules/ext/commons-compress-1.26.1.jar Apache-2.0 +ide/modules/ext/commons-compress-1.26.2.jar Apache-2.0 ide/modules/ext/flexmark-0.64.8.jar BSD-flexmark ide/modules/ext/flexmark-ext-anchorlink-0.64.8.jar BSD-flexmark ide/modules/ext/flexmark-ext-emoji-0.64.8.jar BSD-flexmark @@ -315,7 +315,7 @@ ide/modules/ext/flexmark-util-html-0.64.8.jar BSD-flexma ide/modules/ext/flexmark-util-misc-0.64.8.jar BSD-flexmark ide/modules/ext/flexmark-util-sequence-0.64.8.jar BSD-flexmark ide/modules/ext/flexmark-util-visitor-0.64.8.jar BSD-flexmark -ide/modules/ext/freemarker-2.3.32.jar Apache-2.0-freemarker +ide/modules/ext/freemarker-2.3.33.jar Apache-2.0-freemarker ide/modules/ext/jaxb/activation.jar EDL-1.0 ide/modules/ext/jaxb/api/jaxb-api.jar EDL-1.0 ide/modules/ext/jaxb/jaxb-impl.jar CDDL-1.1 @@ -374,25 +374,23 @@ java/maven/bin/mvn.cmd Apache-2.0 java/maven/bin/mvnDebug Apache-2.0 java/maven/bin/mvnDebug.cmd Apache-2.0 java/maven/bin/mvnyjp Apache-2.0 -java/maven/boot/plexus-classworlds-2.7.0.jar Apache-2.0 +java/maven/boot/plexus-classworlds-2.8.0.jar Apache-2.0 java/maven/boot/plexus-classworlds.license Apache-2.0 java/maven/conf/logging/simplelogger.properties Apache-2.0 java/maven/conf/settings.xml Apache-2.0 java/maven/conf/toolchains.xml Apache-2.0 java/maven/lib/aopalliance-1.0.jar Apache-2.0 java/maven/lib/aopalliance.license Apache-2.0 -java/maven/lib/commons-cli-1.5.0.jar Apache-2.0 +java/maven/lib/commons-cli-1.8.0.jar Apache-2.0 java/maven/lib/commons-cli.license Apache-2.0 -java/maven/lib/commons-codec-1.16.0.jar Apache-2.0 +java/maven/lib/commons-codec-1.17.1.jar Apache-2.0 java/maven/lib/commons-codec.license Apache-2.0 -java/maven/lib/commons-lang3-3.12.0.jar Apache-2.0 -java/maven/lib/commons-lang3.license Apache-2.0 java/maven/lib/ext/README.txt Apache-2.0 java/maven/lib/ext/hazelcast/README.txt Apache-2.0 java/maven/lib/ext/redisson/README.txt Apache-2.0 java/maven/lib/failureaccess-1.0.2.jar Apache-2.0 java/maven/lib/failureaccess.license Apache-2.0 -java/maven/lib/guava-32.0.1-jre.jar Apache-2.0 +java/maven/lib/guava-33.2.1-jre.jar Apache-2.0 java/maven/lib/guava.license Apache-2.0 java/maven/lib/guice-5.1.0.jar Apache-2.0 java/maven/lib/guice.license Apache-2.0 @@ -400,8 +398,9 @@ java/maven/lib/httpclient-4.5.14.jar Apache-2.0 java/maven/lib/httpclient.license Apache-2.0 java/maven/lib/httpcore-4.4.16.jar Apache-2.0 java/maven/lib/httpcore.license Apache-2.0 -java/maven/lib/jansi-2.4.0.jar Apache-2.0 +java/maven/lib/jansi-2.4.1.jar Apache-2.0 java/maven/lib/jansi-native/README.txt Apache-2.0 +java/maven/lib/jansi-native/Windows/arm64/libjansi.so Apache-2.0 java/maven/lib/jansi-native/Windows/x86/jansi.dll Apache-2.0 java/maven/lib/jansi-native/Windows/x86_64/jansi.dll Apache-2.0 java/maven/lib/jansi.license Apache-2.0 @@ -411,43 +410,45 @@ java/maven/lib/javax.inject-1.jar Apache-2.0 java/maven/lib/javax.inject.license Apache-2.0 java/maven/lib/jcl-over-slf4j-1.7.36.jar Apache-2.0 java/maven/lib/jcl-over-slf4j.license Apache-2.0 -java/maven/lib/maven-artifact-3.9.6.jar Apache-2.0 -java/maven/lib/maven-builder-support-3.9.6.jar Apache-2.0 -java/maven/lib/maven-compat-3.9.6.jar Apache-2.0 -java/maven/lib/maven-core-3.9.6.jar Apache-2.0 -java/maven/lib/maven-embedder-3.9.6.jar Apache-2.0 -java/maven/lib/maven-model-3.9.6.jar Apache-2.0 -java/maven/lib/maven-model-builder-3.9.6.jar Apache-2.0 -java/maven/lib/maven-plugin-api-3.9.6.jar Apache-2.0 -java/maven/lib/maven-repository-metadata-3.9.6.jar Apache-2.0 -java/maven/lib/maven-resolver-api-1.9.18.jar Apache-2.0 -java/maven/lib/maven-resolver-connector-basic-1.9.18.jar Apache-2.0 -java/maven/lib/maven-resolver-impl-1.9.18.jar Apache-2.0 -java/maven/lib/maven-resolver-named-locks-1.9.18.jar Apache-2.0 -java/maven/lib/maven-resolver-provider-3.9.6.jar Apache-2.0 -java/maven/lib/maven-resolver-spi-1.9.18.jar Apache-2.0 -java/maven/lib/maven-resolver-transport-file-1.9.18.jar Apache-2.0 -java/maven/lib/maven-resolver-transport-http-1.9.18.jar Apache-2.0 -java/maven/lib/maven-resolver-transport-wagon-1.9.18.jar Apache-2.0 -java/maven/lib/maven-resolver-util-1.9.18.jar Apache-2.0 -java/maven/lib/maven-settings-3.9.6.jar Apache-2.0 -java/maven/lib/maven-settings-builder-3.9.6.jar Apache-2.0 -java/maven/lib/maven-shared-utils-3.3.4.jar Apache-2.0 -java/maven/lib/maven-slf4j-provider-3.9.6.jar Apache-2.0 -java/maven/lib/org.eclipse.sisu.inject-0.9.0.M2.jar Maven-EPL-v10 +java/maven/lib/maven-artifact-3.9.9.jar Apache-2.0 +java/maven/lib/maven-builder-support-3.9.9.jar Apache-2.0 +java/maven/lib/maven-compat-3.9.9.jar Apache-2.0 +java/maven/lib/maven-core-3.9.9.jar Apache-2.0 +java/maven/lib/maven-embedder-3.9.9.jar Apache-2.0 +java/maven/lib/maven-model-3.9.9.jar Apache-2.0 +java/maven/lib/maven-model-builder-3.9.9.jar Apache-2.0 +java/maven/lib/maven-plugin-api-3.9.9.jar Apache-2.0 +java/maven/lib/maven-repository-metadata-3.9.9.jar Apache-2.0 +java/maven/lib/maven-resolver-api-1.9.22.jar Apache-2.0 +java/maven/lib/maven-resolver-connector-basic-1.9.22.jar Apache-2.0 +java/maven/lib/maven-resolver-impl-1.9.22.jar Apache-2.0 +java/maven/lib/maven-resolver-named-locks-1.9.22.jar Apache-2.0 +java/maven/lib/maven-resolver-provider-3.9.9.jar Apache-2.0 +java/maven/lib/maven-resolver-spi-1.9.22.jar Apache-2.0 +java/maven/lib/maven-resolver-transport-file-1.9.22.jar Apache-2.0 +java/maven/lib/maven-resolver-transport-http-1.9.22.jar Apache-2.0 +java/maven/lib/maven-resolver-transport-wagon-1.9.22.jar Apache-2.0 +java/maven/lib/maven-resolver-util-1.9.22.jar Apache-2.0 +java/maven/lib/maven-settings-3.9.9.jar Apache-2.0 +java/maven/lib/maven-settings-builder-3.9.9.jar Apache-2.0 +java/maven/lib/maven-shared-utils-3.4.2.jar Apache-2.0 +java/maven/lib/maven-slf4j-provider-3.9.9.jar Apache-2.0 +java/maven/lib/org.eclipse.sisu.inject-0.9.0.M3.jar Maven-EPL-v10 java/maven/lib/org.eclipse.sisu.inject.license Apache-2.0 -java/maven/lib/org.eclipse.sisu.plexus-0.9.0.M2.jar Maven-EPL-v10 +java/maven/lib/org.eclipse.sisu.plexus-0.9.0.M3.jar Maven-EPL-v10 java/maven/lib/org.eclipse.sisu.plexus.license Apache-2.0 java/maven/lib/plexus-cipher-2.0.jar Apache-2.0 java/maven/lib/plexus-cipher.license Apache-2.0 java/maven/lib/plexus-component-annotations-2.1.0.jar Apache-2.0 java/maven/lib/plexus-component-annotations.license Apache-2.0 -java/maven/lib/plexus-interpolation-1.26.jar Apache-2.0 +java/maven/lib/plexus-interpolation-1.27.jar Apache-2.0 java/maven/lib/plexus-interpolation.license Apache-2.0 java/maven/lib/plexus-sec-dispatcher-2.0.jar Apache-2.0 java/maven/lib/plexus-sec-dispatcher.license Apache-2.0 java/maven/lib/plexus-utils-3.5.1.jar Apache-2.0 java/maven/lib/plexus-utils.license Apache-2.0 +java/maven/lib/plexus-xml-3.0.1.jar Apache-2.0 +java/maven/lib/plexus-xml.license Apache-2.0 java/maven/lib/slf4j-api-1.7.36.jar MIT-slf4j-22 java/maven/lib/slf4j-api.license Apache-2.0 java/maven/lib/wagon-file-3.5.3.jar Apache-2.0 @@ -466,20 +467,19 @@ java/modules/ext/eclipselink/org.eclipse.persistence.jpa.jpql-2.7.12.jar EPL-v20 java/modules/ext/eclipselink/org.eclipse.persistence.jpa.modelgen.processor-2.7.12.jar EPL-v20 java/modules/ext/eclipselink/org.eclipse.persistence.moxy-2.7.12.jar EPL-v20 java/modules/ext/jdktools-11.0.11.jar Apache-2.0 -java/modules/ext/maven/gson-2.10.1.jar Apache-2.0 -java/modules/ext/maven/indexer-core-7.1.2.jar Apache-2.0 +java/modules/ext/maven/indexer-core-7.1.4.jar Apache-2.0 java/modules/ext/maven/javax.annotation-api-1.3.2.jar CDDL-1.1 java/modules/ext/maven/jdom2-2.0.6.1.jar BSD-JDOM -java/modules/ext/maven/lucene-analysis-common-9.10.0.jar Apache-2.0-lucene2 -java/modules/ext/maven/lucene-backward-codecs-9.10.0.jar Apache-2.0-lucene2 -java/modules/ext/maven/lucene-core-9.10.0.jar Apache-2.0-lucene2 -java/modules/ext/maven/lucene-highlighter-9.10.0.jar Apache-2.0-lucene2 -java/modules/ext/maven/lucene-queryparser-9.10.0.jar Apache-2.0-lucene2 +java/modules/ext/maven/lucene-analysis-common-9.11.1.jar Apache-2.0-lucene2 +java/modules/ext/maven/lucene-backward-codecs-9.11.1.jar Apache-2.0-lucene2 +java/modules/ext/maven/lucene-core-9.11.1.jar Apache-2.0-lucene2 +java/modules/ext/maven/lucene-highlighter-9.11.1.jar Apache-2.0-lucene2 +java/modules/ext/maven/lucene-queryparser-9.11.1.jar Apache-2.0-lucene2 java/modules/ext/maven/maven-dependency-tree-2.2.jar Apache-2.0 -java/modules/ext/maven/search-api-7.1.2.jar Apache-2.0 -java/modules/ext/maven/search-backend-smo-7.1.2.jar Apache-2.0 -java/modules/ext/nb-javac-jdk-22u1-8-api.jar GPL-2-CP -java/modules/ext/nb-javac-jdk-22u1-8.jar GPL-2-CP +java/modules/ext/maven/search-api-7.1.4.jar Apache-2.0 +java/modules/ext/maven/search-backend-smo-7.1.4.jar Apache-2.0 +java/modules/ext/nb-javac-jdk-23-30-api.jar GPL-2-CP +java/modules/ext/nb-javac-jdk-23-30.jar GPL-2-CP java/modules/ext/org.eclipse.lsp4j-0.13.0.jar EPL-v20 java/modules/ext/org.eclipse.lsp4j.debug-0.13.0.jar EPL-v20 java/modules/ext/org.eclipse.lsp4j.generator-0.13.0.jar EPL-v20 @@ -495,27 +495,27 @@ platform/core/asm-commons-9.7.jar BSD-INRIA platform/core/asm-tree-9.7.jar BSD-INRIA platform/docs/junit-4.13.2-javadoc.jar EPL-v10 platform/docs/junit-4.13.2-sources.jar EPL-v10 -platform/docs/junit-jupiter-api-5.10.2-javadoc.jar EPL-v20 -platform/docs/junit-jupiter-api-5.10.2-sources.jar EPL-v20 -platform/docs/junit-jupiter-engine-5.10.2-javadoc.jar EPL-v20 -platform/docs/junit-jupiter-engine-5.10.2-sources.jar EPL-v20 -platform/docs/junit-jupiter-params-5.10.2-javadoc.jar EPL-v20 -platform/docs/junit-jupiter-params-5.10.2-sources.jar EPL-v20 +platform/docs/junit-jupiter-api-5.10.3-javadoc.jar EPL-v20 +platform/docs/junit-jupiter-api-5.10.3-sources.jar EPL-v20 +platform/docs/junit-jupiter-engine-5.10.3-javadoc.jar EPL-v20 +platform/docs/junit-jupiter-engine-5.10.3-sources.jar EPL-v20 +platform/docs/junit-jupiter-params-5.10.3-javadoc.jar EPL-v20 +platform/docs/junit-jupiter-params-5.10.3-sources.jar EPL-v20 platform/docs/testng-6.13.1-javadoc.jar Apache-2.0+MIT+testng platform/lib/nbexec.dll Apache-2.0 platform/lib/nbexec.exe Apache-2.0 platform/lib/nbexec64.dll Apache-2.0 platform/lib/nbexec64.exe Apache-2.0 -platform/modules/ext/commons-io-2.16.0.jar Apache-2.0 -platform/modules/ext/flatlaf-3.3.jar Apache-2.0 +platform/modules/ext/commons-io-2.16.1.jar Apache-2.0 +platform/modules/ext/flatlaf-3.5.1.jar Apache-2.0 platform/modules/ext/hamcrest-core-1.3.jar BSD-hamcrest platform/modules/ext/jcommander-1.78.jar Apache-2.0 platform/modules/ext/jna-5.14.0.jar Apache-2.0 platform/modules/ext/jna-platform-5.14.0.jar Apache-2.0 platform/modules/ext/junit-4.13.2.jar EPL-v10 -platform/modules/ext/junit-jupiter-api-5.10.2.jar EPL-v20 -platform/modules/ext/junit-jupiter-engine-5.10.2.jar EPL-v20 -platform/modules/ext/junit-jupiter-params-5.10.2.jar EPL-v20 +platform/modules/ext/junit-jupiter-api-5.10.3.jar EPL-v20 +platform/modules/ext/junit-jupiter-engine-5.10.3.jar EPL-v20 +platform/modules/ext/junit-jupiter-params-5.10.3.jar EPL-v20 platform/modules/ext/org.eclipse.osgi_3.9.1.nb9.jar OSGi platform/modules/ext/osgi.cmpn-7.0.0.jar Apache-2.0 platform/modules/ext/osgi.core-8.0.0.jar Apache-2.0 @@ -767,6 +767,10 @@ enterprise/web.core/src/org/netbeans/modules/web/taglib/resources/web-jsptaglibr enterprise/web.core/src/org/netbeans/modules/web/taglib/resources/web-jsptaglibrary_2_1.mdd CDDL-1.1 enterprise/web.core/src/org/netbeans/modules/web/taglib/resources/web-jsptaglibrary_3_0.xsd EPL-v20 1 enterprise/web.core/src/org/netbeans/modules/web/taglib/resources/web-jsptaglibrary_3_0.mdd EPL-v20 1 +enterprise/web.core/src/org/netbeans/modules/web/taglib/resources/web-jsptaglibrary_3_1.xsd EPL-v20 1 +enterprise/web.core/src/org/netbeans/modules/web/taglib/resources/web-jsptaglibrary_3_1.mdd EPL-v20 1 +enterprise/web.core/src/org/netbeans/modules/web/taglib/resources/web-jsptaglibrary_4_0.xsd EPL-v20 1 +enterprise/web.core/src/org/netbeans/modules/web/taglib/resources/web-jsptaglibrary_4_0.mdd EPL-v20 1 enterprise/web.jsf/src/org/netbeans/modules/web/jsf/resources/xml.xsd W3C2 enterprise/web.jsf/src/org/netbeans/modules/web/jsf/resources/facelet-taglib_1_0.dtd CDDL-1.1 enterprise/web.jsf/src/org/netbeans/modules/web/jsf/resources/javaee_5.xsd CDDL-1.1 @@ -801,6 +805,8 @@ extide/gradle/src/org/netbeans/modules/gradle/resources/gradle.png extide/gradle/src/org/netbeans/modules/gradle/resources/gradle_dark.png Gradle-icon extide/options.java/src/org/netbeans/modules/options/java/resources/java_logo.png CC0-v10 extide/options.java/src/org/netbeans/modules/options/java/resources/java_logo.svg CC0-v10 +groovy/gradle.groovy/src/org/netbeans/modules/gradle/groovy/resources/groovyProjectIcon.png Gradle-icon +groovy/gradle.groovy/src/org/netbeans/modules/gradle/groovy/resources/groovyProjectIcon_dark.png Gradle-icon ide/css.editor/src/org/netbeans/modules/css/resources/icons/chrome20-disabled.png CDDL-1.0 ide/css.editor/src/org/netbeans/modules/css/resources/icons/chrome20.png CDDL-1.0 ide/css.editor/src/org/netbeans/modules/css/resources/icons/firefox20-disabled.png CDDL-1.0 @@ -877,6 +883,7 @@ java/j2ee.persistence/src/org/netbeans/modules/j2ee/persistence/dd/resources/orm java/j2ee.persistence/src/org/netbeans/modules/j2ee/persistence/dd/resources/persistence_3_0.xsd EDL-1.0 6 java/j2ee.persistence/src/org/netbeans/modules/j2ee/persistence/dd/resources/persistence_3_2.xsd EDL-1.0 6 java/java.lsp.server/vscode/syntaxes/java.tmLanguage.json MIT-java-grammar +java/java.lsp.server/vscode/language-configuration.json MIT-vscode java/junit.ui/src/org/netbeans/modules/junit/ui/resources/junit5-logo.png EPL-v20 java/languages.antlr/src/org/antlr/parser/antlr3/g4/ANTLRv3Lexer.g4 BSD-antlr4-grammar java/languages.antlr/src/org/antlr/parser/antlr3/g4/ANTLRv3Parser.g4 BSD-antlr4-grammar @@ -1126,7 +1133,7 @@ webcommon/javascript2.editor/test/unit/data/testfiles/ecmascript6/parser/ES6/ide webcommon/javascript2.editor/test/unit/data/testfiles/ecmascript6/parser/ES6/identifier/estimated.js BSD-jsfoundation webcommon/javascript2.editor/test/unit/data/testfiles/ecmascript6/parser/ES6/identifier/ethiopic_digits.js BSD-jsfoundation webcommon/javascript2.editor/test/unit/data/testfiles/ecmascript6/parser/ES6/identifier/invalid_escaped_surrogate_pairs.js BSD-jsfoundation -webcommon/javascript2.editor/test/unit/data/testfiles/ecmascript6/parser/ES6/identifier/invalid_expression_await.js BSD-jsfoundation +webcommon/javascript2.editor/test/unit/data/testfiles/ecmascript6/parser/ES6/identifier/export_await.js BSD-jsfoundation webcommon/javascript2.editor/test/unit/data/testfiles/ecmascript6/parser/ES6/identifier/invalid_function_wait.js BSD-jsfoundation webcommon/javascript2.editor/test/unit/data/testfiles/ecmascript6/parser/ES6/identifier/invalid_id_smp.js BSD-jsfoundation webcommon/javascript2.editor/test/unit/data/testfiles/ecmascript6/parser/ES6/identifier/invalid_lone_surrogate.source.js BSD-jsfoundation @@ -1854,24 +1861,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Julian Seward, jseward@acm.org - -Apache Batik -Copyright 1999-2020 The Apache Software Foundation - -This software contains code from the World Wide Web Consortium (W3C) for the -Document Object Model API (DOM API) and SVG Document Type Definition (DTD). - -This software contains code from the International Organisation for -Standardization for the definition of character entities used in the software's -documentation. - -This product includes images from the Tango Desktop Project -(http://tango.freedesktop.org/). - -This product includes images from the Pasodoble Icon Theme -(http://www.jesusda.com/projects/pasodoble). - - Apache Commons Lang Copyright 2001-2021 The Apache Software Foundation @@ -3916,11 +3905,11 @@ Nori Korean Morphological Analyzer - Apache Lucene Integration This software includes a binary and/or source version of data from - mecab-ko-dic-2.0.3-20170922 + mecab-ko-dic-2.1.1-20180720 which can be obtained from - https://bitbucket.org/eunjeon/mecab-ko-dic/downloads/mecab-ko-dic-2.0.3-20170922.tar.gz + https://bitbucket.org/eunjeon/mecab-ko-dic/downloads/mecab-ko-dic-2.1.1-20180720.tar.gz ====== license text follows @@ -8499,7 +8488,12 @@ recognized. === ====== ========================= UPL ========================= +====== The following are the NOTICEs pertaining to components using this license. For the full license text, please see the license text below the NOTICEs +Graal SDK and Truffle API +Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + +====== license text follows Copyright (c) __YEARS__, __NAMES__ The Universal Permissive License (UPL), Version 1.0 diff --git a/build.xml b/build.xml index 52e8ad3c..8ea4ba05 100644 --- a/build.xml +++ b/build.xml @@ -34,25 +34,15 @@ patches/6330.diff - patches/7001.diff - patches/7271.diff - patches/7353.diff - patches/7368.diff - patches/7370.diff - patches/7382.diff - patches/7390.diff - patches/7491-preliminary.diff - patches/7497.diff - patches/7548_source-1.8.diff - patches/7583_source-1.8.diff + patches/7491.diff patches/7610.diff - patches/7621.diff patches/7641.diff patches/7654.diff - patches/7690.diff + patches/7670.diff patches/7699.diff patches/7722.diff patches/7724.diff + patches/7733.diff patches/mvn-sh.diff patches/generate-dependencies.diff patches/rename-debugger.diff diff --git a/nbcode/branding/modules/org-netbeans-modules-java-testrunner.jar/org/netbeans/modules/java/testrunner/Bundle.properties b/nbcode/branding/modules/org-netbeans-modules-java-testrunner.jar/org/netbeans/modules/java/testrunner/Bundle.properties new file mode 100644 index 00000000..f630844e --- /dev/null +++ b/nbcode/branding/modules/org-netbeans-modules-java-testrunner.jar/org/netbeans/modules/java/testrunner/Bundle.properties @@ -0,0 +1,21 @@ +# 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. + +PROP_generate_setUp_default=false +PROP_generate_tearDown_default=false +PROP_generate_class_setUp_default=false +PROP_generate_class_tearDown_default=false diff --git a/nbcode/integration/src/org/netbeans/modules/nbcode/integration/layer.xml b/nbcode/integration/src/org/netbeans/modules/nbcode/integration/layer.xml index 11e1bf64..5256a0c7 100644 --- a/nbcode/integration/src/org/netbeans/modules/nbcode/integration/layer.xml +++ b/nbcode/integration/src/org/netbeans/modules/nbcode/integration/layer.xml @@ -52,12 +52,16 @@ + + + + @@ -75,6 +79,9 @@ + + + @@ -123,4 +130,16 @@ + + + + + + + + + + + + diff --git a/nbcode/integration/src/org/netbeans/modules/nbcode/integration/maven-actions-override.xml b/nbcode/integration/src/org/netbeans/modules/nbcode/integration/maven-actions-override.xml new file mode 100644 index 00000000..14e3f7db --- /dev/null +++ b/nbcode/integration/src/org/netbeans/modules/nbcode/integration/maven-actions-override.xml @@ -0,0 +1,130 @@ + + + + + + build + + * + + + install + + also-make + + + rebuild + + * + + + clean + install + + also-make + + + test.single + + * + + + process-test-classes + surefire:test + + + ${packageClassName} + + + + + run + + jar + + + process-classes + org.codehaus.mojo:exec-maven-plugin:3.1.0:exec + + + + ${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs} + + ${packageClassName} + java + + build-with-dependencies + + + debug + + jar + + + process-classes + org.codehaus.mojo:exec-maven-plugin:3.1.0:exec + + + -agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address} + ${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs} + + ${packageClassName} + java + true + + build-with-dependencies + + + run.single.main + + * + + + process-classes + org.codehaus.mojo:exec-maven-plugin:3.1.0:exec + + + + ${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs} + java + ${packageClassName} + ${classPathScope} + + build-with-dependencies + + + + debug.single.main + + * + + + process-test-classes + org.codehaus.mojo:exec-maven-plugin:3.1.0:exec + + + -agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address} + ${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs} + java + ${packageClassName} + ${classPathScope} + true + + build-with-dependencies + + diff --git a/patches/7001.diff b/patches/7001.diff deleted file mode 100644 index df519ca8..00000000 --- a/patches/7001.diff +++ /dev/null @@ -1,264 +0,0 @@ -diff --git a/java/maven.embedder/build.xml b/java/maven.embedder/build.xml -index 1cf553760703..9dd89c01e769 100644 ---- a/java/maven.embedder/build.xml -+++ b/java/maven.embedder/build.xml -@@ -25,7 +25,9 @@ - - - -- -+ -+ -+ - - - -diff --git a/java/maven.embedder/external/binaries-list b/java/maven.embedder/external/binaries-list -index 94e918c9a5f8..c62571b895cb 100644 ---- a/java/maven.embedder/external/binaries-list -+++ b/java/maven.embedder/external/binaries-list -@@ -17,3 +17,4 @@ - DC15DFF8F701B227EE523EEB7A17F77C10EAFE2F org.jdom:jdom2:2.0.6.1 - 5D9CE6ADD7B714B8095F0E3E396C5E9F8C5DCFEF org.apache.maven.shared:maven-dependency-tree:2.2 - FC5C01A07E4A2DDF84AF0DFADF382E6F7993462D org.apache.maven:apache-maven:3.9.6:bin@zip -+C4A06A64E650562F30B7BF9AAEC1BFED43ACA12B com.google.guava:failureaccess:1.0.2 -diff --git a/java/maven.embedder/external/failureaccess-1.0.2-license.txt b/java/maven.embedder/external/failureaccess-1.0.2-license.txt -new file mode 100644 -index 000000000000..0c948ba081a4 ---- /dev/null -+++ b/java/maven.embedder/external/failureaccess-1.0.2-license.txt -@@ -0,0 +1,208 @@ -+Name: Guava - Failure Access Library -+Version: 1.0.2 -+License: Apache-2.0 -+Origin: https://github.com/google/guava -+Description: A Guava subproject -+ -+ -+ Apache License -+ Version 2.0, January 2004 -+ http://www.apache.org/licenses/ -+ -+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -+ -+ 1. Definitions. -+ -+ "License" shall mean the terms and conditions for use, reproduction, -+ and distribution as defined by Sections 1 through 9 of this document. -+ -+ "Licensor" shall mean the copyright owner or entity authorized by -+ the copyright owner that is granting the License. -+ -+ "Legal Entity" shall mean the union of the acting entity and all -+ other entities that control, are controlled by, or are under common -+ control with that entity. For the purposes of this definition, -+ "control" means (i) the power, direct or indirect, to cause the -+ direction or management of such entity, whether by contract or -+ otherwise, or (ii) ownership of fifty percent (50%) or more of the -+ outstanding shares, or (iii) beneficial ownership of such entity. -+ -+ "You" (or "Your") shall mean an individual or Legal Entity -+ exercising permissions granted by this License. -+ -+ "Source" form shall mean the preferred form for making modifications, -+ including but not limited to software source code, documentation -+ source, and configuration files. -+ -+ "Object" form shall mean any form resulting from mechanical -+ transformation or translation of a Source form, including but -+ not limited to compiled object code, generated documentation, -+ and conversions to other media types. -+ -+ "Work" shall mean the work of authorship, whether in Source or -+ Object form, made available under the License, as indicated by a -+ copyright notice that is included in or attached to the work -+ (an example is provided in the Appendix below). -+ -+ "Derivative Works" shall mean any work, whether in Source or Object -+ form, that is based on (or derived from) the Work and for which the -+ editorial revisions, annotations, elaborations, or other modifications -+ represent, as a whole, an original work of authorship. For the purposes -+ of this License, Derivative Works shall not include works that remain -+ separable from, or merely link (or bind by name) to the interfaces of, -+ the Work and Derivative Works thereof. -+ -+ "Contribution" shall mean any work of authorship, including -+ the original version of the Work and any modifications or additions -+ to that Work or Derivative Works thereof, that is intentionally -+ submitted to Licensor for inclusion in the Work by the copyright owner -+ or by an individual or Legal Entity authorized to submit on behalf of -+ the copyright owner. For the purposes of this definition, "submitted" -+ means any form of electronic, verbal, or written communication sent -+ to the Licensor or its representatives, including but not limited to -+ communication on electronic mailing lists, source code control systems, -+ and issue tracking systems that are managed by, or on behalf of, the -+ Licensor for the purpose of discussing and improving the Work, but -+ excluding communication that is conspicuously marked or otherwise -+ designated in writing by the copyright owner as "Not a Contribution." -+ -+ "Contributor" shall mean Licensor and any individual or Legal Entity -+ on behalf of whom a Contribution has been received by Licensor and -+ subsequently incorporated within the Work. -+ -+ 2. Grant of Copyright License. Subject to the terms and conditions of -+ this License, each Contributor hereby grants to You a perpetual, -+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable -+ copyright license to reproduce, prepare Derivative Works of, -+ publicly display, publicly perform, sublicense, and distribute the -+ Work and such Derivative Works in Source or Object form. -+ -+ 3. Grant of Patent License. Subject to the terms and conditions of -+ this License, each Contributor hereby grants to You a perpetual, -+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable -+ (except as stated in this section) patent license to make, have made, -+ use, offer to sell, sell, import, and otherwise transfer the Work, -+ where such license applies only to those patent claims licensable -+ by such Contributor that are necessarily infringed by their -+ Contribution(s) alone or by combination of their Contribution(s) -+ with the Work to which such Contribution(s) was submitted. If You -+ institute patent litigation against any entity (including a -+ cross-claim or counterclaim in a lawsuit) alleging that the Work -+ or a Contribution incorporated within the Work constitutes direct -+ or contributory patent infringement, then any patent licenses -+ granted to You under this License for that Work shall terminate -+ as of the date such litigation is filed. -+ -+ 4. Redistribution. You may reproduce and distribute copies of the -+ Work or Derivative Works thereof in any medium, with or without -+ modifications, and in Source or Object form, provided that You -+ meet the following conditions: -+ -+ (a) You must give any other recipients of the Work or -+ Derivative Works a copy of this License; and -+ -+ (b) You must cause any modified files to carry prominent notices -+ stating that You changed the files; and -+ -+ (c) You must retain, in the Source form of any Derivative Works -+ that You distribute, all copyright, patent, trademark, and -+ attribution notices from the Source form of the Work, -+ excluding those notices that do not pertain to any part of -+ the Derivative Works; and -+ -+ (d) If the Work includes a "NOTICE" text file as part of its -+ distribution, then any Derivative Works that You distribute must -+ include a readable copy of the attribution notices contained -+ within such NOTICE file, excluding those notices that do not -+ pertain to any part of the Derivative Works, in at least one -+ of the following places: within a NOTICE text file distributed -+ as part of the Derivative Works; within the Source form or -+ documentation, if provided along with the Derivative Works; or, -+ within a display generated by the Derivative Works, if and -+ wherever such third-party notices normally appear. The contents -+ of the NOTICE file are for informational purposes only and -+ do not modify the License. You may add Your own attribution -+ notices within Derivative Works that You distribute, alongside -+ or as an addendum to the NOTICE text from the Work, provided -+ that such additional attribution notices cannot be construed -+ as modifying the License. -+ -+ You may add Your own copyright statement to Your modifications and -+ may provide additional or different license terms and conditions -+ for use, reproduction, or distribution of Your modifications, or -+ for any such Derivative Works as a whole, provided Your use, -+ reproduction, and distribution of the Work otherwise complies with -+ the conditions stated in this License. -+ -+ 5. Submission of Contributions. Unless You explicitly state otherwise, -+ any Contribution intentionally submitted for inclusion in the Work -+ by You to the Licensor shall be under the terms and conditions of -+ this License, without any additional terms or conditions. -+ Notwithstanding the above, nothing herein shall supersede or modify -+ the terms of any separate license agreement you may have executed -+ with Licensor regarding such Contributions. -+ -+ 6. Trademarks. This License does not grant permission to use the trade -+ names, trademarks, service marks, or product names of the Licensor, -+ except as required for reasonable and customary use in describing the -+ origin of the Work and reproducing the content of the NOTICE file. -+ -+ 7. Disclaimer of Warranty. Unless required by applicable law or -+ agreed to in writing, Licensor provides the Work (and each -+ Contributor provides its Contributions) on an "AS IS" BASIS, -+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -+ implied, including, without limitation, any warranties or conditions -+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A -+ PARTICULAR PURPOSE. You are solely responsible for determining the -+ appropriateness of using or redistributing the Work and assume any -+ risks associated with Your exercise of permissions under this License. -+ -+ 8. Limitation of Liability. In no event and under no legal theory, -+ whether in tort (including negligence), contract, or otherwise, -+ unless required by applicable law (such as deliberate and grossly -+ negligent acts) or agreed to in writing, shall any Contributor be -+ liable to You for damages, including any direct, indirect, special, -+ incidental, or consequential damages of any character arising as a -+ result of this License or out of the use or inability to use the -+ Work (including but not limited to damages for loss of goodwill, -+ work stoppage, computer failure or malfunction, or any and all -+ other commercial damages or losses), even if such Contributor -+ has been advised of the possibility of such damages. -+ -+ 9. Accepting Warranty or Additional Liability. While redistributing -+ the Work or Derivative Works thereof, You may choose to offer, -+ and charge a fee for, acceptance of support, warranty, indemnity, -+ or other liability obligations and/or rights consistent with this -+ License. However, in accepting such obligations, You may act only -+ on Your own behalf and on Your sole responsibility, not on behalf -+ of any other Contributor, and only if You agree to indemnify, -+ defend, and hold each Contributor harmless for any liability -+ incurred by, or claims asserted against, such Contributor by reason -+ of your accepting any such warranty or additional liability. -+ -+ END OF TERMS AND CONDITIONS -+ -+ APPENDIX: How to apply the Apache License to your work. -+ -+ To apply the Apache License to your work, attach the following -+ boilerplate notice, with the fields enclosed by brackets "[]" -+ replaced with your own identifying information. (Don't include -+ the brackets!) The text should be enclosed in the appropriate -+ comment syntax for the file format. We also recommend that a -+ file or class name and description of purpose be included on the -+ same "printed page" as the copyright notice for easier -+ identification within third-party archives. -+ -+ Copyright [yyyy] [name of copyright owner] -+ -+ Licensed 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. -diff --git a/java/maven.embedder/nbproject/project.xml b/java/maven.embedder/nbproject/project.xml -index 8271d91403a2..69494e4f2e05 100644 ---- a/java/maven.embedder/nbproject/project.xml -+++ b/java/maven.embedder/nbproject/project.xml -@@ -305,7 +305,7 @@ - ../maven/lib/commons-lang3-3.12.0.jar - - -- ../maven/lib/failureaccess-1.0.1.jar -+ ../maven/lib/failureaccess-1.0.2.jar - - - ../maven/lib/guava-32.0.1-jre.jar -diff --git a/nbbuild/antsrc/org/netbeans/nbbuild/extlibs/ignored-overlaps b/nbbuild/antsrc/org/netbeans/nbbuild/extlibs/ignored-overlaps -index ec07902509..dd52e08038 100644 ---- a/nbbuild/antsrc/org/netbeans/nbbuild/extlibs/ignored-overlaps -+++ b/nbbuild/antsrc/org/netbeans/nbbuild/extlibs/ignored-overlaps -@@ -26,7 +26,7 @@ ide/db.sql.visualeditor/external/javacc-7.0.10.jar java/performance/external/jav - java/maven.embedder/external/apache-maven-3.9.6-bin.zip ide/slf4j.api/external/slf4j-api-1.7.36.jar - java/maven.embedder/external/apache-maven-3.9.6-bin.zip platform/o.apache.commons.lang3/external/commons-lang3-3.12.0.jar - java/maven.embedder/external/apache-maven-3.9.6-bin.zip platform/o.apache.commons.codec/external/commons-codec-1.16.1.jar --java/maven.embedder/external/apache-maven-3.9.6-bin.zip ide/c.google.guava.failureaccess/external/failureaccess-1.0.2.jar -+java/maven.embedder/external/failureaccess-1.0.2.jar ide/c.google.guava.failureaccess/external/failureaccess-1.0.2.jar - java/maven.embedder/external/apache-maven-3.9.6-bin.zip java/maven.indexer/external/javax.annotation-api-1.3.2.jar - - # Used to parse data during build, but need to as a lib for ide cluster diff --git a/patches/7271.diff b/patches/7271.diff deleted file mode 100644 index dee2a821..00000000 --- a/patches/7271.diff +++ /dev/null @@ -1,31 +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 5349a20e091a..d24c6f5ec98a 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 -@@ -107,18 +107,16 @@ public static void main(String... args) throws IOException, InterruptedException - if ("@types".equals(module.getFileName().toString())) continue; - if ("@esbuild".equals(module.getFileName().toString())) continue; - if ("@microsoft".equals(module.getFileName().toString())) continue; -- if ("@vscode".equals(module.getFileName().toString())) { -- try (DirectoryStream sds = Files.newDirectoryStream(module)) { -- for (Path sModule : sds) { -- checkModule(sModule, sb, tokens2Projects, project2License, bundlesDir, targetDir, externalDir, binariesList); -- } -- } -- continue; -+ Path packageJson = module.resolve("package.json"); -+ if (Files.isReadable(packageJson)) { -+ checkModule(module, sb, tokens2Projects, project2License, bundlesDir, targetDir, externalDir, binariesList); -+ continue; - } -- if ("@ungap".equals(module.getFileName().toString())) { -- module = module.resolve("promise-all-settled"); -+ try (DirectoryStream sds = Files.newDirectoryStream(module)) { -+ for (Path sModule : sds) { -+ checkModule(sModule, sb, tokens2Projects, project2License, bundlesDir, targetDir, externalDir, binariesList); -+ } - } -- checkModule(module, sb, tokens2Projects, project2License, bundlesDir, targetDir, externalDir, binariesList); - } - } - if (sb.length() > 0) { diff --git a/patches/7353.diff b/patches/7353.diff deleted file mode 100644 index ea44d4c8..00000000 --- a/patches/7353.diff +++ /dev/null @@ -1,16 +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 b33ff46f4643..5e5d10079dae 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 -@@ -1022,6 +1022,11 @@ public CompletableFuture>> codeAction(CodeActio - if (err.getEndPosition().getOffset() < lineStartOffset || err.getStartPosition().getOffset() > lineEndOffset) { - continue; - } -+ int lineStart = NbDocument.findLineNumber(doc, startOffset); -+ int errStartLine = NbDocument.findLineNumber(doc, err.getStartPosition().getOffset()); -+ if(errStartLine != lineStart){ -+ continue; -+ } - } - Optional diag = diagnostics.stream().filter(d -> entry.getKey().equals(d.getCode().getLeft())).findFirst(); - org.netbeans.api.lsp.Diagnostic.LazyCodeActions actions = err.getActions(); diff --git a/patches/7368.diff b/patches/7368.diff deleted file mode 100644 index 9ea78b17..00000000 --- a/patches/7368.diff +++ /dev/null @@ -1,507 +0,0 @@ -diff --git a/ide/api.lsp/apichanges.xml b/ide/api.lsp/apichanges.xml -index faa0a1b27335..d3cbe5631476 100644 ---- a/ide/api.lsp/apichanges.xml -+++ b/ide/api.lsp/apichanges.xml -@@ -51,6 +51,19 @@ - - - -+ -+ -+ Adding CodeActionProvider.getSupportedCodeActionKinds method -+ -+ -+ -+ -+ -+ A CodeActionProvider.getSupportedCodeActionKinds method is -+ introduced that allows to specify supported kinds of code actions. -+ -+ -+ - - - Adding ErrorProvider.Context.getHintsConfigFile() method -diff --git a/ide/api.lsp/manifest.mf b/ide/api.lsp/manifest.mf -index ca8e28f14b05..476426e3b2eb 100644 ---- a/ide/api.lsp/manifest.mf -+++ b/ide/api.lsp/manifest.mf -@@ -1,5 +1,5 @@ - Manifest-Version: 1.0 - OpenIDE-Module: org.netbeans.api.lsp/1 - OpenIDE-Module-Localizing-Bundle: org/netbeans/api/lsp/Bundle.properties --OpenIDE-Module-Specification-Version: 1.25 -+OpenIDE-Module-Specification-Version: 1.27 - AutoUpdate-Show-In-Client: false -diff --git a/ide/api.lsp/src/org/netbeans/spi/lsp/CodeActionProvider.java b/ide/api.lsp/src/org/netbeans/spi/lsp/CodeActionProvider.java -index b234018a0d50..3006803ebd9b 100644 ---- a/ide/api.lsp/src/org/netbeans/spi/lsp/CodeActionProvider.java -+++ b/ide/api.lsp/src/org/netbeans/spi/lsp/CodeActionProvider.java -@@ -19,7 +19,9 @@ - package org.netbeans.spi.lsp; - - import java.util.List; -+import java.util.Set; - import javax.swing.text.Document; -+import org.netbeans.api.annotations.common.CheckForNull; - import org.netbeans.api.annotations.common.NonNull; - import org.netbeans.api.lsp.CodeAction; - import org.netbeans.api.lsp.Range; -@@ -42,4 +44,15 @@ public interface CodeActionProvider { - * @since 1.23 - */ - public List getCodeActions(@NonNull Document doc, @NonNull Range range, @NonNull Lookup context); -+ -+ /** -+ * Return the set of code action kinds produced by this provider. May return null -+ * if unknown/all kinds may be produced. -+ * -+ * @return the set of supported code action kinds, or {@code null} -+ * @since 1.27 -+ */ -+ public default @CheckForNull Set getSupportedCodeActionKinds() { -+ return null; -+ } - } -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Utils.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Utils.java -index d818c9315f71..bb69242e7cf0 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Utils.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Utils.java -@@ -33,6 +33,7 @@ - import java.util.List; - import java.util.Locale; - import java.util.Set; -+import java.util.function.Predicate; - import java.util.stream.Collectors; - import javax.lang.model.element.Element; - import javax.lang.model.element.ElementKind; -@@ -571,4 +572,10 @@ public static WorkspaceEdit workspaceEditFromApi(org.netbeans.api.lsp.WorkspaceE - } - return new WorkspaceEdit(documentChanges); - } -+ -+ public static Predicate codeActionKindFilter(List only) { -+ return k -> only == null || -+ only.stream() -+ .anyMatch(o -> k.equals(o) || k.startsWith(o + ".")); -+ } - } -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider.java -index 47eccb3455dd..5a167e313edc 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider.java -@@ -37,6 +37,7 @@ - import org.eclipse.lsp4j.Command; - import org.eclipse.lsp4j.Position; - import org.eclipse.xtext.xbase.lib.Pure; -+import org.netbeans.api.annotations.common.CheckForNull; - import org.netbeans.api.java.source.CompilationInfo; - import org.netbeans.api.java.source.ElementHandle; - import org.netbeans.modules.java.lsp.server.Utils; -@@ -54,6 +55,10 @@ public abstract class CodeActionsProvider { - public static final String DATA = "data"; - protected static final String ERROR = ""; //NOI18N - -+ public @CheckForNull Set getSupportedCodeActionKinds() { -+ return null; -+ } -+ - public abstract List getCodeActions(NbCodeLanguageClient client, ResultIterator resultIterator, CodeActionParams params) throws Exception; - - public CompletableFuture resolve(NbCodeLanguageClient client, CodeAction codeAction, Object data) { -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider2LspApiBridge.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider2LspApiBridge.java -index 7b7f70e9e272..6144650c7c53 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider2LspApiBridge.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider2LspApiBridge.java -@@ -25,6 +25,7 @@ - import java.util.List; - import java.util.Set; - import java.util.concurrent.CompletableFuture; -+import java.util.function.Predicate; - import javax.swing.text.Document; - import javax.swing.text.StyledDocument; - import org.eclipse.lsp4j.CodeAction; -@@ -72,6 +73,23 @@ public CompletableFuture processCommand(NbCodeLanguageClient client, Str - return CompletableFuture.completedFuture(false); - } - -+ @Override -+ public Set getSupportedCodeActionKinds() { -+ Set supportedCodeActionKinds = new HashSet<>(); -+ -+ for (CodeActionProvider caProvider : Lookup.getDefault().lookupAll(CodeActionProvider.class)) { -+ Set providerSupportedCodeActionKinds = caProvider.getSupportedCodeActionKinds(); -+ -+ if (providerSupportedCodeActionKinds == null) { -+ return null; -+ } -+ -+ supportedCodeActionKinds.addAll(providerSupportedCodeActionKinds); -+ } -+ -+ return supportedCodeActionKinds; -+ } -+ - @Override - public List getCodeActions(NbCodeLanguageClient client, ResultIterator resultIterator, CodeActionParams params) throws Exception { - lastCodeActions = new ArrayList<>(); -@@ -84,7 +102,14 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat - org.netbeans.api.lsp.Range r = new org.netbeans.api.lsp.Range(startOffset, endOffset); - List only = params.getContext().getOnly(); - Lookup l = only != null ? Lookups.fixed(client, resultIterator, only) : Lookups.fixed(client, resultIterator); -+ Predicate codeActionKindPermitted = Utils.codeActionKindFilter(only); - for (CodeActionProvider caProvider : Lookup.getDefault().lookupAll(CodeActionProvider.class)) { -+ Set supportedCodeActionKinds = caProvider.getSupportedCodeActionKinds(); -+ if (supportedCodeActionKinds != null && -+ supportedCodeActionKinds.stream() -+ .noneMatch(kind -> codeActionKindPermitted.test(kind))) { -+ continue; -+ } - try { - for (org.netbeans.api.lsp.CodeAction ca : caProvider.getCodeActions(doc, r, l)) { - Object data = null; -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/IntroduceCodeActions.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/IntroduceCodeActions.java -new file mode 100644 -index 000000000000..8fa35abf9038 ---- /dev/null -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/IntroduceCodeActions.java -@@ -0,0 +1,126 @@ -+/* -+ * 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.lsp.server.protocol; -+ -+import java.util.ArrayList; -+import java.util.Arrays; -+import java.util.Collections; -+import java.util.EnumMap; -+import java.util.HashSet; -+import java.util.List; -+import java.util.Set; -+import java.util.concurrent.atomic.AtomicBoolean; -+import javax.swing.text.StyledDocument; -+import org.eclipse.lsp4j.CodeAction; -+import org.eclipse.lsp4j.CodeActionKind; -+import org.eclipse.lsp4j.CodeActionParams; -+import org.eclipse.lsp4j.Command; -+import org.eclipse.lsp4j.Range; -+import org.eclipse.lsp4j.ResourceOperation; -+import org.eclipse.lsp4j.TextDocumentEdit; -+import org.eclipse.lsp4j.TextEdit; -+import org.eclipse.lsp4j.VersionedTextDocumentIdentifier; -+import org.eclipse.lsp4j.WorkspaceEdit; -+import org.eclipse.lsp4j.jsonrpc.messages.Either; -+import org.netbeans.api.java.source.CompilationController; -+import org.netbeans.api.java.source.JavaSource; -+import org.netbeans.api.java.source.ModificationResult; -+import org.netbeans.modules.java.editor.codegen.GeneratorUtils; -+import org.netbeans.modules.java.hints.introduce.IntroduceFixBase; -+import org.netbeans.modules.java.hints.introduce.IntroduceHint; -+import org.netbeans.modules.java.hints.introduce.IntroduceKind; -+import org.netbeans.modules.java.lsp.server.Utils; -+import org.netbeans.modules.parsing.api.ResultIterator; -+import org.netbeans.spi.editor.hints.ErrorDescription; -+import org.netbeans.spi.editor.hints.Fix; -+import org.openide.filesystems.FileObject; -+import org.openide.util.lookup.ServiceProvider; -+ -+@ServiceProvider(service = CodeActionsProvider.class) -+public final class IntroduceCodeActions extends CodeActionsProvider { -+ -+ private static final Set SUPPORTED_CODE_ACTION_KINDS = -+ Collections.unmodifiableSet(new HashSet<>(Arrays.asList(CodeActionKind.RefactorExtract))); -+ -+ public IntroduceCodeActions() { -+ } -+ -+ @Override -+ public Set getSupportedCodeActionKinds() { -+ return SUPPORTED_CODE_ACTION_KINDS; -+ } -+ -+ @Override -+ public List getCodeActions(NbCodeLanguageClient client, ResultIterator resultIterator, CodeActionParams params) throws Exception { -+ Range range = params.getRange(); -+ List result = new ArrayList<>(); -+ -+ if (client.getNbCodeCapabilities().wantsJavaSupport() && !range.getStart().equals(range.getEnd())) { -+ CompilationController cc = resultIterator.getParserResult() != null ? CompilationController.get(resultIterator.getParserResult()) : null; -+ -+ if (cc != null) { -+ cc.toPhase(JavaSource.Phase.RESOLVED); -+ -+ StyledDocument doc = (StyledDocument) cc.getDocument(); -+ int startOffset = Utils.getOffset(doc, range.getStart()); -+ int endOffset = Utils.getOffset(doc, range.getEnd()); -+ -+ for (ErrorDescription err : IntroduceHint.computeError(cc, startOffset, endOffset, new EnumMap(IntroduceKind.class), new EnumMap(IntroduceKind.class), new AtomicBoolean())) { -+ for (Fix fix : err.getFixes().getFixes()) { -+ if (fix instanceof IntroduceFixBase) { -+ try { -+ ModificationResult changes = ((IntroduceFixBase) fix).getModificationResult(); -+ if (changes != null) { -+ List> documentChanges = new ArrayList<>(); -+ Set fos = changes.getModifiedFileObjects(); -+ if (fos.size() == 1) { -+ FileObject fileObject = fos.iterator().next(); -+ List diffs = changes.getDifferences(fileObject); -+ if (diffs != null) { -+ List edits = new ArrayList<>(); -+ for (ModificationResult.Difference diff : diffs) { -+ String newText = diff.getNewText(); -+ edits.add(new TextEdit(new Range(Utils.createPosition(fileObject, diff.getStartPosition().getOffset()), -+ Utils.createPosition(fileObject, diff.getEndPosition().getOffset())), -+ newText != null ? newText : "")); -+ } -+ documentChanges.add(Either.forLeft(new TextDocumentEdit(new VersionedTextDocumentIdentifier(Utils.toUri(fileObject), -1), edits))); -+ } -+ CodeAction codeAction = new CodeAction(fix.getText()); -+ codeAction.setKind(CodeActionKind.RefactorExtract); -+ codeAction.setEdit(new WorkspaceEdit(documentChanges)); -+ int renameOffset = ((IntroduceFixBase) fix).getNameOffset(changes); -+ if (renameOffset >= 0) { -+ codeAction.setCommand(new Command("Rename", client.getNbCodeCapabilities().getCommandPrefix() + ".rename.element.at", Collections.singletonList(renameOffset))); -+ } -+ result.add(codeAction); -+ } -+ } -+ } catch (GeneratorUtils.DuplicateMemberException dme) { -+ } -+ } -+ } -+ } -+ } -+ } -+ -+ return result; -+ } -+ -+} -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 b33ff46f4643..e0cb2c215827 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 -@@ -67,6 +67,7 @@ - import java.util.concurrent.atomic.AtomicReference; - import java.util.function.BiConsumer; - import java.util.function.IntFunction; -+import java.util.function.Predicate; - import java.util.logging.Level; - import java.util.logging.Logger; - import java.util.prefs.Preferences; -@@ -92,6 +93,7 @@ - import org.eclipse.lsp4j.ClientCapabilities; - import org.eclipse.lsp4j.CodeAction; - import org.eclipse.lsp4j.CodeActionKind; -+import org.eclipse.lsp4j.CodeActionKindCapabilities; - import org.eclipse.lsp4j.CodeActionParams; - import org.eclipse.lsp4j.CodeLens; - import org.eclipse.lsp4j.CodeLensParams; -@@ -991,7 +993,9 @@ public CompletableFuture>> codeAction(CodeActio - Range range = params.getRange(); - int startOffset = Utils.getOffset(doc, range.getStart()); - int endOffset = Utils.getOffset(doc, range.getEnd()); -- if (startOffset == endOffset || !params.getContext().getDiagnostics().isEmpty()) { -+ Predicate codeActionKindPermitted = Utils.codeActionKindFilter(params.getContext().getOnly()); -+ if ((startOffset == endOffset || !params.getContext().getDiagnostics().isEmpty()) && -+ (codeActionKindPermitted.test(CodeActionKind.QuickFix) || codeActionKindPermitted.test(CodeActionKind.RefactorRewrite))) { - final javax.swing.text.Element elem = NbDocument.findLineRootElement(doc); - int lineStartOffset = elem.getStartOffset(); - int lineEndOffset = elem.getEndOffset(); -@@ -1031,7 +1035,11 @@ public CompletableFuture>> codeAction(CodeActio - if (diag.isPresent()) { - action.setDiagnostics(Collections.singletonList(diag.get())); - } -- action.setKind(kind(err.getSeverity())); -+ String codeActionKind = kind(err.getSeverity()); -+ if (!codeActionKindPermitted.test(codeActionKind)) { -+ continue; -+ } -+ action.setKind(codeActionKind); - if (inputAction.getCommand() != null) { - List commandParams = new ArrayList<>(); - -@@ -1068,59 +1076,23 @@ public CompletableFuture>> codeAction(CodeActio - public void run(ResultIterator resultIterator) throws Exception { - //code generators: - for (CodeActionsProvider codeGenerator : Lookup.getDefault().lookupAll(CodeActionsProvider.class)) { -+ Set supportedCodeActionKinds = codeGenerator.getSupportedCodeActionKinds(); -+ if (supportedCodeActionKinds != null && -+ supportedCodeActionKinds.stream() -+ .noneMatch(kind -> codeActionKindPermitted.test(kind))) { -+ continue; -+ } - try { - for (CodeAction codeAction : codeGenerator.getCodeActions(client, resultIterator, params)) { -+ if (!codeActionKindPermitted.test(codeAction.getKind())) { -+ continue; -+ } - result.add(Either.forRight(codeAction)); - } - } catch (Exception ex) { - client.logMessage(new MessageParams(MessageType.Error, ex.getMessage())); - } - } -- if (client.getNbCodeCapabilities().wantsJavaSupport()) { -- //introduce hints: -- CompilationController cc = resultIterator.getParserResult() != null ? CompilationController.get(resultIterator.getParserResult()) : null; -- if (cc != null) { -- cc.toPhase(JavaSource.Phase.RESOLVED); -- if (!range.getStart().equals(range.getEnd())) { -- for (ErrorDescription err : IntroduceHint.computeError(cc, startOffset, endOffset, new EnumMap(IntroduceKind.class), new EnumMap(IntroduceKind.class), new AtomicBoolean())) { -- for (Fix fix : err.getFixes().getFixes()) { -- if (fix instanceof IntroduceFixBase) { -- try { -- ModificationResult changes = ((IntroduceFixBase) fix).getModificationResult(); -- if (changes != null) { -- List> documentChanges = new ArrayList<>(); -- Set fos = changes.getModifiedFileObjects(); -- if (fos.size() == 1) { -- FileObject fileObject = fos.iterator().next(); -- List diffs = changes.getDifferences(fileObject); -- if (diffs != null) { -- List edits = new ArrayList<>(); -- for (ModificationResult.Difference diff : diffs) { -- String newText = diff.getNewText(); -- edits.add(new TextEdit(new Range(Utils.createPosition(fileObject, diff.getStartPosition().getOffset()), -- Utils.createPosition(fileObject, diff.getEndPosition().getOffset())), -- newText != null ? newText : "")); -- } -- documentChanges.add(Either.forLeft(new TextDocumentEdit(new VersionedTextDocumentIdentifier(Utils.toUri(fileObject), -1), edits))); -- } -- CodeAction codeAction = new CodeAction(fix.getText()); -- codeAction.setKind(CodeActionKind.RefactorExtract); -- codeAction.setEdit(new WorkspaceEdit(documentChanges)); -- int renameOffset = ((IntroduceFixBase) fix).getNameOffset(changes); -- if (renameOffset >= 0) { -- codeAction.setCommand(new Command("Rename", client.getNbCodeCapabilities().getCommandPrefix() + ".rename.element.at", Collections.singletonList(renameOffset))); -- } -- result.add(Either.forRight(codeAction)); -- } -- } -- } catch (GeneratorUtils.DuplicateMemberException dme) { -- } -- } -- } -- } -- } -- } -- } - } - }); - } catch (ParseException ex) { -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 1b8d71ec96fb..78b327fbec90 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 -@@ -5786,6 +5786,95 @@ public void testDefaultLookupJustOnce() throws Exception { - assertEquals(1, mm4.size()); - } - -+ public void testErrorBasedCodeActionFiltering() throws Exception { -+ File src = new File(getWorkDir(), "Test.java"); -+ src.getParentFile().mkdirs(); -+ String code = "public class Test {\n" + -+ " public void test() {\n" + -+ " System.err.println(0 << 0);\n" + -+ " }\n" + -+ "}\n"; -+ try (Writer w = new FileWriter(src)) { -+ w.write(code); -+ } -+ -+ List[] diags = new List[1]; -+ Launcher serverLauncher = createClientLauncherWithLogging(new LspClient() { -+ private int publishedDiagnosticsCount; -+ @Override -+ public void telemetryEvent(Object arg0) { -+ } -+ -+ @Override -+ public void publishDiagnostics(PublishDiagnosticsParams params) { -+ synchronized (diags) { -+ if (publishedDiagnosticsCount++ == 1) { -+ diags[0] = params.getDiagnostics(); -+ diags.notifyAll(); -+ } -+ } -+ } -+ -+ @Override -+ public void showMessage(MessageParams arg0) { -+ } -+ -+ @Override -+ public CompletableFuture showMessageRequest(ShowMessageRequestParams arg0) { -+ return CompletableFuture.completedFuture(new MessageActionItem(arg0.getActions().get(0).getTitle())); -+ } -+ -+ @Override -+ public void logMessage(MessageParams arg0) { -+ throw new UnsupportedOperationException("Not supported yet."); -+ } -+ -+ @Override -+ public CompletableFuture applyEdit(ApplyWorkspaceEditParams params) { -+ throw new UnsupportedOperationException("Not supported yet."); -+ } -+ -+ }, client.getInputStream(), client.getOutputStream()); -+ serverLauncher.startListening(); -+ LanguageServer server = serverLauncher.getRemoteProxy(); -+ server.initialize(new InitializeParams()).get(); -+ String uri = src.toURI().toString(); -+ server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(uri, "java", 0, code))); -+ synchronized (diags) { -+ while (diags[0] == null) { -+ try { -+ diags.wait(); -+ } catch (InterruptedException ex) { -+ } -+ } -+ } -+ VersionedTextDocumentIdentifier id = new VersionedTextDocumentIdentifier(src.toURI().toString(), 1); -+ Set presentKinds; -+ List> codeActions; -+ codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 32), new Position(2, 32)), new CodeActionContext(Collections.emptyList(), null))).get(); -+ presentKinds = codeActions.stream().map(d -> d.getRight().getKind()).collect(Collectors.toSet()); -+ assertEquals(new HashSet<>(Arrays.asList(CodeActionKind.RefactorRewrite, CodeActionKind.QuickFix)), presentKinds); -+ codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 32), new Position(2, 32)), new CodeActionContext(Collections.emptyList(), Arrays.asList(CodeActionKind.RefactorRewrite)))).get(); -+ presentKinds = codeActions.stream().map(d -> d.getRight().getKind()).collect(Collectors.toSet()); -+ assertEquals(new HashSet<>(Arrays.asList(CodeActionKind.RefactorRewrite)), presentKinds); -+ codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 32), new Position(2, 32)), new CodeActionContext(Collections.emptyList(), Arrays.asList(CodeActionKind.Refactor)))).get(); -+ presentKinds = codeActions.stream().map(d -> d.getRight().getKind()).collect(Collectors.toSet()); -+ assertEquals(new HashSet<>(Arrays.asList(CodeActionKind.RefactorRewrite)), presentKinds); -+ codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 32), new Position(2, 32)), new CodeActionContext(Collections.emptyList(), Arrays.asList(CodeActionKind.QuickFix)))).get(); -+ presentKinds = codeActions.stream().map(d -> d.getRight().getKind()).collect(Collectors.toSet()); -+ assertEquals(new HashSet<>(Arrays.asList(CodeActionKind.QuickFix)), presentKinds); -+ codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 32), new Position(2, 32)), new CodeActionContext(Collections.emptyList(), Arrays.asList(CodeActionKind.RefactorExtract)))).get(); -+ presentKinds = codeActions.stream().map(d -> d.getRight().getKind()).collect(Collectors.toSet()); -+ assertEquals(new HashSet<>(Arrays.asList()), presentKinds); -+ codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 27), new Position(2, 33)), new CodeActionContext(Collections.emptyList(), Arrays.asList(CodeActionKind.RefactorExtract)))).get(); -+ presentKinds = codeActions.stream().map(d -> d.getRight().getKind()).collect(Collectors.toSet()); -+ assertEquals(new HashSet<>(Arrays.asList(CodeActionKind.RefactorExtract)), presentKinds); -+ //verify surround-with hints are correctly filtered: -+ codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 0), new Position(3, 0)), new CodeActionContext(Collections.emptyList(), Arrays.asList(CodeActionKind.RefactorExtract)))).get(); -+ presentKinds = codeActions.stream().map(d -> d.getRight().getKind()).collect(Collectors.toSet()); -+ assertEquals(new HashSet<>(Arrays.asList(CodeActionKind.RefactorExtract)), presentKinds); -+ } -+ - static { - System.setProperty("SourcePath.no.source.filter", "true"); - JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true; diff --git a/patches/7370.diff b/patches/7370.diff deleted file mode 100644 index a36ed8ac..00000000 --- a/patches/7370.diff +++ /dev/null @@ -1,135 +0,0 @@ -diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/infrastructure/JavaErrorProvider.java b/java/java.hints/src/org/netbeans/modules/java/hints/infrastructure/JavaErrorProvider.java -index c8725dce9fdb..dbccee227fb5 100644 ---- a/java/java.hints/src/org/netbeans/modules/java/hints/infrastructure/JavaErrorProvider.java -+++ b/java/java.hints/src/org/netbeans/modules/java/hints/infrastructure/JavaErrorProvider.java -@@ -206,7 +206,7 @@ private static List convertFixes(ErrorDescription err, Consumer - - -+ -+ -+ Result.parseLine permits parsing of argfiles based on current working directory. -+ -+ -+ -+ -+ -+

-+ CompilerOptionsQueryImplementation.Result.parseLine is enhanced with the ability to parse -+ argfiles, based on a specified current working directory -+

-+
-+ -+
- - - Allows SourceJavadocAttacherImplementation.Definer to reject a binary root. -diff --git a/java/api.java/manifest.mf b/java/api.java/manifest.mf -index 56d512e79aa2..1c9b76387829 100644 ---- a/java/api.java/manifest.mf -+++ b/java/api.java/manifest.mf -@@ -1,6 +1,6 @@ - Manifest-Version: 1.0 - OpenIDE-Module: org.netbeans.api.java/1 --OpenIDE-Module-Specification-Version: 1.90 -+OpenIDE-Module-Specification-Version: 1.91 - OpenIDE-Module-Localizing-Bundle: org/netbeans/api/java/queries/Bundle.properties - AutoUpdate-Show-In-Client: false - -diff --git a/java/api.java/src/org/netbeans/spi/java/queries/CompilerOptionsQueryImplementation.java b/java/api.java/src/org/netbeans/spi/java/queries/CompilerOptionsQueryImplementation.java -index bc35eec9cda7..6f678a8c0658 100644 ---- a/java/api.java/src/org/netbeans/spi/java/queries/CompilerOptionsQueryImplementation.java -+++ b/java/api.java/src/org/netbeans/spi/java/queries/CompilerOptionsQueryImplementation.java -@@ -18,13 +18,20 @@ - */ - package org.netbeans.spi.java.queries; - -+import java.io.IOException; -+import java.net.URI; - import java.util.ArrayList; - import java.util.Collections; - import java.util.List; -+import java.util.function.Consumer; -+import java.util.logging.Level; -+import java.util.logging.Logger; - import javax.swing.event.ChangeListener; - import org.netbeans.api.annotations.common.CheckForNull; - import org.netbeans.api.annotations.common.NonNull; -+import org.netbeans.api.annotations.common.NullAllowed; - import org.openide.filesystems.FileObject; -+import org.openide.filesystems.URLMapper; - - /** - * Permits providers to return explicit compiler options for Java source file. -@@ -47,6 +54,8 @@ public interface CompilerOptionsQueryImplementation { - * ability to listen to it. - */ - public abstract static class Result { -+ private static final Logger LOG = Logger.getLogger(CompilerOptionsQueryImplementation.class.getName()); -+ - /** - * Gets the explicit compiler options. - * @return the list of the compiler options -@@ -72,9 +81,29 @@ public abstract static class Result { - * @return a list of command line arguments - */ - protected final List parseLine(@NonNull final String commandLine) { -+ return doParseLine(commandLine, null); -+ } -+ -+ /** -+ * Utility method the tokenize the command line into individual arguments. -+ * @param commandLine the command line to be tokenized -+ * @param workingDirectory if set to null, argument files will not be supported; -+ * if non-null, argument file names will be resolved relative to this directory -+ * @return a list of command line arguments -+ * @since 1.92 -+ */ -+ protected final List parseLine(@NonNull final String commandLine, -+ @NullAllowed URI workingDirectory) { -+ return doParseLine(commandLine, workingDirectory); -+ } -+ -+ static List doParseLine(@NonNull final String commandLine, -+ @NullAllowed URI workingDirectory) { - final List result = new ArrayList<>(); - StringBuilder current = new StringBuilder(); - boolean escape = false, doubleQuote = false, quote = false; -+ Consumer defaultHandleOption = result::add; -+ Consumer handleOption = defaultHandleOption; - for (int i = 0; i < commandLine.length(); i++) { - final char c = commandLine.charAt(i); - switch (c) { -@@ -99,7 +128,8 @@ protected final List parseLine(@NonNull final String commandLine) { - case '\t': //NOI18N - if (!escape && !quote && !doubleQuote) { - if (current.length() > 0) { -- result.add(current.toString()); -+ handleOption.accept(current.toString()); -+ handleOption = defaultHandleOption; - current = new StringBuilder(); - } - } else { -@@ -107,6 +137,27 @@ protected final List parseLine(@NonNull final String commandLine) { - } - escape = false; - break; -+ case '@': -+ if (workingDirectory != null && i + 1 < commandLine.length() && commandLine.charAt(i + 1) != '@' && current.length() == 0) { -+ handleOption = path -> { -+ try { -+ URI resolved = workingDirectory.resolve(path); -+ FileObject file = URLMapper.findFileObject(resolved.toURL()); -+ if (file == null) { -+ LOG.log(Level.FINE, "URI {0}, resolved to {1}, did not yield an existing file", new Object[] {path, resolved.toString()}); -+ result.add("@" + path); -+ return ; -+ } -+ for (String line : file.asLines()) { -+ result.addAll(doParseLine(line, null)); -+ } -+ } catch (IOException ex) { -+ LOG.log(Level.FINE, null, ex); -+ } -+ }; -+ break; -+ } -+ //fall-through - default: - current.append(c); - escape = false; -@@ -114,7 +165,7 @@ protected final List parseLine(@NonNull final String commandLine) { - } - } - if (current.length() > 0) { -- result.add(current.toString()); -+ handleOption.accept(current.toString()); - } - return Collections.unmodifiableList(result); - } -diff --git a/java/api.java/test/unit/src/org/netbeans/spi/java/queries/CompilerOptionsQueryImplementationTest.java b/java/api.java/test/unit/src/org/netbeans/spi/java/queries/CompilerOptionsQueryImplementationTest.java -new file mode 100644 -index 000000000000..29fff1485d40 ---- /dev/null -+++ b/java/api.java/test/unit/src/org/netbeans/spi/java/queries/CompilerOptionsQueryImplementationTest.java -@@ -0,0 +1,60 @@ -+/* -+ * 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.spi.java.queries; -+ -+import java.io.OutputStream; -+import java.nio.charset.StandardCharsets; -+import java.util.Arrays; -+import org.junit.Test; -+import org.netbeans.junit.NbTestCase; -+import org.netbeans.spi.java.queries.CompilerOptionsQueryImplementation.Result; -+import org.openide.filesystems.FileObject; -+import org.openide.filesystems.FileUtil; -+ -+public class CompilerOptionsQueryImplementationTest extends NbTestCase { -+ -+ public CompilerOptionsQueryImplementationTest(String name) { -+ super(name); -+ } -+ -+ @Test -+ public void testArgumentFiles() throws Exception { -+ FileObject wd = FileUtil.toFileObject(getWorkDir()); -+ FileObject argfile = FileUtil.createData(wd, "argfile"); -+ try (OutputStream out = argfile.getOutputStream()) { -+ out.write("test \t\t \"quoted1\" 'quoted2'\n \t\n @argfile\n".getBytes(StandardCharsets.UTF_8)); -+ } -+ -+ assertEquals(Arrays.asList("test", "quoted1", "quoted2", "@argfile"), -+ Result.doParseLine("@argfile", wd.toURI())); -+ assertEquals(Arrays.asList("@argfile"), -+ Result.doParseLine("@argfile", null)); -+ assertEquals(Arrays.asList("prefix@argfile"), -+ Result.doParseLine("prefix@argfile", wd.toURI())); -+ assertEquals(Arrays.asList("@@"), -+ Result.doParseLine("@@", wd.toURI())); -+ assertEquals(Arrays.asList("@"), -+ Result.doParseLine("@", wd.toURI())); -+ assertEquals(Arrays.asList("test", "quoted1", "quoted2", "@argfile"), -+ Result.doParseLine("@" + FileUtil.toFile(argfile).getAbsolutePath(), wd.toURI())); -+ assertEquals(Arrays.asList("@nonexistent"), -+ Result.doParseLine("@nonexistent", wd.toURI())); -+ } -+ -+} -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 cfa6c4d209aa..65e6cd433a03 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 -@@ -18,6 +18,7 @@ - */ - package org.netbeans.modules.java.file.launcher; - -+import java.net.URI; - import javax.swing.event.ChangeListener; - import org.netbeans.modules.java.file.launcher.queries.MultiSourceRootProvider; - import org.netbeans.modules.java.file.launcher.spi.SingleFileOptionsQueryImplementation; -@@ -94,6 +95,11 @@ public String getOptions() { - return vmOptionsObj != null ? (String) vmOptionsObj : ""; - } - -+ @Override -+ public URI getWorkDirectory() { -+ return root != null ? root.toURI() : source.getParent().toURI(); -+ } -+ - @Override - public void addChangeListener(ChangeListener listener) { - cs.addChangeListener(listener); -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 c3404b86cf8a..8ca36068a060 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 -@@ -20,23 +20,26 @@ - - import java.io.File; - import java.io.IOException; -+import java.net.URI; - import java.util.ArrayList; - import java.util.Arrays; -+import java.util.Collections; - import java.util.List; -+import java.util.concurrent.atomic.AtomicInteger; - import java.util.logging.Level; - import java.util.logging.Logger; -+import javax.swing.event.ChangeEvent; - import javax.swing.event.ChangeListener; - import org.netbeans.api.java.platform.JavaPlatformManager; - import org.netbeans.api.project.FileOwnerQuery; --import org.netbeans.api.project.Project; - import org.netbeans.modules.java.file.launcher.queries.MultiSourceRootProvider; - import org.netbeans.modules.java.file.launcher.spi.SingleFileOptionsQueryImplementation; --import org.netbeans.modules.java.file.launcher.spi.SingleFileOptionsQueryImplementation.Result; - import org.netbeans.spi.java.queries.CompilerOptionsQueryImplementation; - import org.openide.filesystems.FileObject; - import org.openide.filesystems.FileStateInvalidException; - import org.openide.filesystems.FileUtil; - import org.openide.loaders.DataObject; -+import org.openide.util.ChangeSupport; - import org.openide.util.Lookup; - - /** -@@ -127,30 +130,31 @@ public static boolean hasClassSibling(FileObject fo) { - return fo.getParent().getFileObject(fo.getName(), "class") != null; - } - -- public static Result getOptionsFor(FileObject file) { -+ public static ParsedFileOptions getOptionsFor(FileObject file) { - if (MultiSourceRootProvider.DISABLE_MULTI_SOURCE_ROOT) { - return null; - } - - for (SingleFileOptionsQueryImplementation i : Lookup.getDefault().lookupAll(SingleFileOptionsQueryImplementation.class)) { -- Result r = i.optionsFor(file); -+ SingleFileOptionsQueryImplementation.Result r = i.optionsFor(file); - - if (r != null) { -- return r; -+ return new ParsedFileOptions(r); - } - } -+ - return null; - } - -- public static List parseLine(String line) { -- return PARSER.doParse(line); -+ public static List parseLine(String line, URI workingDirectory) { -+ return PARSER.doParse(line, workingDirectory); - } - - private static final LineParser PARSER = new LineParser(); - - private static class LineParser extends CompilerOptionsQueryImplementation.Result { -- public List doParse(String line) { -- return parseLine(line); -+ public List doParse(String line, URI workingDirectory) { -+ return parseLine(line, workingDirectory); - } - - @Override -@@ -165,4 +169,69 @@ public void addChangeListener(ChangeListener listener) {} - public void removeChangeListener(ChangeListener listener) {} - } - -+ public static final class ParsedFileOptions extends CompilerOptionsQueryImplementation.Result implements ChangeListener { -+ -+ private final ChangeSupport cs; -+ private final SingleFileOptionsQueryImplementation.Result delegate; -+ private final AtomicInteger updateCount = new AtomicInteger(0); -+ private List arguments; -+ -+ private ParsedFileOptions(SingleFileOptionsQueryImplementation.Result delegate) { -+ this.cs = new ChangeSupport(this); -+ this.delegate = delegate; -+ this.delegate.addChangeListener(this); -+ } -+ -+ @Override -+ public List getArguments() { -+ int update; -+ synchronized (this) { -+ if (arguments != null) { -+ return arguments; -+ } -+ -+ update = updateCount.get(); -+ } -+ -+ while (true) { -+ List newArguments = -+ Collections.unmodifiableList(parseLine(delegate.getOptions(), -+ delegate.getWorkDirectory())); -+ -+ synchronized (this) { -+ if (update == updateCount.get()) { -+ arguments = newArguments; -+ return newArguments; -+ } -+ -+ //changed in the mean time, try again: -+ update = updateCount.get(); -+ } -+ } -+ } -+ -+ public URI getWorkDirectory() { -+ return delegate.getWorkDirectory(); -+ } -+ -+ @Override -+ public void addChangeListener(ChangeListener listener) { -+ cs.addChangeListener(listener); -+ } -+ -+ @Override -+ public void removeChangeListener(ChangeListener listener) { -+ cs.removeChangeListener(listener); -+ } -+ -+ @Override -+ public void stateChanged(ChangeEvent ce) { -+ synchronized (this) { -+ arguments = null; -+ updateCount.incrementAndGet(); -+ } -+ -+ cs.fireChange(); -+ } -+ } - } -diff --git a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/actions/LaunchProcess.java b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/actions/LaunchProcess.java -index 7646ab59554d..51e6b9182f5a 100644 ---- a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/actions/LaunchProcess.java -+++ b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/actions/LaunchProcess.java -@@ -20,6 +20,7 @@ - - import java.io.File; - import java.io.IOException; -+import java.net.URI; - import java.util.ArrayList; - import java.util.Arrays; - import java.util.List; -@@ -31,6 +32,7 @@ - import org.openide.filesystems.FileObject; - import org.openide.filesystems.FileUtil; - import org.openide.util.BaseUtilities; -+import org.openide.util.Utilities; - - final class LaunchProcess implements Callable { - -@@ -69,12 +71,14 @@ private Process setupProcess(String port) throws InterruptedException { - FileObject java = JavaPlatformManager.getDefault().getDefaultPlatform().findTool("java"); //NOI18N - File javaFile = FileUtil.toFile(java); - String javaPath = javaFile.getAbsolutePath(); -+ URI cwd = SingleSourceFileUtil.getOptionsFor(fileObject).getWorkDirectory(); -+ File workDir = Utilities.toFile(cwd); - - ExplicitProcessParameters paramsFromAttributes = - ExplicitProcessParameters.builder() - .args(readArgumentsFromAttribute(fileObject, SingleSourceFileUtil.FILE_ARGUMENTS)) - .launcherArgs(readArgumentsFromAttribute(fileObject, SingleSourceFileUtil.FILE_VM_OPTIONS)) -- .workingDirectory(FileUtil.toFile(fileObject.getParent())) -+ .workingDirectory(workDir) - .build(); - - ExplicitProcessParameters realParameters = -@@ -97,7 +101,7 @@ private Process setupProcess(String port) throws InterruptedException { - commandsList.add(FileUtil.toFile(fileObject.getParent()).toString()); - commandsList.add(fileObject.getName()); - } else { -- commandsList.add(fileObject.getNameExt()); -+ commandsList.add(FileUtil.toFile(fileObject).getAbsolutePath()); - } - - if (realParameters.getArguments() != null) { -diff --git a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/api/SourceLauncher.java b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/api/SourceLauncher.java -index 8603ed6b899b..c09b89592561 100644 ---- a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/api/SourceLauncher.java -+++ b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/api/SourceLauncher.java -@@ -59,7 +59,7 @@ public static String joinCommandLines(Iterable inputLines) { - Map joinedOptions = new HashMap<>(); - - for (String value : inputLines) { -- List args = SingleSourceFileUtil.parseLine(value); -+ List args = SingleSourceFileUtil.parseLine(value, null); - - for (int i = 0; i < args.size(); i++) { - switch (args.get(i)) { -diff --git a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/LauncherSourceLevelQueryImpl.java b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/LauncherSourceLevelQueryImpl.java -index f84258ecce93..2d0452e3f679 100644 ---- a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/LauncherSourceLevelQueryImpl.java -+++ b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/LauncherSourceLevelQueryImpl.java -@@ -24,7 +24,7 @@ - import javax.swing.event.ChangeListener; - import org.netbeans.api.java.platform.JavaPlatformManager; - import org.netbeans.modules.java.file.launcher.SingleSourceFileUtil; --import org.netbeans.modules.java.file.launcher.spi.SingleFileOptionsQueryImplementation; -+import org.netbeans.modules.java.file.launcher.SingleSourceFileUtil.ParsedFileOptions; - import org.netbeans.spi.java.queries.SourceLevelQueryImplementation2; - import org.openide.filesystems.FileObject; - import org.openide.util.ChangeSupport; -@@ -39,7 +39,7 @@ public class LauncherSourceLevelQueryImpl implements SourceLevelQueryImplementat - - @Override - public Result getSourceLevel(FileObject javaFile) { -- SingleFileOptionsQueryImplementation.Result delegate = SingleSourceFileUtil.getOptionsFor(javaFile); -+ ParsedFileOptions delegate = SingleSourceFileUtil.getOptionsFor(javaFile); - - if (delegate != null) { - return new ResultImpl(delegate); -@@ -54,17 +54,17 @@ private static final class ResultImpl implements ChangeListener, Result { - JavaPlatformManager.getDefault().getDefaultPlatform().getSpecification().getVersion().toString(); - - private final ChangeSupport cs = new ChangeSupport(this); -- private final SingleFileOptionsQueryImplementation.Result delegate; -+ private final ParsedFileOptions delegate; - private String sourceLevel; - -- public ResultImpl(SingleFileOptionsQueryImplementation.Result delegate) { -+ public ResultImpl(ParsedFileOptions delegate) { - this.delegate = delegate; - this.delegate.addChangeListener(this); - updateDelegate(); - } - - private void updateDelegate() { -- List parsed = SingleSourceFileUtil.parseLine(delegate.getOptions()); -+ List parsed = delegate.getArguments(); - String sourceLevel = DEFAULT_SOURCE_LEVEL; - - for (int i = 0; i < parsed.size(); i++) { -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 e5ed4166dc2d..7793d0663d79 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 -@@ -20,6 +20,7 @@ - - import java.beans.PropertyChangeListener; - import java.beans.PropertyChangeSupport; -+import java.io.File; - import java.io.IOException; - import java.net.URL; - import java.util.ArrayList; -@@ -47,7 +48,7 @@ - import org.netbeans.api.project.FileOwnerQuery; - import org.netbeans.api.queries.FileEncodingQuery; - import org.netbeans.modules.java.file.launcher.SingleSourceFileUtil; --import org.netbeans.modules.java.file.launcher.spi.SingleFileOptionsQueryImplementation; -+import org.netbeans.modules.java.file.launcher.SingleSourceFileUtil.ParsedFileOptions; - import org.netbeans.spi.java.classpath.ClassPathFactory; - import org.netbeans.spi.java.classpath.ClassPathImplementation; - -@@ -57,10 +58,16 @@ - import org.netbeans.spi.java.classpath.FilteringPathResourceImplementation; - import org.netbeans.spi.java.classpath.PathResourceImplementation; - import org.netbeans.spi.java.classpath.support.ClassPathSupport; -+import org.openide.filesystems.FileChangeAdapter; -+import org.openide.filesystems.FileEvent; - import org.openide.filesystems.FileObject; -+import org.openide.filesystems.FileRenameEvent; - import org.openide.filesystems.FileUtil; - import org.openide.filesystems.URLMapper; - import org.openide.util.NbBundle.Messages; -+import org.openide.util.RequestProcessor; -+import org.openide.util.RequestProcessor.Task; -+import org.openide.util.Utilities; - import org.openide.util.lookup.ServiceProvider; - import org.openide.util.lookup.ServiceProviders; - -@@ -70,9 +77,15 @@ - }) - public class MultiSourceRootProvider implements ClassPathProvider { - -+ private static final RequestProcessor WORKER = new RequestProcessor(MultiSourceRootProvider.class.getName(), 1, false, false); - private static final Logger LOG = Logger.getLogger(MultiSourceRootProvider.class.getName()); - - public static boolean DISABLE_MULTI_SOURCE_ROOT = Boolean.getBoolean("java.disable.multi.source.root"); -+ public static boolean SYNCHRONOUS_UPDATES = false; -+ -+ private static final Set MODULAR_DIRECTORY_OPTIONS = new HashSet<>(Arrays.asList( -+ "--module-path", "-p" -+ )); - - //TODO: the cache will probably be never cleared, as the ClassPath/value refers to the key(?) - private Map file2SourceCP = new WeakHashMap<>(); -@@ -234,7 +247,7 @@ private ClassPath attributeBasedPath(FileObject file, Map - - synchronized (this) { - return file2ClassPath.computeIfAbsent(file, f -> { -- SingleFileOptionsQueryImplementation.Result delegate = SingleSourceFileUtil.getOptionsFor(f); -+ ParsedFileOptions delegate = SingleSourceFileUtil.getOptionsFor(f); - - if (delegate == null) { - return null; -@@ -253,14 +266,16 @@ private static boolean registerRoot(FileObject root) { - return "true".equals(Bundle.SETTING_AutoRegisterAsRoot()); - } - -- private static final class AttributeBasedClassPathImplementation implements ChangeListener, ClassPathImplementation { -+ private static final class AttributeBasedClassPathImplementation extends FileChangeAdapter implements ChangeListener, ClassPathImplementation { - private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); -- private final SingleFileOptionsQueryImplementation.Result delegate; -+ private final Task updateDelegatesTask = WORKER.create(this::doUpdateDelegates); -+ private final Set directoriesWithListener = new HashSet<>(); -+ private final ParsedFileOptions delegate; - private final Set optionKeys; - private Set currentURLs; - private List delegates = Collections.emptyList(); - -- public AttributeBasedClassPathImplementation(SingleFileOptionsQueryImplementation.Result delegate, String... optionKeys) { -+ public AttributeBasedClassPathImplementation(ParsedFileOptions delegate, String... optionKeys) { - this.delegate = delegate; - this.optionKeys = new HashSet<>(Arrays.asList(optionKeys)); - delegate.addChangeListener(this); -@@ -273,29 +288,84 @@ public void stateChanged(ChangeEvent ce) { - } - - private void updateDelegates() { -+ if (SYNCHRONOUS_UPDATES) { -+ doUpdateDelegates(); -+ } else { -+ updateDelegatesTask.schedule(0); -+ } -+ } -+ -+ private void doUpdateDelegates() { - Set newURLs = new HashSet<>(); - List newDelegates = new ArrayList<>(); -- List parsed = SingleSourceFileUtil.parseLine(delegate.getOptions()); -- -- for (int i = 0; i < parsed.size(); i++) { -- if (optionKeys.contains(parsed.get(i)) && i + 1 < parsed.size()) { -- ClassPathSupport.createClassPath(parsed.get(i + 1)) -- .entries() -- .stream() -- .map(e -> e.getURL()) -- .forEach(u -> { -+ List parsed = delegate.getArguments(); -+ File workDirectory = Utilities.toFile(delegate.getWorkDirectory()); -+ Set toRemoveFSListeners = new HashSet<>(); -+ Set addedFSListeners = new HashSet<>(); -+ -+ synchronized (this) { -+ toRemoveFSListeners.addAll(directoriesWithListener); -+ } -+ -+ for (int i = 0; i < parsed.size() - 1; i++) { -+ String currentOption = parsed.get(i); -+ -+ if (optionKeys.contains(currentOption)) { -+ for (String piece : parsed.get(i + 1).split(File.pathSeparator)) { -+ File pieceFile = new File(piece); -+ -+ if (!pieceFile.isAbsolute()) { -+ pieceFile = new File(workDirectory, piece); -+ } -+ -+ File f = FileUtil.normalizeFile(pieceFile); -+ Iterable expandedPaths; -+ -+ if (MODULAR_DIRECTORY_OPTIONS.contains(currentOption)) { -+ if (!toRemoveFSListeners.remove(f.getAbsolutePath()) && -+ addedFSListeners.add(f.getAbsolutePath())) { -+ FileUtil.addFileChangeListener(this, f); -+ } -+ } -+ -+ if (MODULAR_DIRECTORY_OPTIONS.contains(currentOption) && -+ f.isDirectory() && -+ !new File(f, "module-info.class").exists()) { -+ File[] children = f.listFiles(); -+ -+ if (children != null) { -+ expandedPaths = Arrays.asList(children); -+ } else { -+ expandedPaths = Collections.emptyList(); -+ } -+ } else { -+ expandedPaths = Arrays.asList(f); -+ } -+ -+ for (File expanded : expandedPaths) { -+ URL u = FileUtil.urlForArchiveOrDir(expanded); -+ if (u == null) { -+ throw new IllegalArgumentException("Path entry looks to be invalid: " + piece); // NOI18N -+ } - newURLs.add(u); - newDelegates.add(ClassPathSupport.createResource(u)); -- }); -+ } -+ } - } - } - -+ for (String removeFSListener : toRemoveFSListeners) { -+ FileUtil.removeFileChangeListener(this, new File(removeFSListener)); -+ } -+ - synchronized (this) { - if (Objects.equals(currentURLs, newURLs)) { - return ; - } - this.currentURLs = newURLs; - this.delegates = newDelegates; -+ this.directoriesWithListener.removeAll(toRemoveFSListeners); -+ this.directoriesWithListener.addAll(addedFSListeners); - } - - pcs.firePropertyChange(PROP_RESOURCES, null, null); -@@ -316,6 +386,26 @@ public void removePropertyChangeListener(PropertyChangeListener listener) { - pcs.removePropertyChangeListener(listener); - } - -+ @Override -+ public void fileDataCreated(FileEvent fe) { -+ updateDelegates(); -+ } -+ -+ @Override -+ public void fileDeleted(FileEvent fe) { -+ updateDelegates(); -+ } -+ -+ @Override -+ public void fileFolderCreated(FileEvent fe) { -+ updateDelegates(); -+ } -+ -+ @Override -+ public void fileRenamed(FileRenameEvent fe) { -+ updateDelegates(); -+ } -+ - } - - private static final class RootPathResourceImplementation implements FilteringPathResourceImplementation { -diff --git a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/SingleSourceCompilerOptQueryImpl.java b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/SingleSourceCompilerOptQueryImpl.java -index 28d08d914e16..c6e9b64bb69e 100644 ---- a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/SingleSourceCompilerOptQueryImpl.java -+++ b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/SingleSourceCompilerOptQueryImpl.java -@@ -18,14 +18,9 @@ - */ - package org.netbeans.modules.java.file.launcher.queries; - --import java.util.List; --import javax.swing.event.ChangeEvent; --import javax.swing.event.ChangeListener; - import org.netbeans.modules.java.file.launcher.SingleSourceFileUtil; --import org.netbeans.modules.java.file.launcher.spi.SingleFileOptionsQueryImplementation; - import org.netbeans.spi.java.queries.CompilerOptionsQueryImplementation; - import org.openide.filesystems.FileObject; --import org.openide.util.ChangeSupport; - import org.openide.util.lookup.ServiceProvider; - - /** -@@ -35,46 +30,11 @@ - @ServiceProvider(service = CompilerOptionsQueryImplementation.class, position = 100) - public class SingleSourceCompilerOptQueryImpl implements CompilerOptionsQueryImplementation { - -+ public static final SingleSourceCompilerOptQueryImpl INSTANCE = new SingleSourceCompilerOptQueryImpl(); -+ - @Override - public CompilerOptionsQueryImplementation.Result getOptions(FileObject file) { -- SingleFileOptionsQueryImplementation.Result delegate = SingleSourceFileUtil.getOptionsFor(file); -- -- if (delegate != null) { -- return new ResultImpl(delegate); -- } else { -- return null; -- } -+ return SingleSourceFileUtil.getOptionsFor(file); - } - -- private static final class ResultImpl extends CompilerOptionsQueryImplementation.Result implements ChangeListener { -- -- private final ChangeSupport cs; -- private final SingleFileOptionsQueryImplementation.Result delegate; -- -- ResultImpl(SingleFileOptionsQueryImplementation.Result delegate) { -- this.cs = new ChangeSupport(this); -- this.delegate = delegate; -- this.delegate.addChangeListener(this); -- } -- -- @Override -- public List getArguments() { -- return parseLine(delegate.getOptions()); -- } -- -- @Override -- public void addChangeListener(ChangeListener listener) { -- cs.addChangeListener(listener); -- } -- -- @Override -- public void removeChangeListener(ChangeListener listener) { -- cs.removeChangeListener(listener); -- } -- -- @Override -- public void stateChanged(ChangeEvent ce) { -- cs.fireChange(); -- } -- } - } -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 ed29d610d89d..216706463c3b 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 -@@ -18,7 +18,9 @@ - */ - package org.netbeans.modules.java.file.launcher.spi; - -+import java.net.URI; - import javax.swing.event.ChangeListener; -+import org.netbeans.api.annotations.common.NonNull; - import org.openide.filesystems.FileObject; - - public interface SingleFileOptionsQueryImplementation { -@@ -27,6 +29,9 @@ public interface SingleFileOptionsQueryImplementation { - - public interface Result { - public String getOptions(); -+ public default @NonNull URI getWorkDirectory() { -+ throw new UnsupportedOperationException(); -+ } - public void addChangeListener(ChangeListener l); - public void removeChangeListener(ChangeListener l); - } -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 48083a04b762..61694bb7845a 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 -@@ -18,11 +18,24 @@ - */ - package org.netbeans.modules.java.file.launcher.queries; - -+import java.net.URI; -+import java.util.Arrays; -+import java.util.HashSet; -+import java.util.concurrent.atomic.AtomicInteger; -+import java.util.concurrent.atomic.AtomicReference; -+import javax.swing.event.ChangeListener; - import org.netbeans.api.java.classpath.ClassPath; -+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.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.Lookup; -+import org.openide.util.lookup.Lookups; -+import org.openide.util.lookup.ProxyLookup; - - /** - * -@@ -30,6 +43,8 @@ - */ - public class MultiSourceRootProviderTest extends NbTestCase { - -+ private final TestResultImpl testResult = new TestResultImpl(); -+ - public MultiSourceRootProviderTest(String name) { - super(name); - } -@@ -40,8 +55,6 @@ public void testFindPackage() { - } - - public void testSourcePathFiltering() throws Exception { -- clearWorkDir(); -- - FileObject wd = FileUtil.toFileObject(getWorkDir()); - FileObject validTest = FileUtil.createData(wd, "valid/pack/Test1.java"); - FileObject invalidTest1 = FileUtil.createData(wd, "valid/pack/Test2.java"); -@@ -61,4 +74,243 @@ public void testSourcePathFiltering() throws Exception { - assertNull(provider.findClassPath(invalidTest1, ClassPath.SOURCE)); - assertNull(provider.findClassPath(invalidTest2, ClassPath.SOURCE)); - } -+ -+ public void testRelativePaths() throws Exception { -+ FileObject wd = FileUtil.toFileObject(getWorkDir()); -+ FileObject test = FileUtil.createData(wd, "src/pack/Test1.java"); -+ FileObject libJar = FileUtil.createData(wd, "libs/lib.jar"); -+ FileObject other = FileUtil.createFolder(wd, "other"); -+ FileObject otherLibJar = FileUtil.createData(other, "libs/lib.jar"); -+ FileObject otherLib2Jar = FileUtil.createData(other, "libs/lib2.jar"); -+ -+ TestUtilities.copyStringToFile(test, "package pack;"); -+ -+ testResult.setOptions("--class-path libs/lib.jar"); -+ testResult.setWorkDirectory(wd.toURI()); -+ -+ MultiSourceRootProvider provider = new MultiSourceRootProvider(); -+ ClassPath compileCP = provider.findClassPath(test, ClassPath.COMPILE); -+ AtomicInteger changeCount = new AtomicInteger(); -+ -+ compileCP.addPropertyChangeListener(evt -> { -+ if (ClassPath.PROP_ENTRIES.equals(evt.getPropertyName())) { -+ changeCount.incrementAndGet(); -+ } -+ }); -+ assertEquals(FileUtil.toFile(libJar).getAbsolutePath(), compileCP.toString()); -+ -+ testResult.setWorkDirectory(other.toURI()); -+ -+ assertEquals(1, changeCount.get()); -+ -+ assertEquals(FileUtil.toFile(otherLibJar).getAbsolutePath(), compileCP.toString()); -+ -+ testResult.setOptions("--class-path libs/lib2.jar"); -+ -+ assertEquals(2, changeCount.get()); -+ -+ assertEquals(FileUtil.toFile(otherLib2Jar).getAbsolutePath(), compileCP.toString()); -+ } -+ -+ public void testExpandModularDir() throws Exception { -+ FileObject wd = FileUtil.toFileObject(getWorkDir()); -+ FileObject test = FileUtil.createData(wd, "src/pack/Test1.java"); -+ FileObject libsDir = FileUtil.createFolder(wd, "libs"); -+ FileObject lib1Jar = FileUtil.createData(libsDir, "lib1.jar"); -+ FileObject lib2Jar = FileUtil.createData(libsDir, "lib2.jar"); -+ FileObject lib3Dir = FileUtil.createFolder(libsDir, "lib3"); -+ -+ FileUtil.createData(lib3Dir, "module-info.class"); -+ -+ TestUtilities.copyStringToFile(test, "package pack;"); -+ -+ testResult.setOptions("--module-path " + FileUtil.toFile(libsDir).getAbsolutePath()); -+ testResult.setWorkDirectory(FileUtil.toFileObject(getWorkDir()).toURI()); -+ -+ MultiSourceRootProvider provider = new MultiSourceRootProvider(); -+ ClassPath moduleCP = provider.findClassPath(test, JavaClassPathConstants.MODULE_COMPILE_PATH); -+ ClassPath compileCP = provider.findClassPath(test, ClassPath.COMPILE); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir)), -+ new HashSet<>(Arrays.asList(moduleCP.getRoots()))); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir)), -+ new HashSet<>(Arrays.asList(compileCP.getRoots()))); -+ -+ FileObject lib4Jar = FileUtil.createData(libsDir, "lib4.jar"); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir, -+ FileUtil.getArchiveRoot(lib4Jar))), -+ new HashSet<>(Arrays.asList(moduleCP.getRoots()))); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir, -+ FileUtil.getArchiveRoot(lib4Jar))), -+ new HashSet<>(Arrays.asList(compileCP.getRoots()))); -+ -+ testResult.setOptions("--module-path " + FileUtil.toFile(lib1Jar).getAbsolutePath()); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar))), -+ new HashSet<>(Arrays.asList(moduleCP.getRoots()))); -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar))), -+ new HashSet<>(Arrays.asList(compileCP.getRoots()))); -+ -+ testResult.setOptions("--module-path " + FileUtil.toFile(lib3Dir).getAbsolutePath()); -+ -+ assertEquals(new HashSet<>(Arrays.asList(lib3Dir)), -+ new HashSet<>(Arrays.asList(moduleCP.getRoots()))); -+ assertEquals(new HashSet<>(Arrays.asList(lib3Dir)), -+ new HashSet<>(Arrays.asList(compileCP.getRoots()))); -+ -+ testResult.setOptions("--module-path " + FileUtil.toFile(libsDir).getAbsolutePath()); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir, -+ FileUtil.getArchiveRoot(lib4Jar))), -+ new HashSet<>(Arrays.asList(moduleCP.getRoots()))); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir, -+ FileUtil.getArchiveRoot(lib4Jar))), -+ new HashSet<>(Arrays.asList(compileCP.getRoots()))); -+ -+ FileObject lib5Dir = FileUtil.createFolder(libsDir, "lib5Dir"); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir, -+ FileUtil.getArchiveRoot(lib4Jar), -+ lib5Dir)), -+ new HashSet<>(Arrays.asList(moduleCP.getRoots()))); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir, -+ FileUtil.getArchiveRoot(lib4Jar), -+ lib5Dir)), -+ new HashSet<>(Arrays.asList(compileCP.getRoots()))); -+ -+ lib5Dir.delete(); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir, -+ FileUtil.getArchiveRoot(lib4Jar))), -+ new HashSet<>(Arrays.asList(moduleCP.getRoots()))); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir, -+ FileUtil.getArchiveRoot(lib4Jar))), -+ new HashSet<>(Arrays.asList(compileCP.getRoots()))); -+ -+ lib4Jar.delete(); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir)), -+ new HashSet<>(Arrays.asList(moduleCP.getRoots()))); -+ -+ assertEquals(new HashSet<>(Arrays.asList(FileUtil.getArchiveRoot(lib1Jar), -+ FileUtil.getArchiveRoot(lib2Jar), -+ lib3Dir)), -+ new HashSet<>(Arrays.asList(compileCP.getRoots()))); -+ -+ FileUtil.createData(libsDir, "module-info.class"); -+ -+ assertEquals(new HashSet<>(Arrays.asList(libsDir)), -+ new HashSet<>(Arrays.asList(moduleCP.getRoots()))); -+ assertEquals(new HashSet<>(Arrays.asList(libsDir)), -+ new HashSet<>(Arrays.asList(compileCP.getRoots()))); -+ } -+ -+ public void testBrokenOptions() throws Exception { -+ FileObject wd = FileUtil.toFileObject(getWorkDir()); -+ FileObject test = FileUtil.createData(wd, "src/pack/Test1.java"); -+ -+ testResult.setOptions("--module-path"); -+ testResult.setWorkDirectory(FileUtil.toFileObject(getWorkDir()).toURI()); -+ -+ MultiSourceRootProvider provider = new MultiSourceRootProvider(); -+ -+ provider.findClassPath(test, JavaClassPathConstants.MODULE_COMPILE_PATH); -+ } -+ -+ @Override -+ protected void setUp() throws Exception { -+ super.setUp(); -+ clearWorkDir(); -+ } -+ -+ @Override -+ protected void runTest() throws Throwable { -+ SingleFileOptionsQueryImplementation queryImpl = file -> testResult; -+ ProxyLookup newQueryLookup = new ProxyLookup(Lookups.fixed(queryImpl), -+ Lookups.exclude(Lookup.getDefault(), -+ SingleFileOptionsQueryImplementation.class)); -+ Lookups.executeWith(newQueryLookup, () -> { -+ try { -+ super.runTest(); -+ } catch (Error err) { -+ throw err; -+ } catch (RuntimeException ex) { -+ throw ex; -+ } catch (Throwable ex) { -+ throw new IllegalStateException(ex); -+ } -+ }); -+ } -+ -+ 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<>(); -+ -+ public TestResultImpl() { -+ } -+ -+ @Override -+ public String getOptions() { -+ return options.get(); -+ } -+ -+ public void setOptions(String options) { -+ this.options.set(options); -+ cs.fireChange(); -+ } -+ -+ @Override -+ public URI getWorkDirectory() { -+ return workdir.get(); -+ } -+ -+ public void setWorkDirectory(URI workdir) { -+ this.workdir.set(workdir); -+ cs.fireChange(); -+ } -+ -+ @Override -+ public void addChangeListener(ChangeListener l) { -+ cs.addChangeListener(l); -+ } -+ -+ @Override -+ public void removeChangeListener(ChangeListener l) { -+ cs.removeChangeListener(l); -+ } -+ } -+ -+ static { -+ MultiSourceRootProvider.SYNCHRONOUS_UPDATES = true; -+ } - } -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java -index ea7db731562e..06411f66497d 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java -@@ -1108,6 +1108,7 @@ protected LanguageClient client() { - sessionServices.add(new WorkspaceUIContext(client)); - sessionServices.add(treeService.getNodeRegistry()); - sessionServices.add(inputService.getRegistry()); -+ sessionServices.add(workspaceService.getWorkspace()); - ((LanguageClientAware) getTextDocumentService()).connect(client); - ((LanguageClientAware) getWorkspaceService()).connect(client); - ((LanguageClientAware) treeService).connect(client); -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Workspace.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Workspace.java -new file mode 100644 -index 000000000000..0ed7ae25386a ---- /dev/null -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Workspace.java -@@ -0,0 +1,29 @@ -+/* -+ * 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.lsp.server.protocol; -+ -+import java.util.List; -+import org.openide.filesystems.FileObject; -+ -+/** -+ * One workspace opened in the UI. -+ */ -+public interface Workspace { -+ public List getClientWorkspaceFolders(); -+} -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java -index 7060c264a420..9cb5bde33036 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java -@@ -169,6 +169,12 @@ public final class WorkspaceServiceImpl implements WorkspaceService, LanguageCli - - private final Gson gson = new Gson(); - private final LspServerState server; -+ private final Workspace workspace = new Workspace() { -+ @Override -+ public List getClientWorkspaceFolders() { -+ return WorkspaceServiceImpl.this.getClientWorkspaceFolders(); -+ } -+ }; - private NbCodeLanguageClient client; - - /** -@@ -1342,15 +1348,18 @@ public void didChangeConfiguration(DidChangeConfigurationParams params) { - String altConfigPrefix = fullAltConfigPrefix.substring(0, fullAltConfigPrefix.length() - 1); - boolean modified = false; - String newVMOptions = ""; -+ String newWorkingDirectory = null; - JsonObject javaPlus = ((JsonObject) params.getSettings()).getAsJsonObject(altConfigPrefix); - if (javaPlus != null) { - JsonObject runConfig = javaPlus.getAsJsonObject("runConfig"); - if (runConfig != null) { - newVMOptions = runConfig.getAsJsonPrimitive("vmOptions").getAsString(); -+ JsonPrimitive cwd = runConfig.getAsJsonPrimitive("cwd"); -+ newWorkingDirectory = cwd != null ? cwd.getAsString() : null; - } - } - for (SingleFileOptionsQueryImpl query : Lookup.getDefault().lookupAll(SingleFileOptionsQueryImpl.class)) { -- modified |= query.setConfiguration(client, newVMOptions); -+ modified |= query.setConfiguration(workspace, newVMOptions, newWorkingDirectory); - } - if (modified) { - ((TextDocumentServiceImpl)server.getTextDocumentService()).reRunDiagnostics(); -@@ -1484,6 +1493,10 @@ public void connect(LanguageClient client) { - this.client = (NbCodeLanguageClient)client; - } - -+ public Workspace getWorkspace() { -+ return workspace; -+ } -+ - private static final class CommandProgress extends ActionProgress { - - private final CompletableFuture commandFinished = new CompletableFuture<>(); -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 c2e70cc1027c..da4898786f11 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 -@@ -18,59 +18,115 @@ - */ - package org.netbeans.modules.java.lsp.server.singlesourcefile; - --import java.util.ArrayList; --import java.util.List; -+import java.io.File; -+import java.net.URI; -+import java.util.HashMap; - import java.util.Map; - import java.util.Objects; -+import java.util.Set; - import java.util.WeakHashMap; -+import javax.swing.event.ChangeEvent; - import javax.swing.event.ChangeListener; - import org.netbeans.api.project.FileOwnerQuery; - import org.netbeans.api.project.Project; - import org.netbeans.modules.java.file.launcher.api.SourceLauncher; - import org.netbeans.modules.java.file.launcher.spi.SingleFileOptionsQueryImplementation; --import org.netbeans.modules.java.lsp.server.protocol.NbCodeLanguageClient; -+import org.netbeans.modules.java.lsp.server.protocol.Workspace; -+import org.openide.filesystems.FileChangeAdapter; -+import org.openide.filesystems.FileEvent; - import org.openide.filesystems.FileObject; -+import org.openide.filesystems.FileUtil; - import org.openide.util.ChangeSupport; - import org.openide.util.Lookup; -+import org.openide.util.Parameters; - - public abstract class SingleFileOptionsQueryImpl implements SingleFileOptionsQueryImplementation { - -- private final Map client2Options = new WeakHashMap<>(); -- private final GlobalResultImpl globalOptions = new GlobalResultImpl(); -+ private final Map workspace2Settings = new WeakHashMap<>(); -+ private final Map> workspace2Folder2Options = new WeakHashMap<>(); - - @Override - public Result optionsFor(FileObject file) { - if (isSingleSourceFile(file)) { -- NbCodeLanguageClient client = Lookup.getDefault().lookup(NbCodeLanguageClient.class); -+ Workspace workspace = Lookup.getDefault().lookup(Workspace.class); -+ FileObject workspaceFolder = workspace != null ? findWorkspaceFolder(workspace, file) : null; - -- if (client != null) { -- return getResult(client); -+ if (workspaceFolder != null) { -+ return getResult(workspace, workspaceFolder); - } else { -- return globalOptions; -+ Set workspaces; -+ -+ synchronized (this) { -+ workspaces = workspace2Settings.keySet(); -+ } -+ -+ for (Workspace w : workspaces) { -+ FileObject folder = findWorkspaceFolder(w, file); -+ if (folder != null) { -+ return getResult(w, folder); -+ } -+ } -+ -+ return null; -+ } -+ } -+ return null; -+ } -+ -+ private synchronized Result getResult(Workspace workspace, FileObject workspaceFolder) { -+ Map folder2Result = -+ workspace2Folder2Options.computeIfAbsent(workspace, w -> new HashMap<>()); -+ return folder2Result.computeIfAbsent(workspaceFolder, f -> new ResultImpl(folder2Result, -+ workspaceFolder, -+ getWorkspaceSettings(workspace))); -+ } -+ -+ static FileObject findWorkspaceFolder(Workspace workspace, FileObject file) { -+ for (FileObject workspaceFolder : workspace.getClientWorkspaceFolders()) { -+ if (FileUtil.isParentOf(workspaceFolder, file) || workspaceFolder == file) { -+ return workspaceFolder; - } - } -+ -+ //in case file is a source root, and the workspace folder is nested inside the root: -+ for (FileObject workspaceFolder : workspace.getClientWorkspaceFolders()) { -+ if (FileUtil.isParentOf(file, workspaceFolder)) { -+ return workspaceFolder; -+ } -+ } -+ - return null; - } - -- private static final class ResultImpl implements Result { -+ private static final class ResultImpl extends FileChangeAdapter implements Result, ChangeListener { - - private final ChangeSupport cs = new ChangeSupport(this); -- private String options = ""; -+ private final Map workspaceFolders2Results; -+ private final FileObject workspaceFolder; -+ private final WorkspaceSettings workspaceSettings; -+ -+ public ResultImpl(Map workspaceFolders2Results, -+ FileObject workspaceFolder, -+ WorkspaceSettings workspaceSettings) { -+ this.workspaceFolders2Results = workspaceFolders2Results; -+ this.workspaceFolder = workspaceFolder; -+ this.workspaceSettings = workspaceSettings; -+ -+ workspaceSettings.addChangeListener(this); -+ workspaceFolder.addFileChangeListener(this); -+ } - - @Override -- public synchronized String getOptions() { -- return options; -+ public String getOptions() { -+ return workspaceSettings.getOptions(); - } - -- public boolean setOptions(String options) { -- synchronized (this) { -- if (Objects.equals(this.options, options)) { -- return false; -- } -- this.options = options; -- } -- cs.fireChange(); -- return true; -+ @Override -+ public URI getWorkDirectory() { -+ String cwd = workspaceSettings.getWorkDirectory(); -+ FileObject workDir = cwd != null ? FileUtil.toFileObject(new File(cwd)) -+ : workspaceFolder; -+ return workDir.toURI(); - } - - @Override -@@ -83,48 +139,69 @@ public void removeChangeListener(ChangeListener l) { - cs.removeChangeListener(l); - } - -+ @Override -+ public void stateChanged(ChangeEvent ce) { -+ cs.fireChange(); -+ } -+ -+ @Override -+ public void fileDeleted(FileEvent fe) { -+ workspaceFolders2Results.remove(workspaceFolder); -+ } -+ - } - -- private final class GlobalResultImpl implements Result { -+ private final class WorkspaceSettings { - - private final ChangeSupport cs = new ChangeSupport(this); - -- @Override -- public String getOptions() { -- List options = new ArrayList<>(); -+ private String options; -+ private String workdirDirectory; - -- synchronized (SingleFileOptionsQueryImpl.this) { -- for (ResultImpl r : client2Options.values()) { -- options.add(r.getOptions()); -+ public synchronized String getOptions() { -+ return options; -+ } -+ -+ public synchronized String getWorkDirectory() { -+ return workdirDirectory; -+ } -+ -+ public boolean setOptions(String options, String workingDirectory) { -+ boolean modified = false; -+ synchronized (this) { -+ if (!Objects.equals(this.options, options)) { -+ this.options = options; -+ modified = true; -+ } -+ if (!Objects.equals(this.workdirDirectory, workingDirectory)) { -+ this.workdirDirectory = workingDirectory; -+ modified = true; - } - } -- -- return SourceLauncher.joinCommandLines(options); -+ if (modified) { -+ cs.fireChange(); -+ } -+ return modified; - } - -- @Override - public void addChangeListener(ChangeListener l) { - cs.addChangeListener(l); - } - -- @Override - public void removeChangeListener(ChangeListener l) { - cs.removeChangeListener(l); - } - - } - -- public boolean setConfiguration(NbCodeLanguageClient client, String vmOptions) { -- if (getResult(client).setOptions(vmOptions)) { -- globalOptions.cs.fireChange(); -- return true; -- } -- return false; -+ public boolean setConfiguration(Workspace workspace, String vmOptions, String workDirectory) { -+ return getWorkspaceSettings(workspace).setOptions(vmOptions, workDirectory); - } - -- private synchronized ResultImpl getResult(NbCodeLanguageClient client) { -- return client2Options.computeIfAbsent(client, cl -> { -- return new ResultImpl(); -+ private synchronized WorkspaceSettings getWorkspaceSettings(Workspace workspace) { -+ Parameters.notNull("workspace", workspace); -+ return workspace2Settings.computeIfAbsent(workspace, w -> { -+ return new WorkspaceSettings(); - }); - } - -diff --git a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/singlesourcefile/SingleFileOptionsQueryImplTest.java b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/singlesourcefile/SingleFileOptionsQueryImplTest.java -new file mode 100644 -index 000000000000..4c6d3c812f3a ---- /dev/null -+++ b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/singlesourcefile/SingleFileOptionsQueryImplTest.java -@@ -0,0 +1,169 @@ -+/* -+ * 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.lsp.server.singlesourcefile; -+ -+import java.util.Arrays; -+import java.util.List; -+import java.util.concurrent.atomic.AtomicInteger; -+import org.netbeans.junit.NbTestCase; -+import org.netbeans.modules.java.lsp.server.protocol.Workspace; -+import org.openide.filesystems.FileObject; -+import org.openide.filesystems.FileUtil; -+import org.openide.util.Lookup; -+import org.openide.util.lookup.Lookups; -+import org.openide.util.lookup.ProxyLookup; -+ -+public class SingleFileOptionsQueryImplTest extends NbTestCase { -+ -+ public SingleFileOptionsQueryImplTest(String name) { -+ super(name); -+ } -+ -+ @Override -+ protected void setUp() throws Exception { -+ super.setUp(); -+ -+ clearWorkDir(); -+ } -+ -+ public void testFindWorkspaceFolder() throws Exception { -+ FileObject wd = FileUtil.toFileObject(getWorkDir()); -+ FileObject workspace1 = FileUtil.createFolder(wd, "workspace1"); -+ FileObject source1 = FileUtil.createData(workspace1, "test/Test.java"); -+ FileObject prjRoot = FileUtil.createFolder(wd, "prjRoot"); -+ FileObject workspace2 = FileUtil.createFolder(prjRoot, "test2"); -+ FileObject source2 = FileUtil.createData(workspace2, "Test.java"); -+ -+ Workspace workspace = new WorkspaceImpl(Arrays.asList(workspace1, workspace2)); -+ -+ assertEquals(workspace1, SingleFileOptionsQueryImpl.findWorkspaceFolder(workspace, source1)); -+ assertEquals(workspace1, SingleFileOptionsQueryImpl.findWorkspaceFolder(workspace, source1.getParent())); -+ assertEquals(workspace1, SingleFileOptionsQueryImpl.findWorkspaceFolder(workspace, workspace1)); -+ assertEquals(workspace2, SingleFileOptionsQueryImpl.findWorkspaceFolder(workspace, source2)); -+ assertEquals(workspace2, SingleFileOptionsQueryImpl.findWorkspaceFolder(workspace, source2.getParent())); -+ assertEquals(workspace2, SingleFileOptionsQueryImpl.findWorkspaceFolder(workspace, source2.getParent().getParent())); -+ } -+ -+ public void testWorkspaceOptions() throws Exception { -+ FileObject wd = FileUtil.toFileObject(getWorkDir()); -+ FileObject workspace1 = FileUtil.createFolder(wd, "workspace1"); -+ FileObject source1 = FileUtil.createData(workspace1, "test1/Test.java"); -+ FileObject workspace2 = FileUtil.createFolder(wd, "workspace2"); -+ FileObject source2 = FileUtil.createData(workspace2, "test2/Test.java"); -+ FileObject source3 = FileUtil.createData(wd, "test3/Test.java"); -+ -+ SingleFileOptionsQueryImpl query = new SingleFileOptionsQueryImpl() {}; -+ Workspace workspace = new WorkspaceImpl(Arrays.asList(workspace1, workspace2)); -+ -+ query.setConfiguration(workspace, "-Dtest=test", null); -+ -+ Lookups.executeWith(new ProxyLookup(Lookups.fixed(workspace), Lookup.getDefault()), () -> { -+ assertEquals("-Dtest=test", query.optionsFor(source1).getOptions()); -+ assertEquals(workspace1.toURI(), query.optionsFor(source1).getWorkDirectory()); -+ assertEquals("-Dtest=test", query.optionsFor(source1.getParent()).getOptions()); -+ assertEquals(workspace1.toURI(), query.optionsFor(source1.getParent()).getWorkDirectory()); -+ -+ assertEquals("-Dtest=test", query.optionsFor(source2).getOptions()); -+ assertEquals(workspace2.toURI(), query.optionsFor(source2).getWorkDirectory()); -+ assertEquals("-Dtest=test", query.optionsFor(source2.getParent()).getOptions()); -+ assertEquals(workspace2.toURI(), query.optionsFor(source2.getParent()).getWorkDirectory()); -+ -+ AtomicInteger changeCount = new AtomicInteger(); -+ -+ query.optionsFor(source1).addChangeListener(evt -> changeCount.incrementAndGet()); -+ -+ query.setConfiguration(workspace, "-Dtest=test", null); -+ -+ assertEquals(0, changeCount.get()); -+ -+ FileObject newWD = source1.getParent(); -+ -+ query.setConfiguration(workspace, "-Dtest=test", FileUtil.toFile(newWD).getAbsolutePath()); -+ -+ assertEquals(1, changeCount.get()); -+ -+ assertEquals("-Dtest=test", query.optionsFor(source1).getOptions()); -+ assertEquals(newWD.toURI(), query.optionsFor(source1).getWorkDirectory()); -+ assertEquals("-Dtest=test", query.optionsFor(source1.getParent()).getOptions()); -+ assertEquals(newWD.toURI(), query.optionsFor(source1.getParent()).getWorkDirectory()); -+ -+ assertEquals("-Dtest=test", query.optionsFor(source2).getOptions()); -+ assertEquals(newWD.toURI(), query.optionsFor(source2).getWorkDirectory()); -+ assertEquals("-Dtest=test", query.optionsFor(source2.getParent()).getOptions()); -+ assertEquals(newWD.toURI(), query.optionsFor(source2.getParent()).getWorkDirectory()); -+ -+ query.setConfiguration(workspace, "-Dtest=test2", FileUtil.toFile(newWD).getAbsolutePath()); -+ -+ assertEquals(2, changeCount.get()); -+ -+ assertEquals("-Dtest=test2", query.optionsFor(source1).getOptions()); -+ assertEquals(newWD.toURI(), query.optionsFor(source1).getWorkDirectory()); -+ assertEquals("-Dtest=test2", query.optionsFor(source1.getParent()).getOptions()); -+ assertEquals(newWD.toURI(), query.optionsFor(source1.getParent()).getWorkDirectory()); -+ -+ assertEquals("-Dtest=test2", query.optionsFor(source2).getOptions()); -+ assertEquals(newWD.toURI(), query.optionsFor(source2).getWorkDirectory()); -+ assertEquals("-Dtest=test2", query.optionsFor(source2.getParent()).getOptions()); -+ assertEquals(newWD.toURI(), query.optionsFor(source2.getParent()).getWorkDirectory()); -+ -+ query.setConfiguration(workspace, "-Dtest=test2", null); -+ -+ assertEquals(3, changeCount.get()); -+ -+ assertEquals("-Dtest=test2", query.optionsFor(source1).getOptions()); -+ assertEquals(workspace1.toURI(), query.optionsFor(source1).getWorkDirectory()); -+ assertEquals("-Dtest=test2", query.optionsFor(source1.getParent()).getOptions()); -+ assertEquals(workspace1.toURI(), query.optionsFor(source1.getParent()).getWorkDirectory()); -+ -+ assertEquals("-Dtest=test2", query.optionsFor(source2).getOptions()); -+ assertEquals(workspace2.toURI(), query.optionsFor(source2).getWorkDirectory()); -+ assertEquals("-Dtest=test2", query.optionsFor(source2.getParent()).getOptions()); -+ assertEquals(workspace2.toURI(), query.optionsFor(source2.getParent()).getWorkDirectory()); -+ }); -+ -+ -+ //with no workspace context: -+ assertEquals("-Dtest=test2", query.optionsFor(source1).getOptions()); -+ assertEquals(workspace1.toURI(), query.optionsFor(source1).getWorkDirectory()); -+ assertEquals("-Dtest=test2", query.optionsFor(source1.getParent()).getOptions()); -+ assertEquals(workspace1.toURI(), query.optionsFor(source1.getParent()).getWorkDirectory()); -+ -+ assertEquals("-Dtest=test2", query.optionsFor(source2).getOptions()); -+ assertEquals(workspace2.toURI(), query.optionsFor(source2).getWorkDirectory()); -+ assertEquals("-Dtest=test2", query.optionsFor(source2.getParent()).getOptions()); -+ assertEquals(workspace2.toURI(), query.optionsFor(source2.getParent()).getWorkDirectory()); -+ -+ assertNull(query.optionsFor(source3)); -+ assertNull(query.optionsFor(source3.getParent())); -+ } -+ -+ private static final class WorkspaceImpl implements Workspace { -+ private final List workspaceFolders; -+ -+ public WorkspaceImpl(List workspaceFolders) { -+ this.workspaceFolders = workspaceFolders; -+ } -+ -+ @Override -+ public List getClientWorkspaceFolders() { -+ return workspaceFolders; -+ } -+ -+ } -+} -diff --git a/java/java.lsp.server/vscode/src/extension.ts b/java/java.lsp.server/vscode/src/extension.ts -index a4fdf4b1e59d..426d2e457e38 100644 ---- a/java/java.lsp.server/vscode/src/extension.ts -+++ b/java/java.lsp.server/vscode/src/extension.ts -@@ -1071,7 +1071,8 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex - 'netbeans.hints', - 'netbeans.format', - 'netbeans.java.imports', -- 'java+.runConfig.vmOptions' -+ 'java+.runConfig.vmOptions', -+ 'java+.runConfig.cwd' - ], - fileEvents: [ - workspace.createFileSystemWatcher('**/*.java') -diff --git a/java/java.source.base/src/org/netbeans/modules/java/source/indexing/APTUtils.java b/java/java.source.base/src/org/netbeans/modules/java/source/indexing/APTUtils.java -index 2aa6f9657cb3..54ec330cbe36 100644 ---- a/java/java.source.base/src/org/netbeans/modules/java/source/indexing/APTUtils.java -+++ b/java/java.source.base/src/org/netbeans/modules/java/source/indexing/APTUtils.java -@@ -135,9 +135,6 @@ private APTUtils(@NonNull final FileObject root) { - this.root = root; - bootPath = ClassPath.getClassPath(root, ClassPath.BOOT); - compilePath = ClassPath.getClassPath(root, ClassPath.COMPILE); -- if (compilePath != null) { -- compilePath.addPropertyChangeListener(this); -- } - processorPath = new AtomicReference<>(ClassPath.getClassPath(root, JavaClassPathConstants.PROCESSOR_PATH)); - processorModulePath = new AtomicReference<>(ClassPath.getClassPath(root, JavaClassPathConstants.MODULE_PROCESSOR_PATH)); - aptOptions = AnnotationProcessingQuery.getAnnotationProcessingOptions(root); -@@ -150,6 +147,9 @@ private APTUtils(@NonNull final FileObject root) { - false); - }); - usedRoots = new UsedRoots(root.toURL()); -+ if (compilePath != null) { -+ compilePath.addPropertyChangeListener(this); -+ } - } - - @CheckForNull diff --git a/patches/7390.diff b/patches/7390.diff deleted file mode 100644 index c45e6dfe..00000000 --- a/patches/7390.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/java/java.project.ui/src/org/netbeans/modules/java/project/ui/resources/Exception.java.template b/java/java.project.ui/src/org/netbeans/modules/java/project/ui/resources/Exception.java.template -index ab05caa21d2e..bdccca88f8e3 100644 ---- a/java/java.project.ui/src/org/netbeans/modules/java/project/ui/resources/Exception.java.template -+++ b/java/java.project.ui/src/org/netbeans/modules/java/project/ui/resources/Exception.java.template -@@ -33,7 +33,7 @@ import ${interface}; - - <#assign implementation = "${implementation}"?remove_ending(", ")> - --public class ${name}<#if extension?? && extension != ""> extends ${extension}<#if implementation?? && implementation != ""> implements ${implementation} { -+public class ${name}<#if extension?? && extension != ""> extends ${extension}<#else> extends Exception<#if implementation?? && implementation != ""> implements ${implementation} { - - /** - * Creates a new instance of ${name} without detail message. diff --git a/patches/7491-preliminary.diff b/patches/7491.diff similarity index 76% rename from patches/7491-preliminary.diff rename to patches/7491.diff index ca336235..a0a90593 100644 --- a/patches/7491-preliminary.diff +++ b/patches/7491.diff @@ -1,8 +1,8 @@ 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..95ae5235c6 100644 +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 @@ abstract class BaseTask extends UserTask { +@@ -176,6 +176,7 @@ TokenSequence nextNonWhitespaceToken(TokenSequence ts) case LINE_COMMENT: case BLOCK_COMMENT: case JAVADOC_COMMENT: @@ -10,7 +10,7 @@ index 78fdd4caa3..95ae5235c6 100644 break; default: return ts; -@@ -206,6 +207,7 @@ abstract class BaseTask extends UserTask { +@@ -206,6 +207,7 @@ TokenSequence previousNonWhitespaceToken(TokenSequence case LINE_COMMENT: case BLOCK_COMMENT: case JAVADOC_COMMENT: @@ -18,7 +18,7 @@ index 78fdd4caa3..95ae5235c6 100644 break; default: return ts; -@@ -427,6 +429,7 @@ abstract class BaseTask extends UserTask { +@@ -427,6 +429,7 @@ private Env getEnvImpl(CompilationController controller, TreePath orig, TreePath case LINE_COMMENT: case BLOCK_COMMENT: case JAVADOC_COMMENT: @@ -26,7 +26,7 @@ index 78fdd4caa3..95ae5235c6 100644 break; case ARROW: scope = controller.getTrees().getScope(blockPath); -@@ -456,6 +459,7 @@ abstract class BaseTask extends UserTask { +@@ -456,6 +459,7 @@ private Env getEnvImpl(CompilationController controller, TreePath orig, TreePath case LINE_COMMENT: case BLOCK_COMMENT: case JAVADOC_COMMENT: @@ -35,10 +35,10 @@ index 78fdd4caa3..95ae5235c6 100644 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 9a2d8de483..2cf8691813 100644 +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 -@@ -1611,6 +1611,7 @@ public final class JavaCompletionTask extends BaseTask { +@@ -1608,6 +1608,7 @@ private void insideMemberSelect(Env env) throws IOException { case LINE_COMMENT: case BLOCK_COMMENT: case JAVADOC_COMMENT: @@ -46,11 +46,24 @@ index 9a2d8de483..2cf8691813 100644 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 a0682653d7..c6f54e77b8 100644 +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 @@ import org.netbeans.api.lexer.TokenSequence; +@@ -51,7 +51,7 @@ */ public final class JavadocCompletionUtils { @@ -68,7 +81,7 @@ index a0682653d7..c6f54e77b8 100644 private static final Logger LOGGER = Logger.getLogger(JavadocCompletionUtils.class.getName()); /** -@@ -196,6 +196,7 @@ public final class JavadocCompletionUtils { +@@ -196,6 +196,7 @@ public static TokenSequence findJavadocTokenSequence(Compilation break; } case JAVADOC_COMMENT: @@ -76,7 +89,7 @@ index a0682653d7..c6f54e77b8 100644 if (token.partType() == PartType.COMPLETE) { return javac.getElements().getDocComment(e) == null ? null : s.embedded(JavadocTokenId.language()); -@@ -246,36 +247,39 @@ public final class JavadocCompletionUtils { +@@ -246,36 +247,39 @@ static boolean isInsideIndent(Token token, int offset) { /** * Is javadoc line break? @@ -114,7 +127,7 @@ index a0682653d7..c6f54e77b8 100644 && JAVADOC_LINE_BREAK.matcher(text).find() - && (pos == token.length() || !isInsideIndent(token, pos)); + && (pos == token.length() || !isInsideIndent(token, pos)) -+ ) || ts.index() == 0; ++ ); return result; } catch (IndexOutOfBoundsException e) { throw (IndexOutOfBoundsException) new IndexOutOfBoundsException("pos: " + pos + ", token.length: " + token.length() + ", token text: " + token.text()).initCause(e); @@ -125,7 +138,7 @@ index a0682653d7..c6f54e77b8 100644 public static boolean isWhiteSpace(CharSequence text) { return text != null && text.length() > 0 && !JAVADOC_WHITE_SPACE.matcher(text).find(); } -@@ -437,7 +441,8 @@ public final class JavadocCompletionUtils { +@@ -437,7 +441,8 @@ private static boolean movedToJavadocToken(TokenSequence ts, int of return false; } @@ -135,7 +148,7 @@ index a0682653d7..c6f54e77b8 100644 return false; } -@@ -456,6 +461,11 @@ public final class JavadocCompletionUtils { +@@ -456,6 +461,11 @@ private static boolean isEmptyJavadoc(Token token, int offset) { // check special case /**|*/ return offset == 3 && "/***/".contentEquals(text); //NOI18N } @@ -148,10 +161,10 @@ index a0682653d7..c6f54e77b8 100644 } 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 07ccd639c0..2d91d7065f 100644 +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 final class JavadocImports { +@@ -641,7 +641,7 @@ public static boolean isInsideReference(TokenSequence jdts, int } case OTHER_TEXT: isBeforeWS |= JavadocCompletionUtils.isWhiteSpace(jdt); @@ -160,7 +173,7 @@ index 07ccd639c0..2d91d7065f 100644 if (isBeforeWS) { continue; } else { -@@ -690,7 +690,9 @@ public final class JavadocImports { +@@ -690,7 +690,9 @@ private static TokenSequence getJavadocTS(CompilationInfo javac, TokenSequence javadoc = null; TokenSequence ts = SourceUtils.getJavaTokenSequence(javac.getTokenHierarchy(), start); @@ -171,7 +184,7 @@ index 07ccd639c0..2d91d7065f 100644 javadoc = ts.embedded(JavadocTokenId.language()); } -@@ -893,14 +895,14 @@ public final class JavadocImports { +@@ -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) @@ -189,10 +202,10 @@ index 07ccd639c0..2d91d7065f 100644 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 add1ca7dc2..33926dc0b4 100644 +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 class JavadocCompletionUtilsTest extends JavadocTestSupport { +@@ -270,32 +270,32 @@ public void testIsLineBreak() throws Exception { TokenSequence jdts = JavadocCompletionUtils.findJavadocTokenSequence(info, offset); assertTrue(jdts.moveNext()); assertTrue(insertPointer(code, offset), @@ -231,7 +244,7 @@ index add1ca7dc2..33926dc0b4 100644 } public void testIsLineBreak2() throws Exception { -@@ -319,10 +319,10 @@ public class JavadocCompletionUtilsTest extends JavadocTestSupport { +@@ -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()), @@ -245,43 +258,45 @@ index add1ca7dc2..33926dc0b4 100644 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 e64ec4698b..88443c53d7 100644 +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,122 @@ public class JavadocImportsTest extends JavadocTestSupport { +@@ -207,6 +207,124 @@ public void testComputeReferencedElements() throws Exception { assertEquals(exp, sortedResult); } + public void testComputeReferencedElementsMarkdown() throws Exception { + String code = -+ "package p;\n" + -+ "import java.io.IOException;\n" + -+ "import java.util.Collections;\n" + -+ "import java.util.List;\n" + -+ "class C {\n" + -+ " ///link1 {@link Runnable}\n" + -+ " ///link3 {@linkplain Collections#binarySearch(java.util.List, Object) search}\n" + -+ " ///{@link java. uncomplete reference}\n" + -+// " ///unclosed link {@value Math#PI\n" + //TODO: does not work -+ " ///unclosed link {@value Math#PI}\n" + -+ " ///@see List\n" + -+ " ///@throws IOException\n" + -+ " void m() throws java.io.IOException {\n" + -+ " }\n" + -+ " ///\n" + -+ " ///{@link Collections}\n" + -+ " ///\n" + -+ " int field;\n" + -+ " /// {@link IOException\n" + -+ " interface InnerInterface {}\n" + -+ " /// {@link Collections}\n" + -+ " @interface InnerAnnotationType {}\n" + -+ "}\n" + -+ "/// {@link Collections}\n" + -+ "enum TopLevelEnum {\n" + -+ " /** {@link Collections} */" + -+ " E1\n" + -+ "}\n"; ++ """ ++ 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() @@ -371,28 +386,30 @@ index e64ec4698b..88443c53d7 100644 public void testComputeTokensOfReferencedElements() throws Exception { String code = "package p;\n" + -@@ -286,6 +402,85 @@ public class JavadocImportsTest extends JavadocTestSupport { +@@ -286,6 +404,87 @@ public void testComputeTokensOfReferencedElements() throws Exception { // assertEquals(toFind.toString(), exp, tokens); } + public void testComputeTokensOfReferencedElementsMarkdown() throws Exception { + String code = -+ "package p;\n" + -+ "import java.util.Collections;\n" + -+ "class C {\n" + -+ " ///link1 {@link Runnable}\n" + -+ " ///link2 {@link Collections#binarySearch(java.util.List, java.lang.Object) search}\n" + -+ " ///{@link java. uncomplete reference}" + -+// " ///unclosed link {@value Math#PI\n" + //TODO: does not work -+ " ///unclosed link {@value Math#PI}\n" + -+ " ///@see java.util.Collections\n" + -+ " ///@throws ThrowsUnresolved\n" + -+ " ///\n" + -+ " void m() throws java.io.IOException {\n" + -+ " Collections.binarySearch(Collections.emptyList(), \"\");\n" + -+ " double pi = Math.PI;\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ 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"); @@ -457,11 +474,66 @@ index e64ec4698b..88443c53d7 100644 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 8a60126a72..3e8b08e4e7 100644 +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 class GoToSupport { +@@ -408,7 +408,7 @@ public static Context resolveContext(CompilationInfo controller, Document doc, i boolean insideImportStmt = false; TreePath path = controller.getTreeUtilities().pathFor(exactOffset); @@ -470,7 +542,7 @@ index 8a60126a72..3e8b08e4e7 100644 el = JavadocImports.findReferencedElement(controller, offset); } else { path = adjustPathForModuleName(path); -@@ -662,7 +662,7 @@ public class GoToSupport { +@@ -662,7 +662,7 @@ public void run() { Token t = ts.token(); @@ -480,10 +552,10 @@ index 8a60126a72..3e8b08e4e7 100644 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 91c22e9338..6c86a21dd3 100644 +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 -@@ -1318,6 +1318,7 @@ public class JavaCompletionCollector implements CompletionCollector { +@@ -1319,6 +1319,7 @@ private static TokenSequence findLastNonWhitespaceToken(TokenSequen case LINE_COMMENT: case BLOCK_COMMENT: case JAVADOC_COMMENT: @@ -492,10 +564,10 @@ index 91c22e9338..6c86a21dd3 100644 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 733ba9ad32..47fbd2803f 100644 +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 @@ public abstract class JavaCompletionItem implements CompletionItem { +@@ -4617,6 +4617,7 @@ private static TokenSequence findLastNonWhitespaceToken(TokenSequen case LINE_COMMENT: case BLOCK_COMMENT: case JAVADOC_COMMENT: @@ -504,10 +576,10 @@ index 733ba9ad32..47fbd2803f 100644 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 5ac389c224..adefaee33d 100644 +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 class JavaKit extends NbEditorKit { +@@ -458,6 +458,7 @@ public void insert(MutableContext context) throws BadLocationException { if (isJavadocTouched) { blockCommentComplete(doc, dotPos, context); } @@ -516,10 +588,10 @@ index 5ac389c224..adefaee33d 100644 } 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 5f2172b49d..0682f52f5f 100644 +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 @@ class TypingCompletion { +@@ -563,6 +563,21 @@ private static boolean isClosedBlockComment(CharSequence txt, int pos) { return false; } @@ -542,10 +614,10 @@ index 5f2172b49d..0682f52f5f 100644 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 da31e094c5..6ccb51a51e 100644 +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 final class Utilities { +@@ -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: @@ -554,10 +626,10 @@ index da31e094c5..6ccb51a51e 100644 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 eac6a5607c..c328d4ae41 100644 +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.lang.model.util.Types; +@@ -64,6 +64,7 @@ import javax.swing.text.Document; import org.netbeans.api.annotations.common.NonNull; import org.netbeans.api.annotations.common.NullAllowed; @@ -565,7 +637,7 @@ index eac6a5607c..c328d4ae41 100644 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.ResultIterator; +@@ -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; @@ -573,7 +645,7 @@ index eac6a5607c..c328d4ae41 100644 public class JavadocCompletionTask extends UserTask { -@@ -188,16 +190,20 @@ public class JavadocCompletionTask extends UserTask { +@@ -188,16 +190,20 @@ private void analyzeContext(JavadocContext jdctx) { return; } jdts.move(this.caretOffset); @@ -600,7 +672,7 @@ index eac6a5607c..c328d4ae41 100644 case TAG: resolveTagToken(jdctx); break; -@@ -265,7 +271,9 @@ public class JavadocCompletionTask extends UserTask { +@@ -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()) { @@ -611,7 +683,7 @@ index eac6a5607c..c328d4ae41 100644 } do { Token t = jdctx.jdts.token(); -@@ -415,13 +423,13 @@ public class JavadocCompletionTask extends UserTask { +@@ -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; @@ -627,7 +699,7 @@ index eac6a5607c..c328d4ae41 100644 // not java reference return; } else if (jdts.moveNext()) { -@@ -519,7 +527,7 @@ public class JavadocCompletionTask extends UserTask { +@@ -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; @@ -636,7 +708,7 @@ index eac6a5607c..c328d4ae41 100644 // none prefix anchorOffset = caretOffset; completeParamName(tag, "", caretOffset, jdctx); // NOI18N -@@ -1226,11 +1234,10 @@ public class JavadocCompletionTask extends UserTask { +@@ -1226,11 +1234,10 @@ private boolean startsWith(String theString, String prefix) { void resolveOtherText(JavadocContext jdctx, TokenSequence jdts) { Token token = jdts.token(); @@ -652,7 +724,7 @@ index eac6a5607c..c328d4ae41 100644 if (pos > 0 && pos <= text.length() && text.charAt(pos - 1) == '{') { if (tag != null && !JavadocCompletionUtils.isBlockTag(tag)) { -@@ -1244,10 +1251,10 @@ public class JavadocCompletionTask extends UserTask { +@@ -1244,10 +1251,10 @@ void resolveOtherText(JavadocContext jdctx, TokenSequence jdts) } if (tag != null) { insideTag(tag, jdctx); @@ -665,7 +737,7 @@ index eac6a5607c..c328d4ae41 100644 resolveBlockTag(null, jdctx); } } -@@ -1336,6 +1343,7 @@ public class JavadocCompletionTask extends UserTask { +@@ -1336,6 +1343,7 @@ private static class JavadocContext { private DocCommentTree comment; private DocSourcePositions positions; private TokenSequence jdts; @@ -674,21 +746,23 @@ index eac6a5607c..c328d4ae41 100644 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 8deac6817b..5e143d4358 100644 +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,67 @@ public class GoToSupportTest extends NbTestCase { +@@ -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;\n" + -+ "/**\n" + -+ " * @see Obj|ect\n" + -+ " */\n" + -+ "public class Test {\n" + -+ "}\n"; ++ final String code = """ ++ package test; ++ /** ++ * @see Obj|ect ++ */ ++ public class Test { ++ } ++ """; + + performTest(code, new UiUtilsCaller() { + @Override public boolean open(FileObject fo, int pos) { @@ -715,10 +789,12 @@ index 8deac6817b..5e143d4358 100644 + public void testMarkdownJavadoc() throws Exception { + final boolean[] wasCalled = new boolean[1]; + this.sourceLevel = "23"; -+ final String code = "package test;\n" + -+ "///@see Obj|ect\n" + -+ "public class Test {\n" + -+ "}\n"; ++ final String code = """ ++ package test; ++ ///@see Obj|ect ++ public class Test { ++ } ++ """; + + performTest(code, new UiUtilsCaller() { + @Override public boolean open(FileObject fo, int pos) { @@ -746,36 +822,37 @@ index 8deac6817b..5e143d4358 100644 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 a0713d608f..0525fc6a68 100644 +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,21 @@ public void testPositionInTextBlock() throws Exception { +@@ -1419,6 +1419,22 @@ public void testX() throws Exception { ctx.assertDocumentTextEquals("{"); } + public void testJavadocLineRun() { + Context ctx = new Context(new JavaKit(), -+ "class Test {\n" + -+ " ///|\n" + -+ "}\n" -+ ); ++ """ ++ class Test { ++ ///| ++ } ++ """); + ctx.typeChar('\n'); -+ ctx.assertDocumentTextEquals( -+ "class Test {\n" + -+ " ///\n" + -+ " ///|\n" + -+ "}\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 8bf90d619f..884aba40be 100644 +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.ArrayList; +@@ -23,10 +23,14 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -790,7 +867,16 @@ index 8bf90d619f..884aba40be 100644 /** * -@@ -601,8 +605,212 @@ public class JavadocCompletionQueryTest extends JavadocTestSupport { +@@ -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:"); } @@ -799,242 +885,290 @@ index 8bf90d619f..884aba40be 100644 + + public void testBlockTagsCompletionInMarkdown() throws Exception { + String code = -+ "package p;\n" + -+ "class Clazz {\n" + -+ " ///\n" + -+ " /// |\n" + -+ " ///\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ 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;\n" + -+ "class Clazz {\n" + -+ " ///\n" + -+ " ///|\n" + -+ " ///\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ 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;\n" + -+ "class Clazz {\n" + -+ " ///\n" + -+ " ///| \n" + -+ " ///\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ package p; ++ class Clazz { ++ /// ++ ///|\s ++ /// ++ void method(int p1, int p2) { ++ } ++ } ++ """; + + performCompletionTest(code, "@deprecated:", "@exception:", "@hidden:", "@param:", "@return:", "@see:", "@serialData:", "@since:", "@throws:"); + } + -+ public void XXX_testBlockTagsCompletionInMarkdownStart() throws Exception { ++ public void testBlockTagsCompletionInMarkdownStart() throws Exception { + String code = -+ "package p;\n" + -+ "class Clazz {\n" + -+ " ///|\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ 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;\n" + -+ "class Clazz {\n" + -+ " ///\n" + -+ " /// @see CharSequence#le|\n" + -+ " ///\n" + -+ " Clazz() {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ package p; ++ class Clazz { ++ /// ++ /// @see CharSequence#le| ++ /// ++ Clazz() { ++ } ++ } ++ """; + + performCompletionTest(code, "public abstract int length()"); + } + + public void testSeeMarkdown2() throws Exception { + String code = -+ "package p;\n" + -+ "class Clazz {\n" + -+ " ///\n" + -+ " /// @see |\n" + -+ " ///\n" + -+ " Clazz() {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ package p; ++ class Clazz { ++ /// ++ /// @see | ++ /// ++ Clazz() { ++ } ++ } ++ """; + + performCompletionTest(code, null, "String", "Clazz"); + } + + public void testSeeMarkdown3() throws Exception { + String code = -+ "package p;\n" + -+ "class Clazz {\n" + -+ " ///@param i i\n" + -+ " ///@see |\n" + -+ " ///\n" + -+ " Clazz(int i) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ 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;\n" + -+ "class Clazz {\n" + -+ " ///\n" + -+ " /// @param |\n" + -+ " ///\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ package p; ++ class Clazz { ++ /// ++ /// @param | ++ /// ++ void method(int p1, int p2) { ++ } ++ } ++ """; + + performCompletionTest(code, "p1:", "p2:"); + } + + public void testJavadocOldStart1() throws Exception { + String code = -+ "package p;\n" + -+ "class Clazz {\n" + -+ " /**| */\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ 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;\n" + -+ "class Clazz {\n" + -+ " /**@s| */\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ 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;\n" + -+ "class Clazz {\n" + -+ " /**@param | */\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ package p; ++ class Clazz { ++ /**@param | */ ++ void method(int p1, int p2) { ++ } ++ } ++ """; + + performCompletionTest(code, "p1:", "p2:"); + } + + public void testJavadocOldStart4() throws Exception { + String code = -+ "package p;\n" + -+ "class Clazz {\n" + -+ " /**@see | */\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ 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;\n" + -+ "class Clazz {\n" + -+ " ///|\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ 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;\n" + -+ "class Clazz {\n" + -+ " ///@s|\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ 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;\n" + -+ "class Clazz {\n" + -+ " ///@param |\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ package p; ++ class Clazz { ++ ///@param | ++ void method(int p1, int p2) { ++ } ++ } ++ """; + + performCompletionTest(code, "p1:", "p2:"); + } + + public void testJavadocMarkdownStart4() throws Exception { + String code = -+ "package p;\n" + -+ "class Clazz {\n" + -+ " ///@see |\n" + -+ " void method(int p1, int p2) {\n" + -+ " }\n" + -+ "}\n"; ++ """ ++ 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 +855,27 @@ public class JavadocCompletionQueryTest extends JavadocTestSupport { +@@ -647,4 +887,30 @@ private void performCompletionTest(String code, String... golden) throws Excepti assertEquals(goldenList, resultStrings); } } + -+ @ServiceProvider(service=SourceLevelQueryImplementation2.class) -+ public static final class SourceLevelQueryImpl implements SourceLevelQueryImplementation2 { ++ @Override ++ protected Object[] additionalServices() { ++ return new Object[] { ++ new SourceLevelQueryImplementation2() { + -+ @Override -+ public Result getSourceLevel(FileObject javaFile) { -+ return new Result() { + @Override -+ public String getSourceLevel() { -+ return "23"; -+ } ++ public Result getSourceLevel(FileObject javaFile) { ++ return new Result() { ++ @Override ++ public String getSourceLevel() { ++ return "23"; ++ } + -+ @Override -+ public void addChangeListener(ChangeListener listener) { -+ } ++ @Override ++ public void addChangeListener(ChangeListener listener) { ++ } + -+ @Override -+ public void removeChangeListener(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 17602a5e40..25738a8da1 100644 +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 { @@ -1045,7 +1179,7 @@ index 17602a5e40..25738a8da1 100644 // Errors INVALID_COMMENT_END("*/", "error"), -@@ -262,6 +263,9 @@ public enum JavaTokenId implements TokenId { +@@ -262,6 +263,9 @@ protected LanguageEmbedding embedding( case JAVADOC_COMMENT: return LanguageEmbedding.create(JavadocTokenId.language(), 3, (token.partType() == PartType.COMPLETE) ? 2 : 0); @@ -1056,10 +1190,10 @@ index 17602a5e40..25738a8da1 100644 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 49c32619b4..438b8b84ff 100644 +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 class JavaLexer implements Lexer { +@@ -321,6 +321,13 @@ public Token nextToken() { case '/': switch (nextChar()) { case '/': // in single-line comment @@ -1073,7 +1207,7 @@ index 49c32619b4..438b8b84ff 100644 while (true) switch (nextChar()) { case '\r': consumeNewline(); -@@ -1428,6 +1435,34 @@ public class JavaLexer implements Lexer { +@@ -1428,6 +1435,34 @@ private Token finishFloatExponent() { return token(JavaTokenId.DOUBLE_LITERAL); } } @@ -1109,10 +1243,10 @@ index 49c32619b4..438b8b84ff 100644 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 fa7189b483..c6ab507fee 100644 +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 @@ public class JavadocLexer implements Lexer { +@@ -194,6 +194,20 @@ private Token otherText(int ch) { leftbr = false; newline = false; break; @@ -1134,21 +1268,23 @@ index fa7189b483..c6ab507fee 100644 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 6402fc5c34..6ea9909143 100644 +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,78 @@ public class JavaLexerBatchTest extends TestCase { +@@ -812,4 +812,83 @@ public void testTemplates2() { assertFalse(ts.moveNext()); } + public void testMarkdown1() { -+ String text = "///test\n" + -+ "///@see second line\n" + -+ "///third\n" + -+ "\n" + -+ "///another run\n" + -+ "///another line\n" + -+ "\n"; ++ 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(); @@ -1168,12 +1304,14 @@ index 6402fc5c34..6ea9909143 100644 + } + + public void testMarkdown2() { -+ String text = "///test\n" + -+ "///@see second line\n" + -+ "///third\n" + -+ "\n" + -+ "///another run\n" + -+ "///another line\n"; ++ 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(); @@ -1192,12 +1330,13 @@ index 6402fc5c34..6ea9909143 100644 + } + + public void testMarkdown3() { -+ String text = "///test\n" + -+ "///@see second line\n" + -+ "///third\n" + -+ "\n" + -+ "///another run\n" + -+ "///another line"; ++ 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(); @@ -1217,10 +1356,10 @@ index 6402fc5c34..6ea9909143 100644 + } 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 72668f24a6..c0859ba2f8 100644 +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 class JavadocLexerTest extends NbTestCase { +@@ -143,6 +143,20 @@ public void test233097b() { LexerTestUtilities.assertNextTokenEquals(ts, JavadocTokenId.OTHER_TEXT, "}"); } @@ -1242,7 +1381,7 @@ index 72668f24a6..c0859ba2f8 100644 // 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 fc390b64bb..001c83187e 100644 +index fc390b64bbc9..001c83187ebd 100644 --- a/java/java.lsp.server/licenseinfo.xml +++ b/java/java.lsp.server/licenseinfo.xml @@ -50,4 +50,9 @@ @@ -1257,7 +1396,7 @@ index fc390b64bb..001c83187e 100644 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 0000000000..c6b69e5336 +index 000000000000..c6b69e53360e --- /dev/null +++ b/java/java.lsp.server/vscode/language-configuration.json @@ -0,0 +1,111 @@ @@ -1373,7 +1512,7 @@ index 0000000000..c6b69e5336 + ] +} diff --git a/java/java.lsp.server/vscode/package.json b/java/java.lsp.server/vscode/package.json -index 07c3269640..a341dad480 100644 +index cad97381ef73..60795163b16a 100644 --- a/java/java.lsp.server/vscode/package.json +++ b/java/java.lsp.server/vscode/package.json @@ -35,6 +35,18 @@ @@ -1396,7 +1535,7 @@ index 07c3269640..a341dad480 100644 "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 1357aff68d..d990bf88fd 100644 +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 @@ @@ -1464,10 +1603,10 @@ index 1357aff68d..d990bf88fd 100644 ] }, 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 a7453c4477..bca4cbd46a 100644 +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 final class TreeUtilities { +@@ -1302,7 +1302,11 @@ public int[] findNameSpan(DocCommentTree docTree, ReferenceTree ref) { tokenSequence.move(pos); @@ -1481,7 +1620,7 @@ index a7453c4477..bca4cbd46a 100644 TokenSequence jdocTS = tokenSequence.embedded(JavadocTokenId.language()); diff --git a/java/java.sourceui/nbproject/project.xml b/java/java.sourceui/nbproject/project.xml -index 9ac80a3c09..1f7e220870 100644 +index 9ac80a3c0929..f1bb6375971b 100644 --- a/java/java.sourceui/nbproject/project.xml +++ b/java/java.sourceui/nbproject/project.xml @@ -86,6 +86,14 @@ @@ -1493,17 +1632,25 @@ index 9ac80a3c09..1f7e220870 100644 + + + -+ 1.17 ++ 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 fdabe50444..0ce9410f9e 100644 +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 -@@ -119,6 +119,11 @@ import javax.tools.SimpleJavaFileObject; +@@ -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; @@ -1515,7 +1662,7 @@ index fdabe50444..0ce9410f9e 100644 import javax.lang.model.element.RecordComponentElement; import org.netbeans.api.java.queries.SourceLevelQuery; import org.netbeans.api.java.queries.SourceLevelQuery.Profile; -@@ -1275,7 +1280,7 @@ public class ElementJavadoc { +@@ -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; @@ -1524,13 +1671,23 @@ index fdabe50444..0ce9410f9e 100644 switch (tag.getKind()) { case REFERENCE: ReferenceTree refTag = (ReferenceTree)tag; -@@ -1395,6 +1400,53 @@ public class ElementJavadoc { +@@ -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 -> "MARKDOWN".equals(t.getKind().name()))) { ++ if (tags.stream().noneMatch(t -> t.getKind() == DocTree.Kind.MARKDOWN)) { + return tags; + } + @@ -1538,8 +1695,8 @@ index fdabe50444..0ce9410f9e 100644 + List replacements = new ArrayList<>(); + + for (DocTree t : tags) { -+ if ("MARKDOWN".equals(t.getKind().name())) { -+ markdownSource.append(t.toString()); //TODO: should avoid toString() ++ if (t.getKind() == DocTree.Kind.MARKDOWN) { ++ markdownSource.append(((RawTextTree) t).getContent()); + } else { + markdownSource.append(REPLACEMENT); + replacements.add(t); @@ -1580,10 +1737,10 @@ index fdabe50444..0ce9410f9e 100644 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 0000000000..9aaa49ecb5 +index 000000000000..13bee62039d3 --- /dev/null +++ b/java/java.sourceui/test/unit/src/org/netbeans/api/java/source/ui/ElementJavadocTest.java -@@ -0,0 +1,149 @@ +@@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file @@ -1732,4 +1889,19 @@ index 0000000000..9aaa49ecb5 + 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/7497.diff b/patches/7497.diff deleted file mode 100644 index 87962363..00000000 --- a/patches/7497.diff +++ /dev/null @@ -1,305 +0,0 @@ -diff --git a/java/java.lsp.server/nbcode/integration/nbproject/project.xml b/java/java.lsp.server/nbcode/integration/nbproject/project.xml -index 3cee4feb1698..70102d62f3a3 100644 ---- a/java/java.lsp.server/nbcode/integration/nbproject/project.xml -+++ b/java/java.lsp.server/nbcode/integration/nbproject/project.xml -@@ -118,6 +118,15 @@ - - - -+ -+ org.netbeans.modules.java.platform -+ -+ -+ -+ 1 -+ 1.66 -+ -+ - - org.netbeans.modules.parsing.api - -diff --git a/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspJavaPlatformProviderOverride.java b/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspJavaPlatformProviderOverride.java -new file mode 100644 -index 000000000000..31066657e35d ---- /dev/null -+++ b/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspJavaPlatformProviderOverride.java -@@ -0,0 +1,31 @@ -+/* -+ * 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.nbcode.integration; -+ -+import org.netbeans.modules.java.lsp.server.ui.AbstractJavaPlatformProviderOverride; -+import org.netbeans.modules.java.platform.implspi.JavaPlatformProvider; -+import org.openide.util.lookup.ServiceProvider; -+ -+/** -+ * -+ * @author sdedic -+ */ -+@ServiceProvider(service = JavaPlatformProvider.class, position = 10_000) -+public class LspJavaPlatformProviderOverride extends AbstractJavaPlatformProviderOverride { -+} -diff --git a/java/java.lsp.server/nbproject/project.xml b/java/java.lsp.server/nbproject/project.xml -index 2b5122a231bb..cea7ea966aae 100644 ---- a/java/java.lsp.server/nbproject/project.xml -+++ b/java/java.lsp.server/nbproject/project.xml -@@ -400,6 +400,15 @@ - 0.3 - - -+ -+ org.netbeans.modules.java.platform -+ -+ -+ -+ 1 -+ 1.66 -+ -+ - - org.netbeans.modules.java.project - -@@ -697,12 +706,6 @@ - 9.24 - - -- -- com.google.guava -- -- 27.16 -- -- - - - -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java -index f42d7ee24809..2b4565d80dd5 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java -@@ -45,6 +45,7 @@ - import java.util.logging.Logger; - import com.google.gson.InstanceCreator; - import com.google.gson.JsonObject; -+import com.google.gson.JsonPrimitive; - import java.util.prefs.Preferences; - import java.util.LinkedHashSet; - import java.util.Objects; -@@ -382,6 +383,7 @@ public static class LanguageServerImpl implements LanguageServer, LanguageClient - - private static final String NETBEANS_FORMAT = "format"; - private static final String NETBEANS_JAVA_IMPORTS = "java.imports"; -+ private static final String NETBEANS_PROJECT_JDKHOME = "project.jdkhome"; - private static final String NETBEANS_JAVA_HINTS = "hints"; - - // change to a greater throughput if the initialization waits on more processes than just (serialized) project open. -@@ -1020,6 +1022,7 @@ private void collectProjectCandidates(FileObject fo, List candidates - private void initializeOptions() { - getWorkspaceProjects().thenAccept(projects -> { - ConfigurationItem item = new ConfigurationItem(); -+ // PENDING: what about doing just one roundtrip to the client- we may request multiple ConfiguratonItems in one message ? - item.setSection(client.getNbCodeCapabilities().getConfigurationPrefix() + NETBEANS_JAVA_HINTS); - client.configuration(new ConfigurationParams(Collections.singletonList(item))).thenAccept(c -> { - if (c != null && !c.isEmpty() && c.get(0) instanceof JsonObject) { -@@ -1030,6 +1033,16 @@ private void initializeOptions() { - textDocumentService.reRunDiagnostics(); - } - }); -+ item.setSection(client.getNbCodeCapabilities().getConfigurationPrefix() + NETBEANS_PROJECT_JDKHOME); -+ client.configuration(new ConfigurationParams(Collections.singletonList(item))).thenAccept(c -> { -+ JsonPrimitive newProjectJDKHomePath = null; -+ -+ if (c != null && !c.isEmpty() && c.get(0) instanceof JsonPrimitive) { -+ newProjectJDKHomePath = (JsonPrimitive) c.get(0); -+ } else { -+ } -+ textDocumentService.updateProjectJDKHome(newProjectJDKHomePath); -+ }); - if (projects != null && projects.length > 0) { - FileObject fo = projects[0].getProjectDirectory(); - item.setScopeUri(Utils.toUri(fo)); -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 ad0d82fa448e..c112b4eb73d7 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 -@@ -240,6 +240,7 @@ - import org.netbeans.api.lsp.StructureElement; - import org.netbeans.modules.editor.indent.api.Reformat; - import org.netbeans.modules.java.lsp.server.URITranslator; -+import org.netbeans.modules.java.lsp.server.ui.AbstractJavaPlatformProviderOverride; - import org.netbeans.modules.parsing.impl.SourceAccessor; - import org.netbeans.spi.editor.hints.ErrorDescription; - import org.netbeans.spi.editor.hints.Fix; -@@ -2086,6 +2087,15 @@ void updateJavaHintPreferences(JsonObject configuration) { - reRunDiagnostics(); - } - -+ void updateProjectJDKHome(JsonPrimitive configuration) { -+ if (configuration == null) { -+ client.logMessage(new MessageParams(MessageType.Log,"Project runtime JDK unset, defaults to NBLS JDK")); -+ } else { -+ client.logMessage(new MessageParams(MessageType.Log, "Project runtime JDK set to " + configuration.getAsString())); -+ } -+ AbstractJavaPlatformProviderOverride.setDefaultPlatformOverride(configuration != null ? configuration.getAsString() : null); -+ } -+ - private String key(ErrorProvider.Kind errorKind) { - return errorKind.name().toLowerCase(Locale.ROOT); - } -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java -index d3027a2eb620..6e0755bfb6e6 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java -@@ -1347,7 +1347,9 @@ public void didChangeConfiguration(DidChangeConfigurationParams params) { - String fullConfigPrefix = client.getNbCodeCapabilities().getConfigurationPrefix(); - String configPrefix = fullConfigPrefix.substring(0, fullConfigPrefix.length() - 1); - server.openedProjects().thenAccept(projects -> { -+ // PENDING: invent a pluggable mechanism for this, this does not scale and the typecast to serviceImpl is ugly - ((TextDocumentServiceImpl)server.getTextDocumentService()).updateJavaHintPreferences(((JsonObject) params.getSettings()).getAsJsonObject(configPrefix).getAsJsonObject(NETBEANS_JAVA_HINTS)); -+ ((TextDocumentServiceImpl)server.getTextDocumentService()).updateProjectJDKHome(((JsonObject) params.getSettings()).getAsJsonObject(configPrefix).getAsJsonObject("project").getAsJsonPrimitive("jdkhome")); - if (projects != null && projects.length > 0) { - updateJavaFormatPreferences(projects[0].getProjectDirectory(), ((JsonObject) params.getSettings()).getAsJsonObject(configPrefix).getAsJsonObject("format")); - updateJavaImportPreferences(projects[0].getProjectDirectory(), ((JsonObject) params.getSettings()).getAsJsonObject(configPrefix).getAsJsonObject("java").getAsJsonObject("imports")); -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/AbstractJavaPlatformProviderOverride.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/AbstractJavaPlatformProviderOverride.java -new file mode 100644 -index 000000000000..df668954dd94 ---- /dev/null -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/AbstractJavaPlatformProviderOverride.java -@@ -0,0 +1,122 @@ -+/* -+ * 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.lsp.server.ui; -+ -+import java.beans.PropertyChangeListener; -+import java.beans.PropertyChangeSupport; -+import java.io.File; -+import java.io.IOException; -+import java.util.HashSet; -+import java.util.Set; -+import org.netbeans.api.java.platform.JavaPlatform; -+import org.netbeans.api.java.platform.JavaPlatformManager; -+import org.netbeans.modules.java.platform.implspi.JavaPlatformProvider; -+import org.netbeans.spi.java.platform.JavaPlatformFactory; -+import org.openide.filesystems.FileObject; -+import org.openide.filesystems.FileUtil; -+import org.openide.util.Exceptions; -+import org.openide.util.Lookup; -+ -+public abstract class AbstractJavaPlatformProviderOverride implements JavaPlatformProvider { -+ -+ private static final JavaPlatform[] NO_PLATFORMS = new JavaPlatform[0]; -+ private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); -+ private FileObject defaultPlatformOverride; -+ -+ @Override -+ @SuppressWarnings("ReturnOfCollectionOrArrayField") -+ public JavaPlatform[] getInstalledPlatforms() { -+ return NO_PLATFORMS; -+ } -+ -+ @Override -+ public JavaPlatform getDefaultPlatform() { -+ FileObject override; -+ -+ synchronized (this) { -+ override = defaultPlatformOverride; -+ } -+ -+ if (override == null) { -+ return null; -+ } -+ -+ Set existingNames = new HashSet<>(); -+ JavaPlatform found = null; -+ -+ for (JavaPlatform platform : JavaPlatformManager.getDefault().getInstalledPlatforms()) { -+ if (platform.getInstallFolders().stream().anyMatch(folder -> folder.equals(override))) { -+ found = platform; -+ break; -+ } -+ existingNames.add(platform.getDisplayName()); -+ } -+ -+ if (found == null ){ -+ String newName = defaultPlatformOverride.getPath(); -+ -+ while (existingNames.contains(newName)) { -+ newName += "1"; -+ } -+ -+ for (JavaPlatformFactory.Provider provider : Lookup.getDefault().lookupAll(JavaPlatformFactory.Provider.class)) { -+ JavaPlatformFactory factory = provider.forType("j2se"); -+ if (factory != null) { -+ try { -+ found = factory.create(override, newName, true); -+ } catch (IOException ex) { -+ Exceptions.printStackTrace(ex); -+ } -+ } -+ } -+ } -+ -+ return found; -+ } -+ -+ @Override -+ public void addPropertyChangeListener(PropertyChangeListener listener) { -+ pcs.addPropertyChangeListener(listener); -+ } -+ -+ @Override -+ public void removePropertyChangeListener(PropertyChangeListener listener) { -+ pcs.removePropertyChangeListener(listener); -+ } -+ -+ private void dosetDefaultPlatformOverride(String defaultPlatformOverride) { -+ FileObject override = defaultPlatformOverride != null ? FileUtil.toFileObject(new File(defaultPlatformOverride)) -+ : null; -+ -+ synchronized (this) { -+ this.defaultPlatformOverride = override; -+ } -+ -+ pcs.firePropertyChange(null, null, null); -+ } -+ -+ public static void setDefaultPlatformOverride(String defaultPlatformOverride) { -+ for (JavaPlatformProvider p : Lookup.getDefault().lookupAll(JavaPlatformProvider.class)) { -+ if (p instanceof AbstractJavaPlatformProviderOverride) { -+ ((AbstractJavaPlatformProviderOverride) p).dosetDefaultPlatformOverride(defaultPlatformOverride); -+ } -+ } -+ } -+ -+} diff --git a/patches/7548_source-1.8.diff b/patches/7548_source-1.8.diff deleted file mode 100644 index fc7c7191..00000000 --- a/patches/7548_source-1.8.diff +++ /dev/null @@ -1,73 +0,0 @@ -diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Unused.java b/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Unused.java -index e681ed7b97..0d0c3c4c77 100644 ---- a/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Unused.java -+++ b/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Unused.java -@@ -20,6 +20,7 @@ package org.netbeans.modules.java.hints.bugs; - - import com.sun.source.tree.Tree.Kind; - import java.util.ArrayList; -+import java.util.Arrays; - import java.util.List; - import javax.lang.model.element.ElementKind; - import org.netbeans.modules.java.editor.base.semantic.UnusedDetector; -@@ -34,6 +35,7 @@ import org.netbeans.spi.java.hints.JavaFixUtilities; - import org.netbeans.spi.java.hints.TriggerTreeKind; - import org.openide.util.NbBundle.Messages; - -+import static org.netbeans.api.java.source.CompilationInfo.CacheClearPolicy.ON_TASK_END; - /** - * - * @author lahvac -@@ -52,25 +54,47 @@ public class Unused { - @BooleanOption(displayName="#LBL_UnusedPackagePrivate", tooltip="#TP_UnusedPackagePrivate", defaultValue=DETECT_UNUSED_PACKAGE_PRIVATE_DEFAULT) - public static final String DETECT_UNUSED_PACKAGE_PRIVATE = "detect.unused.package.private"; - -- @TriggerTreeKind(Kind.COMPILATION_UNIT) -+ @TriggerTreeKind({ -+ //class-like kinds: -+ Kind.ANNOTATION_TYPE, Kind.CLASS, Kind.ENUM, Kind.INTERFACE, Kind.RECORD, -+ Kind.VARIABLE, -+ Kind.METHOD -+ }) - public static List unused(HintContext ctx) { - List unused = UnusedDetector.findUnused(ctx.getInfo(), () -> ctx.isCanceled()); -- List result = new ArrayList<>(unused.size()); -- boolean detectUnusedPackagePrivate = ctx.getPreferences().getBoolean(DETECT_UNUSED_PACKAGE_PRIVATE, DETECT_UNUSED_PACKAGE_PRIVATE_DEFAULT); -+ if (unused.isEmpty()) { -+ return null; -+ } -+ boolean detectUnusedPackagePrivate = getTaskCachedBoolean(ctx, DETECT_UNUSED_PACKAGE_PRIVATE, DETECT_UNUSED_PACKAGE_PRIVATE_DEFAULT); - for (UnusedDescription ud : unused) { - if (ctx.isCanceled()) { - break; - } -+ if (ud.unusedElementPath.getLeaf() != ctx.getPath().getLeaf()) { -+ continue; -+ } - if (!detectUnusedPackagePrivate && ud.packagePrivate) { - continue; - } - ErrorDescription err = convertUnused(ctx, ud); - if (err != null) { -- result.add(err); -+ return Arrays.asList(err); - } -+ break; - } -- return result; -+ return null; - } -+ -+ // reading from AuxiliaryConfigBasedPreferences in inner loops is not cheap since it needs a mutex -+ private static boolean getTaskCachedBoolean(HintContext ctx, String key, boolean defaultVal) { -+ Object cached = ctx.getInfo().getCachedValue(key); -+ if (cached instanceof Boolean) { -+ return (Boolean)cached; -+ } -+ boolean fromPrefs = ctx.getPreferences().getBoolean(key, defaultVal); -+ ctx.getInfo().putCachedValue(key, fromPrefs, ON_TASK_END); -+ return fromPrefs; -+ } - - @Messages({ - "# {0} - variable name", diff --git a/patches/7583_source-1.8.diff b/patches/7583_source-1.8.diff deleted file mode 100644 index d8250b61..00000000 --- a/patches/7583_source-1.8.diff +++ /dev/null @@ -1,80 +0,0 @@ -diff --git a/extide/gradle/src/org/netbeans/modules/gradle/api/execute/GradleDistributionManager.java b/extide/gradle/src/org/netbeans/modules/gradle/api/execute/GradleDistributionManager.java -index 024f95a670..0a106ffbb6 100644 ---- a/extide/gradle/src/org/netbeans/modules/gradle/api/execute/GradleDistributionManager.java -+++ b/extide/gradle/src/org/netbeans/modules/gradle/api/execute/GradleDistributionManager.java -@@ -101,9 +101,10 @@ public final class GradleDistributionManager { - GradleVersion.version("7.6"), // JDK-19 - GradleVersion.version("8.3"), // JDK-20 - GradleVersion.version("8.5"), // JDK-21 -+ GradleVersion.version("8.8"), // JDK-22 - }; - -- private static final GradleVersion LAST_KNOWN_GRADLE = GradleVersion.version("8.7"); //NOI18N -+ private static final GradleVersion LAST_KNOWN_GRADLE = GradleVersion.version("8.9"); //NOI18N - - final File gradleUserHome; - -diff --git a/extide/libs.gradle/external/binaries-list b/extide/libs.gradle/external/binaries-list -index 2e58f89b85fd..dff2c2265b37 100644 ---- a/extide/libs.gradle/external/binaries-list -+++ b/extide/libs.gradle/external/binaries-list -@@ -15,4 +15,4 @@ - # specific language governing permissions and limitations - # under the License. - --5F48B9BB9099B900FC33864A3794F31C439D9F73 https://repo.gradle.org/artifactory/libs-releases/org/gradle/gradle-tooling-api/8.7/gradle-tooling-api-8.7.jar gradle-tooling-api-8.7.jar -+7BCC4423C529A42ECA9D0CE5B5275369EF4DF55A https://repo.gradle.org/artifactory/libs-releases/org/gradle/gradle-tooling-api/8.9/gradle-tooling-api-8.9.jar gradle-tooling-api-8.9.jar -diff --git a/extide/libs.gradle/external/gradle-tooling-api-8.7-license.txt b/extide/libs.gradle/external/gradle-tooling-api-8.9-license.txt -similarity index 99% -rename from extide/libs.gradle/external/gradle-tooling-api-8.7-license.txt -rename to extide/libs.gradle/external/gradle-tooling-api-8.9-license.txt -index 84a9de902f75..74cb1addb8d6 100644 ---- a/extide/libs.gradle/external/gradle-tooling-api-8.7-license.txt -+++ b/extide/libs.gradle/external/gradle-tooling-api-8.9-license.txt -@@ -1,7 +1,7 @@ - Name: Gradle Tooling API - Description: Gradle Tooling API --Version: 8.7 --Files: gradle-tooling-api-8.7.jar -+Version: 8.9 -+Files: gradle-tooling-api-8.9.jar - License: Apache-2.0 - Origin: Gradle Inc. - URL: https://gradle.org/ -diff --git a/extide/libs.gradle/external/gradle-tooling-api-8.7-notice.txt b/extide/libs.gradle/external/gradle-tooling-api-8.9-notice.txt -similarity index 100% -rename from extide/libs.gradle/external/gradle-tooling-api-8.7-notice.txt -rename to extide/libs.gradle/external/gradle-tooling-api-8.9-notice.txt -diff --git a/extide/libs.gradle/manifest.mf b/extide/libs.gradle/manifest.mf -index 64b5cf508e40..a489976e11ed 100644 ---- a/extide/libs.gradle/manifest.mf -+++ b/extide/libs.gradle/manifest.mf -@@ -2,4 +2,4 @@ Manifest-Version: 1.0 - AutoUpdate-Show-In-Client: false - OpenIDE-Module: org.netbeans.modules.libs.gradle/8 - OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/libs/gradle/Bundle.properties --OpenIDE-Module-Specification-Version: 8.7 -+OpenIDE-Module-Specification-Version: 8.9 -diff --git a/extide/libs.gradle/nbproject/project.properties b/extide/libs.gradle/nbproject/project.properties -index 6cd0e22d8315..6e4605fe4922 100644 ---- a/extide/libs.gradle/nbproject/project.properties -+++ b/extide/libs.gradle/nbproject/project.properties -@@ -22,4 +22,4 @@ javac.compilerargs=-Xlint -Xlint:-serial - # Sigtest fails to read the classes in the gradle-tooling-api - sigtest.skip.gen=true - --release.external/gradle-tooling-api-8.7.jar=modules/gradle/gradle-tooling-api.jar -+release.external/gradle-tooling-api-8.9.jar=modules/gradle/gradle-tooling-api.jar -diff --git a/extide/libs.gradle/nbproject/project.xml b/extide/libs.gradle/nbproject/project.xml -index dc58e80a4500..d82027b5e615 100644 ---- a/extide/libs.gradle/nbproject/project.xml -+++ b/extide/libs.gradle/nbproject/project.xml -@@ -39,7 +39,7 @@ - - - gradle/gradle-tooling-api.jar -- external/gradle-tooling-api-8.7.jar -+ external/gradle-tooling-api-8.9.jar - - - diff --git a/patches/7621.diff b/patches/7621.diff deleted file mode 100644 index ea5e3e64..00000000 --- a/patches/7621.diff +++ /dev/null @@ -1,125 +0,0 @@ -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ConstructorGenerator.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ConstructorGenerator.java -index 583f90585472..c4602b78e2d6 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ConstructorGenerator.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ConstructorGenerator.java -@@ -188,7 +188,7 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat - data.put(OFFSET, startOffset); - data.put(CONSTRUCTORS, constructors); - data.put(FIELDS, fields); -- return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateConstructor(), isSource ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, null, "nbls.generate.code", GENERATE_CONSTRUCTOR, data)); -+ return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateConstructor(), isSource ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_CONSTRUCTOR, client.getNbCodeCapabilities()), data)); - } - - @Override -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/DelegateMethodGenerator.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/DelegateMethodGenerator.java -index 74c4523b85be..b7eae2e038e4 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/DelegateMethodGenerator.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/DelegateMethodGenerator.java -@@ -137,7 +137,7 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat - data.put(OFFSET, offset); - data.put(TYPE, typeItem); - data.put(FIELDS, fields); -- return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateDelegateMethod(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_DELEGATE_METHOD, data)); -+ return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateDelegateMethod(), CODE_GENERATOR_KIND, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_DELEGATE_METHOD, client.getNbCodeCapabilities()), data)); - } - - @Override -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/EqualsHashCodeGenerator.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/EqualsHashCodeGenerator.java -index 4e9b4f747a59..7f2a8084d80f 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/EqualsHashCodeGenerator.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/EqualsHashCodeGenerator.java -@@ -115,11 +115,11 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat - String uri = Utils.toUri(info.getFileObject()); - if (equalsHashCode[0] == null) { - if (equalsHashCode[1] == null) { -- return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateEqualsHashCode(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_EQUALS_HASHCODE, data(0, uri, offset, fields))); -+ return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateEqualsHashCode(), CODE_GENERATOR_KIND, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_EQUALS_HASHCODE, client.getNbCodeCapabilities()), data(0, uri, offset, fields))); - } -- return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateEquals(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_EQUALS_HASHCODE, data(EQUALS_ONLY, uri, offset, fields))); -+ return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateEquals(), CODE_GENERATOR_KIND, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_EQUALS_HASHCODE, client.getNbCodeCapabilities()), data(EQUALS_ONLY, uri, offset, fields))); - } -- return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateHashCode(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_EQUALS_HASHCODE, data(HASH_CODE_ONLY, uri, offset, fields))); -+ return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateHashCode(), CODE_GENERATOR_KIND, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_EQUALS_HASHCODE, client.getNbCodeCapabilities()), data(HASH_CODE_ONLY, uri, offset, fields))); - } - - @Override -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/GetterSetterGenerator.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/GetterSetterGenerator.java -index fb85050e92be..0a96c15dcea8 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/GetterSetterGenerator.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/GetterSetterGenerator.java -@@ -103,7 +103,7 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat - List result = new ArrayList<>(); - if (missingGetters) { - String name = pair.first().size() == 1 ? Bundle.DN_GenerateGetterFor(pair.first().iterator().next().getSimpleName().toString()) : Bundle.DN_GenerateGetters(); -- result.add(createCodeAction(client, name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, null, "nbls.generate.code", GENERATE_GETTER_SETTER, data(GeneratorUtils.GETTERS_ONLY, uri, offset, all, pair.first().stream().map(variableElement -> { -+ result.add(createCodeAction(client, name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_GETTER_SETTER, client.getNbCodeCapabilities()), data(GeneratorUtils.GETTERS_ONLY, uri, offset, all, pair.first().stream().map(variableElement -> { - QuickPickItem item = new QuickPickItem(createLabel(info, variableElement)); - item.setUserData(new ElementData(variableElement)); - return item; -@@ -111,7 +111,7 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat - } - if (missingSetters) { - String name = pair.second().size() == 1 ? Bundle.DN_GenerateSetterFor(pair.second().iterator().next().getSimpleName().toString()) : Bundle.DN_GenerateSetters(); -- result.add(createCodeAction(client, name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, null, "nbls.generate.code", GENERATE_GETTER_SETTER, data(GeneratorUtils.SETTERS_ONLY, uri, offset, all, pair.second().stream().map(variableElement -> { -+ result.add(createCodeAction(client, name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_GETTER_SETTER, client.getNbCodeCapabilities()), data(GeneratorUtils.SETTERS_ONLY, uri, offset, all, pair.second().stream().map(variableElement -> { - QuickPickItem item = new QuickPickItem(createLabel(info, variableElement)); - item.setUserData(new ElementData(variableElement)); - return item; -@@ -120,7 +120,7 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat - if (missingGetters && missingSetters) { - pair.first().retainAll(pair.second()); - String name = pair.first().size() == 1 ? Bundle.DN_GenerateGetterSetterFor(pair.first().iterator().next().getSimpleName().toString()) : Bundle.DN_GenerateGettersSetters(); -- result.add(createCodeAction(client, name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, null, "nbls.generate.code", GENERATE_GETTER_SETTER, data(0, uri, offset, all, pair.first().stream().map(variableElement -> { -+ result.add(createCodeAction(client, name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_GETTER_SETTER, client.getNbCodeCapabilities()), data(0, uri, offset, all, pair.first().stream().map(variableElement -> { - QuickPickItem item = new QuickPickItem(createLabel(info, variableElement)); - item.setUserData(new ElementData(variableElement)); - return item; -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ImplementOverrideMethodGenerator.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ImplementOverrideMethodGenerator.java -index c839e8e60dde..4aca8de3ca64 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ImplementOverrideMethodGenerator.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ImplementOverrideMethodGenerator.java -@@ -109,7 +109,7 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat - implementMethods.add(new QuickPickItem(createLabel(info, method), enclosingTypeName, null, mustImplement, new ElementData(method))); - } - if (!implementMethods.isEmpty()) { -- result.add(createCodeAction(client, Bundle.DN_GenerateImplementMethod(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_IMPLEMENT_OVERRIDE, data(uri, offset, true, implementMethods))); -+ result.add(createCodeAction(client, Bundle.DN_GenerateImplementMethod(), CODE_GENERATOR_KIND, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_IMPLEMENT_OVERRIDE, client.getNbCodeCapabilities()), data(uri, offset, true, implementMethods))); - } - } - if (typeElement.getKind().isClass() || typeElement.getKind().isInterface()) { -@@ -125,7 +125,7 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat - overrideMethods.add(item); - } - if (!overrideMethods.isEmpty()) { -- result.add(createCodeAction(client, Bundle.DN_GenerateOverrideMethod(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_IMPLEMENT_OVERRIDE, data(uri, offset, false, overrideMethods))); -+ result.add(createCodeAction(client, Bundle.DN_GenerateOverrideMethod(), CODE_GENERATOR_KIND, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_IMPLEMENT_OVERRIDE, client.getNbCodeCapabilities()), data(uri, offset, false, overrideMethods))); - } - } - return result; -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LoggerGenerator.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LoggerGenerator.java -index 4c92f0975fd2..46afb67b3541 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LoggerGenerator.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LoggerGenerator.java -@@ -108,7 +108,7 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat - Map data = new HashMap<>(); - data.put(URI, uri); - data.put(OFFSET, offset); -- return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateLogger(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_LOGGER, data)); -+ return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateLogger(), CODE_GENERATOR_KIND, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_LOGGER, client.getNbCodeCapabilities()), data)); - } - - @Override -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ToStringGenerator.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ToStringGenerator.java -index b88b31213572..9391da00bffa 100644 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ToStringGenerator.java -+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ToStringGenerator.java -@@ -116,7 +116,7 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat - data.put(URI, uri); - data.put(OFFSET, offset); - data.put(FIELDS, fields); -- return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateToString(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_TO_STRING, data)); -+ return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateToString(), CODE_GENERATOR_KIND, null, "nbls.generate.code", Utils.encodeCommand(GENERATE_TO_STRING, client.getNbCodeCapabilities()), data)); - } - - @Override - diff --git a/patches/7670.diff b/patches/7670.diff new file mode 100644 index 00000000..486c231c --- /dev/null +++ b/patches/7670.diff @@ -0,0 +1,2037 @@ +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/7690.diff b/patches/7690.diff deleted file mode 100644 index 75ffbbfb..00000000 --- a/patches/7690.diff +++ /dev/null @@ -1,83 +0,0 @@ -diff --git a/extide/gradle/src/org/netbeans/modules/gradle/api/execute/GradleDistributionManager.java b/extide/gradle/src/org/netbeans/modules/gradle/api/execute/GradleDistributionManager.java -index 8b93f19d3208..051d95a2cb8b 100644 ---- a/extide/gradle/src/org/netbeans/modules/gradle/api/execute/GradleDistributionManager.java -+++ b/extide/gradle/src/org/netbeans/modules/gradle/api/execute/GradleDistributionManager.java -@@ -100,9 +100,10 @@ public final class GradleDistributionManager { - GradleVersion.version("8.3"), // JDK-20 - GradleVersion.version("8.5"), // JDK-21 - GradleVersion.version("8.8"), // JDK-22 -+ GradleVersion.version("8.10"),// JDK-23 - }; - -- private static final GradleVersion LAST_KNOWN_GRADLE = GradleVersion.version("8.9"); //NOI18N -+ private static final GradleVersion LAST_KNOWN_GRADLE = GradleVersion.version("8.10"); //NOI18N - - final File gradleUserHome; - -diff --git a/extide/libs.gradle/external/binaries-list b/extide/libs.gradle/external/binaries-list -index dff2c2265b37..72218b9d5a29 100644 ---- a/extide/libs.gradle/external/binaries-list -+++ b/extide/libs.gradle/external/binaries-list -@@ -15,4 +15,4 @@ - # specific language governing permissions and limitations - # under the License. - --7BCC4423C529A42ECA9D0CE5B5275369EF4DF55A https://repo.gradle.org/artifactory/libs-releases/org/gradle/gradle-tooling-api/8.9/gradle-tooling-api-8.9.jar gradle-tooling-api-8.9.jar -+1FC754376876B11AE26D811F8812AA37773660DD https://repo.gradle.org/artifactory/libs-releases/org/gradle/gradle-tooling-api/8.10/gradle-tooling-api-8.10.jar gradle-tooling-api-8.10.jar -diff --git a/extide/libs.gradle/external/gradle-tooling-api-8.9-license.txt b/extide/libs.gradle/external/gradle-tooling-api-8.10-license.txt -similarity index 99% -rename from extide/libs.gradle/external/gradle-tooling-api-8.9-license.txt -rename to extide/libs.gradle/external/gradle-tooling-api-8.10-license.txt -index 74cb1addb8d6..ab7d5a2a2558 100644 ---- a/extide/libs.gradle/external/gradle-tooling-api-8.9-license.txt -+++ b/extide/libs.gradle/external/gradle-tooling-api-8.10-license.txt -@@ -1,7 +1,7 @@ - Name: Gradle Tooling API - Description: Gradle Tooling API --Version: 8.9 --Files: gradle-tooling-api-8.9.jar -+Version: 8.10 -+Files: gradle-tooling-api-8.10.jar - License: Apache-2.0 - Origin: Gradle Inc. - URL: https://gradle.org/ -diff --git a/extide/libs.gradle/external/gradle-tooling-api-8.9-notice.txt b/extide/libs.gradle/external/gradle-tooling-api-8.10-notice.txt -similarity index 100% -rename from extide/libs.gradle/external/gradle-tooling-api-8.9-notice.txt -rename to extide/libs.gradle/external/gradle-tooling-api-8.10-notice.txt -diff --git a/extide/libs.gradle/nbproject/project.properties b/extide/libs.gradle/nbproject/project.properties -index 6e4605fe4922..d20b5a229d4c 100644 ---- a/extide/libs.gradle/nbproject/project.properties -+++ b/extide/libs.gradle/nbproject/project.properties -@@ -22,4 +22,4 @@ javac.compilerargs=-Xlint -Xlint:-serial - # Sigtest fails to read the classes in the gradle-tooling-api - sigtest.skip.gen=true - --release.external/gradle-tooling-api-8.9.jar=modules/gradle/gradle-tooling-api.jar -+release.external/gradle-tooling-api-8.10.jar=modules/gradle/gradle-tooling-api.jar -diff --git a/extide/libs.gradle/nbproject/project.xml b/extide/libs.gradle/nbproject/project.xml -index d82027b5e615..9b1dfe36f2ad 100644 ---- a/extide/libs.gradle/nbproject/project.xml -+++ b/extide/libs.gradle/nbproject/project.xml -@@ -39,7 +39,7 @@ - - - gradle/gradle-tooling-api.jar -- external/gradle-tooling-api-8.9.jar -+ external/gradle-tooling-api-8.10.jar - - - -diff --git a/java/gradle.java/src/org/netbeans/modules/gradle/java/newproject/Wizards.java b/java/gradle.java/src/org/netbeans/modules/gradle/java/newproject/Wizards.java -index 255def8bce12..031c25128452 100644 ---- a/java/gradle.java/src/org/netbeans/modules/gradle/java/newproject/Wizards.java -+++ b/java/gradle.java/src/org/netbeans/modules/gradle/java/newproject/Wizards.java -@@ -35,7 +35,7 @@ public final class Wizards { - - private Wizards() {}; - -- private static final List JAVA_VERSIONS = List.of(22, 21, 17, 11, 8); -+ private static final List JAVA_VERSIONS = List.of(23, 22, 21, 17, 11, 8); - private static final List JAVA_TEST_FRAMEWORKS = List.of( - JUNIT, - JUNIT_5, diff --git a/patches/7733.diff b/patches/7733.diff new file mode 100644 index 00000000..4b7646db --- /dev/null +++ b/patches/7733.diff @@ -0,0 +1,614 @@ +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/remove-db.diff b/patches/remove-db.diff index 844ce8b4..3ee12a3d 100644 --- a/patches/remove-db.diff +++ b/patches/remove-db.diff @@ -457,222 +457,6 @@ index e3a89225a1..0000000000 - return CompletableFuture.completedFuture(em); - } -} -diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/db/DBConnectionProvider.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/db/DBConnectionProvider.java -deleted file mode 100644 -index 9da0614d30..0000000000 ---- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/db/DBConnectionProvider.java -+++ /dev/null -@@ -1,210 +0,0 @@ --/* -- * 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.lsp.server.db; -- --import java.io.FileWriter; --import java.io.IOException; --import java.io.Writer; --import java.nio.charset.Charset; --import java.nio.file.DirectoryStream; --import java.nio.file.FileSystems; --import java.nio.file.Files; --import java.nio.file.LinkOption; --import java.nio.file.Path; --import java.nio.file.attribute.AclEntry; --import java.nio.file.attribute.AclEntryPermission; --import static java.nio.file.attribute.AclEntryPermission.*; --import java.nio.file.attribute.AclFileAttributeView; --import java.nio.file.attribute.DosFileAttributeView; --import java.nio.file.attribute.FileAttribute; --import java.nio.file.attribute.PosixFileAttributeView; --import java.nio.file.attribute.PosixFilePermission; --import static java.nio.file.attribute.PosixFilePermission.*; --import java.nio.file.attribute.PosixFilePermissions; --import java.util.Collections; --import java.util.EnumSet; --import java.util.HashMap; --import java.util.List; --import java.util.Map; --import java.util.Properties; --import java.util.Set; --import java.util.concurrent.CompletableFuture; --import java.util.logging.Level; --import java.util.logging.Logger; --import org.netbeans.api.db.explorer.ConnectionManager; --import org.netbeans.api.db.explorer.DatabaseConnection; --import org.netbeans.spi.lsp.CommandProvider; --import org.openide.util.lookup.ServiceProvider; -- --/** -- * -- * @author Jan Horvath -- */ --@ServiceProvider(service = CommandProvider.class) --public class DBConnectionProvider implements CommandProvider { -- private static final Logger LOG = Logger.getLogger(DBConnectionProvider.class.getName()); -- private static final String GET_DB_CONNECTION = "nbls.db.connection"; //NOI18N -- -- private static final boolean POSIX = FileSystems.getDefault().supportedFileAttributeViews().contains("posix"); // NOI18N -- private static final EnumSet readWritePosix = EnumSet.of(OWNER_READ, OWNER_WRITE); -- private static final EnumSet readOnlyAcl = EnumSet.of(READ_ACL, READ_ATTRIBUTES, WRITE_ATTRIBUTES, READ_DATA, READ_NAMED_ATTRS, DELETE, SYNCHRONIZE); -- -- // temporary directory location -- private static final Path tmpdir = Path.of(System.getProperty("java.io.tmpdir")); // NOI18N -- -- public DBConnectionProvider() { -- try { -- deleteOldFiles(generateDirPath()); -- } catch (IOException ex) { -- LOG.log(Level.SEVERE, "deleteOldFiles", ex); -- } -- } -- -- @Override -- public CompletableFuture runCommand(String command, List arguments) { -- Map result = new HashMap<> (); -- CompletableFuture ret = new CompletableFuture(); -- Properties dbProps = new Properties(); -- DatabaseConnection conn = ConnectionManager.getDefault().getPreferredConnection(true); -- -- if (conn != null) { -- Path temp = null; -- Path dir = generateDirPath(); -- -- try { -- if (!Files.isDirectory(dir, LinkOption.NOFOLLOW_LINKS)) { -- Files.createDirectory(dir); -- } -- if (POSIX) { -- FileAttribute readWriteAttribs = PosixFilePermissions.asFileAttribute(readWritePosix); --// FileAttribute readWriteAttribs = PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rw-------")); -- temp = Files.createTempFile(dir, "db-", ".properties", readWriteAttribs); // NOI18N -- } else { -- temp = Files.createTempFile(dir, "db-", ".properties"); // NOI18N -- AclFileAttributeView acl = Files.getFileAttributeView(temp, AclFileAttributeView.class); -- AclEntry ownerEntry = null; -- for(AclEntry e : acl.getAcl()) { -- if (e.principal().equals(acl.getOwner())) { -- ownerEntry = e; -- break; -- } -- } -- if (ownerEntry != null) { -- acl.setAcl(Collections.singletonList(ownerEntry)); -- } else { -- deleteTempFile(temp); -- ret.completeExceptionally(new IOException("Owner missing, file:"+temp.toString())); // NOI18N -- return ret; -- } -- } -- } catch (IOException ex) { -- deleteTempFile(temp); -- ret.completeExceptionally(ex); -- return ret; -- } -- -- try (Writer writer = new FileWriter(temp.toFile(), Charset.defaultCharset());) { -- dbProps.put("datasources.default.url", conn.getDatabaseURL()); //NOI18N -- dbProps.put("datasources.default.username", conn.getUser()); //NOI18N -- dbProps.put("datasources.default.password", conn.getPassword()); //NOI18N -- dbProps.put("datasources.default.driverClassName", conn.getDriverClass()); //NOI18N -- String ocid = (String) conn.getConnectionProperties().get("OCID"); //NOI18N -- if (ocid != null && !ocid.isEmpty()) { -- dbProps.put("datasources.default.ocid", ocid); //NOI18N -- } -- dbProps.store(writer, ""); -- if (POSIX) { -- PosixFileAttributeView attribs = Files.getFileAttributeView(temp, PosixFileAttributeView.class); -- attribs.setPermissions(EnumSet.of(OWNER_READ)); -- } else { -- DosFileAttributeView attribs = Files.getFileAttributeView(temp, DosFileAttributeView.class); -- attribs.setReadOnly(true); -- AclFileAttributeView acl = Files.getFileAttributeView(temp, AclFileAttributeView.class); -- AclEntry ownerEntry = null; -- if (acl.getAcl().size() != 1) { -- deleteTempFile(temp); -- ret.completeExceptionally(new IOException("Too many Acls, file:"+temp.toString())); // NOI18N -- return ret; -- } -- for(AclEntry e : acl.getAcl()) { -- if (e.principal().equals(acl.getOwner())) { -- ownerEntry = e; -- break; -- } -- } -- if (ownerEntry != null) { -- AclEntry readOnly = AclEntry.newBuilder(ownerEntry).setPermissions(readOnlyAcl).build(); -- acl.setAcl(Collections.singletonList(readOnly)); -- } else { -- deleteTempFile(temp); -- ret.completeExceptionally(new IOException("Owner missing, file:"+temp.toString())); // NOI18N -- return ret; -- } -- } -- temp.toFile().deleteOnExit(); -- result.put("MICRONAUT_CONFIG_FILES", temp.toAbsolutePath().toString()); // NOI18N -- } catch (IOException ex) { -- deleteTempFile(temp); -- ret.completeExceptionally(ex); -- return ret; -- } -- } -- -- ret.complete(result); -- return ret; -- } -- -- @Override -- public Set getCommands() { -- return Collections.singleton(GET_DB_CONNECTION); -- } -- -- private static Path generateDirPath() { -- String s = GET_DB_CONNECTION + "_" + System.getProperty("user.name"); // NOI18N -- Path name = tmpdir.getFileSystem().getPath(s); -- return tmpdir.resolve(name); -- } -- -- private static void deleteOldFiles(Path dir) throws IOException { -- if (Files.isDirectory(dir, LinkOption.NOFOLLOW_LINKS)) { -- try (DirectoryStream stream = Files.newDirectoryStream(dir)) { -- for (Path f : stream) { -- deleteTempFile(f); -- } -- } -- } -- } -- -- private static void deleteTempFile(Path temp) { -- if (temp != null && Files.isRegularFile(temp, LinkOption.NOFOLLOW_LINKS)) { -- try { -- if (POSIX) { -- PosixFileAttributeView attribs = Files.getFileAttributeView(temp, PosixFileAttributeView.class); -- attribs.setPermissions(readWritePosix); -- } else { -- DosFileAttributeView attribs = Files.getFileAttributeView(temp, DosFileAttributeView.class); -- attribs.setReadOnly(false); -- } -- Files.delete(temp); -- } catch (IOException ex) { -- LOG.log(Level.WARNING, "deleteTempFile", ex); -- } -- } -- } --} diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/db/DBDecorationProvider.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/db/DBDecorationProvider.java deleted file mode 100644 index e057526173..0000000000 diff --git a/vscode/package-lock.json b/vscode/package-lock.json index 16437a74..eff871fd 100644 --- a/vscode/package-lock.json +++ b/vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "oracle-java", - "version": "23.0.0", + "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "oracle-java", - "version": "23.0.0", + "version": "0.1.0", "license": "Apache 2.0", "dependencies": { "@vscode/debugadapter": "^1.65.0", diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index 41a5e8c4..0a1b7e4a 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -393,7 +393,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { }); // register commands - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.workspace.new', async (ctx) => { + context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.workspace.new', async (ctx, template) => { let c : LanguageClient = await client; const commands = await vscode.commands.getCommands(); if (commands.includes(COMMAND_PREFIX + '.new.from.template')) { @@ -417,9 +417,14 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { return; } - // first give the context, then the open-file hint in the case the context is not specific enough - const res = await vscode.commands.executeCommand(COMMAND_PREFIX + '.new.from.template', contextUri(ctx)?.toString(), vscode.window.activeTextEditor?.document?.uri?.toString()); - + // first give the template (if present), then the context, and then the open-file hint in the case the context is not specific enough + const params = []; + if (typeof template === 'string') { + params.push(template); + } + params.push(contextUri(ctx)?.toString(), vscode.window.activeTextEditor?.document?.uri?.toString()); + const res = await vscode.commands.executeCommand(COMMAND_PREFIX + '.new.from.template', ...params); + if (typeof res === 'string') { let newFile = vscode.Uri.parse(res as string); await vscode.window.showTextDocument(newFile, { preview: false }); @@ -649,7 +654,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { name: "Java Single Debug", request: "launch" }; - if (!methodName) { + if (methodName) { debugConfig['methodName'] = methodName; } if (launchConfiguration == '') { From 13bbb7b8d8de8aff3803ed6f57ce683b0372e6db Mon Sep 17 00:00:00 2001 From: Achal Talati Date: Thu, 12 Sep 2024 19:09:56 +0530 Subject: [PATCH 15/18] fixed launch configuration issue while running tests --- vscode/src/extension.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index 41a5e8c4..8266e6d9 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -607,13 +607,13 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { })); async function findRunConfiguration(uri : vscode.Uri) : Promise { - // do not invoke debug start with no (java+) configurations, as it would probably create an user prompt + // do not invoke debug start with no (jdk) configurations, as it would probably create an user prompt let cfg = vscode.workspace.getConfiguration("launch"); let c = cfg.get('configurations'); if (!Array.isArray(c)) { return undefined; } - let f = c.filter((v) => v['type'] === 'java+'); + let f = c.filter((v) => v['type'] === 'jdk'); if (!f.length) { return undefined; } @@ -626,10 +626,10 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { } } let provider = new P(); - let d = vscode.debug.registerDebugConfigurationProvider('java+', provider); + let d = vscode.debug.registerDebugConfigurationProvider('jdk', provider); // let vscode to select a debug config return await vscode.commands.executeCommand('workbench.action.debug.start', { config: { - type: 'java+', + type: 'jdk', mainClass: uri.toString() }, noDebug: true}).then((v) => { d.dispose(); From bf4123101b490d7f63cb31fecec5c683b23d5e53 Mon Sep 17 00:00:00 2001 From: Achal Talati Date: Fri, 13 Sep 2024 11:21:15 +0530 Subject: [PATCH 16/18] Doing code cleanup and updated copyright --- NOTICE | 4 +-- build.xml | 4 +-- vscode/src/extension.ts | 71 ++++++++++++++++++++--------------------- 3 files changed, 38 insertions(+), 41 deletions(-) diff --git a/NOTICE b/NOTICE index 87968026..c9a3a108 100644 --- a/NOTICE +++ b/NOTICE @@ -2,10 +2,10 @@ This product includes software developed at The Apache Software Foundation (http://www.apache.org/). Oracle Java Platform Extension for Visual Studio Code -Copyright (c) 2023, Oracle and/or its affiliates. +Copyright (c) 2023-2024, Oracle and/or its affiliates. Apache NetBeans -Copyright 2017-2023 The Apache Software Foundation +Copyright 2017-2024 The Apache Software Foundation The code is based on NetBeans, that has been kindly donated to the Apache Software Foundation by Oracle. diff --git a/build.xml b/build.xml index 8ea4ba05..62cb6429 100644 --- a/build.xml +++ b/build.xml @@ -206,7 +206,7 @@ ${patches} - + @@ -235,7 +235,7 @@ ${reverse.patches} - + diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index 9fb6aba0..9e83f3e9 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -72,6 +72,9 @@ import { JdkDownloaderView } from './jdkDownloader/view'; const API_VERSION : string = "1.0"; const SERVER_NAME : string = "Oracle Java SE Language Server"; +const NB_LANGUAGE_CLIENT_ID: string = "java"; +const LANGUAGE_ID: string = "java"; + export const COMMAND_PREFIX : string = "jdk"; const listeners = new Map(); let client: Promise; @@ -171,15 +174,10 @@ function findJDK(onChange: (path : string | null) => void): void { let nowDark : boolean = isDarkColorTheme(); let nowNbJavacDisabled : boolean = isNbJavacDisabled(); function find(): string | null { - let nbJdk = workspace.getConfiguration('jdk').get('jdkhome'); + let nbJdk = workspace.getConfiguration(COMMAND_PREFIX).get('jdkhome'); if (nbJdk) { return nbJdk as string; } - let javahome = workspace.getConfiguration('java').get('home'); - if (javahome) { - return javahome as string; - } - let jdkHome: any = process.env.JDK_HOME; if (jdkHome) { return jdkHome as string; @@ -199,7 +197,7 @@ function findJDK(onChange: (path : string | null) => void): void { return; } let interested : boolean = false; - if (params.affectsConfiguration('jdk') || params.affectsConfiguration('java')) { + if (params.affectsConfiguration(COMMAND_PREFIX)) { interested = true; } else if (params.affectsConfiguration('workbench.colorTheme')) { let d = isDarkColorTheme(); @@ -215,7 +213,7 @@ function findJDK(onChange: (path : string | null) => void): void { let newJdk = find(); let newD = isDarkColorTheme(); let newNbJavacDisabled = isNbJavacDisabled(); - let newProjectJdk : string | undefined = workspace.getConfiguration('jdk')?.get('project.jdkhome') as string; + let newProjectJdk : string | undefined = workspace.getConfiguration(COMMAND_PREFIX)?.get('project.jdkhome') as string; if (newJdk !== currentJdk || newD != nowDark || newNbJavacDisabled != nowNbJavacDisabled || newProjectJdk != projectJdk) { nowDark = newD; currentJdk = newJdk; @@ -367,23 +365,22 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { //register debugger: let debugTrackerFactory =new NetBeansDebugAdapterTrackerFactory(); - context.subscriptions.push(vscode.debug.registerDebugAdapterTrackerFactory('jdk', debugTrackerFactory)); + context.subscriptions.push(vscode.debug.registerDebugAdapterTrackerFactory(COMMAND_PREFIX, debugTrackerFactory)); let configInitialProvider = new NetBeansConfigurationInitialProvider(); - context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('jdk', configInitialProvider, vscode.DebugConfigurationProviderTriggerKind.Initial)); + context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(COMMAND_PREFIX, configInitialProvider, vscode.DebugConfigurationProviderTriggerKind.Initial)); let configDynamicProvider = new NetBeansConfigurationDynamicProvider(context); - context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('jdk', configDynamicProvider, vscode.DebugConfigurationProviderTriggerKind.Dynamic)); + context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(COMMAND_PREFIX, configDynamicProvider, vscode.DebugConfigurationProviderTriggerKind.Dynamic)); let configResolver = new NetBeansConfigurationResolver(); - context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('jdk', configResolver)); + context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(COMMAND_PREFIX, configResolver)); context.subscriptions.push(vscode.debug.onDidTerminateDebugSession(((session) => onDidTerminateSession(session)))); let debugDescriptionFactory = new NetBeansDebugAdapterDescriptionFactory(); - context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('jdk', debugDescriptionFactory)); + context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory(COMMAND_PREFIX, debugDescriptionFactory)); // initialize Run Configuration initializeRunConfiguration().then(initialized => { if (initialized) { - context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('jdk', runConfigurationProvider)); - context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('java', runConfigurationProvider)); + context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(COMMAND_PREFIX, runConfigurationProvider)); context.subscriptions.push(vscode.window.registerTreeDataProvider('run-config', runConfigurationNodeProvider)); context.subscriptions.push(vscode.commands.registerCommand(COMMAND_PREFIX + '.workspace.configureRunSettings', (...params: any[]) => { configureRunSettings(context, params); @@ -572,7 +569,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { }); })); context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.java.goto.super.implementation', async () => { - if (window.activeTextEditor?.document.languageId !== "java") { + if (window.activeTextEditor?.document.languageId !== LANGUAGE_ID) { return; } const uri = window.activeTextEditor.document.uri; @@ -618,7 +615,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { if (!Array.isArray(c)) { return undefined; } - let f = c.filter((v) => v['type'] === 'jdk'); + let f = c.filter((v) => v['type'] === COMMAND_PREFIX); if (!f.length) { return undefined; } @@ -631,10 +628,10 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { } } let provider = new P(); - let d = vscode.debug.registerDebugConfigurationProvider('jdk', provider); + let d = vscode.debug.registerDebugConfigurationProvider(COMMAND_PREFIX, provider); // let vscode to select a debug config return await vscode.commands.executeCommand('workbench.action.debug.start', { config: { - type: 'jdk', + type: COMMAND_PREFIX, mainClass: uri.toString() }, noDebug: true}).then((v) => { d.dispose(); @@ -650,7 +647,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { if (docUri) { // attempt to find the active configuration in the vsode launch settings; undefined if no config is there. let debugConfig : vscode.DebugConfiguration = await findRunConfiguration(docUri) || { - type: "jdk", + type: COMMAND_PREFIX, name: "Java Single Debug", request: "launch" }; @@ -879,11 +876,11 @@ function isDarkColorTheme() : boolean { } function isNbJavacDisabled() : boolean { - return workspace.getConfiguration('jdk')?.get('advanced.disable.nbjavac') as boolean; + return workspace.getConfiguration(COMMAND_PREFIX)?.get('advanced.disable.nbjavac') as boolean; } function getProjectJDKHome() : string { - return workspace.getConfiguration('jdk')?.get('project.jdkhome') as string; + return workspace.getConfiguration(COMMAND_PREFIX)?.get('project.jdkhome') as string; } function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContext, log : vscode.OutputChannel, notifyKill: boolean, @@ -897,7 +894,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex }, time); }; - const netbeansConfig = workspace.getConfiguration('jdk'); + const netbeansConfig = workspace.getConfiguration(COMMAND_PREFIX); const beVerbose : boolean = netbeansConfig.get('verbose', false); let userdir = process.env['nbcode_userdir'] || netbeansConfig.get('userdir', 'local'); switch (userdir) { @@ -993,7 +990,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex if (isDarkColorTheme()) { extras.push('--laf', 'com.formdev.flatlaf.FlatDarkLaf'); } - let serverVmOptions: string[] = workspace.getConfiguration('jdk').get("serverVmOptions",[]); + let serverVmOptions: string[] = workspace.getConfiguration(COMMAND_PREFIX).get("serverVmOptions",[]); extras.push(...serverVmOptions.map(el => `-J${el}`)); let p = launcher.launch(info, ...extras); handleLog(log, "LSP server launching: " + p.pid); @@ -1087,7 +1084,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex }); const conf = workspace.getConfiguration(); let documentSelectors : DocumentSelector = [ - { language: 'java' }, + { language: LANGUAGE_ID }, { language: 'yaml', pattern: '**/{application,bootstrap}*.yml' }, { language: 'properties', pattern: '**/{application,bootstrap}*.properties' }, { language: 'jackpot-hint' }, @@ -1101,12 +1098,12 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex documentSelector: documentSelectors, synchronize: { configurationSection: [ - 'jdk.hints', - 'jdk.format', - 'jdk.java.imports', - 'jdk.project.jdkhome', - 'jdk.runConfig.vmOptions', - 'jdk.runConfig.cwd' + COMMAND_PREFIX + '.hints', + COMMAND_PREFIX + '.format', + COMMAND_PREFIX + '.java.imports', + COMMAND_PREFIX + '.project.jdkhome', + COMMAND_PREFIX + '.runConfig.vmOptions', + COMMAND_PREFIX + '.runConfig.cwd' ], fileEvents: [ workspace.createFileSystemWatcher('**/*.java') @@ -1143,7 +1140,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex let c = new NbLanguageClient( - 'java', + NB_LANGUAGE_CLIENT_ID, 'Oracle Java SE', connection, log, @@ -1324,7 +1321,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex } ctx.subscriptions.push(window.onDidChangeActiveTextEditor(ed => { - const netbeansConfig = workspace.getConfiguration('jdk'); + const netbeansConfig = workspace.getConfiguration(COMMAND_PREFIX); if (netbeansConfig.get("revealActiveInProjects")) { revealActiveEditor(ed); } @@ -1431,9 +1428,9 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex const settings = l10n.value("jdk.extension.nbjavac.label.openSettings"); window.showErrorMessage(message, enable, settings).then(reply => { if (enable === reply) { - workspace.getConfiguration().update('jdk.advanced.disable.nbjavac', false); + workspace.getConfiguration().update(COMMAND_PREFIX + '.advanced.disable.nbjavac', false); } else if (settings === reply) { - vscode.commands.executeCommand('workbench.action.openSettings', 'jdk.jdkhome'); + vscode.commands.executeCommand('workbench.action.openSettings', COMMAND_PREFIX + '.jdkhome'); } }); } else { @@ -1578,7 +1575,7 @@ class NetBeansConfigurationInitialProvider implements vscode.DebugConfigurationP } const debugConfig : vscode.DebugConfiguration = { name: cname, - type: "jdk", + type: COMMAND_PREFIX, request: "launch", launchConfiguration: cn, }; @@ -1652,7 +1649,7 @@ class NetBeansConfigurationResolver implements vscode.DebugConfigurationProvider resolveDebugConfiguration(_folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration, _token?: vscode.CancellationToken): vscode.ProviderResult { if (!config.type) { - config.type = 'jdk'; + config.type = COMMAND_PREFIX; } if (!config.request) { config.request = 'launch'; From 1d8d2bdb33b7993ee8bc09a082dd4692a0d7211e Mon Sep 17 00:00:00 2001 From: Balyam muralidhar narendra kumar Date: Mon, 16 Sep 2024 21:26:08 +0530 Subject: [PATCH 17/18] [JAVSCODE-253] enabling nb support for localisation [JAVSCODE-253] enabling nb support for localisation --- .github/workflows/main.yml | 7 + BUILD.md | 9 + THIRD_PARTY_LICENSES.txt | 27 +- build.xml | 131 +++++- patches/l10n/adding-ja-and-zh_CN.diff | 560 ++++++++++++++++++++++++++ vscode/l10n/bundle.l10n.ja.json | 32 +- vscode/l10n/bundle.l10n.zh-cn.json | 31 +- vscode/package.nls.ja.json | 3 + vscode/package.nls.zh-cn.json | 3 + vscode/src/extension.ts | 4 +- vscode/src/localiser.ts | 16 +- 11 files changed, 799 insertions(+), 24 deletions(-) create mode 100644 patches/l10n/adding-ja-and-zh_CN.diff diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cfd13cc6..e563e0e6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -64,6 +64,13 @@ jobs: path: netbeans ref: 23-rc3 + - name: Checkout NetBeans l10n + uses: actions/checkout@v4 + with: + repository: apache/netbeans-l10n + path: netbeans-l10n + ref: ece00239dc7a208fba60703c2256ffd818da1646 + - name: Apply NetBeans patches run: ant apply-patches diff --git a/BUILD.md b/BUILD.md index d9d6b772..2c763641 100644 --- a/BUILD.md +++ b/BUILD.md @@ -41,11 +41,20 @@ $ git clone https://github.com/apache/netbeans.git $ cd netbeans/ $ git checkout 23-rc3 $ cd .. +$ git clone https://github.com/apache/netbeans-l10n.git +$ cd netbeans-l10n +$ git checkout ece00239dc7a208fba60703c2256ffd818da1646 # head commit in master +$ cd .. # the following target requires git executable to be on PATH: $ ant apply-patches $ ant build-netbeans + +#Note if you do not wish to have l10n in scope then add no-l10n before any ant invocation target at beginning as below, by default l10n is enabled +$ ant no-l10n apply-patches +$ ant no-l10n build-netbeans ``` + ## Building VS Code extension To build the VS Code extension invoke: diff --git a/THIRD_PARTY_LICENSES.txt b/THIRD_PARTY_LICENSES.txt index 5e3bb7dc..fff53e17 100644 --- a/THIRD_PARTY_LICENSES.txt +++ b/THIRD_PARTY_LICENSES.txt @@ -10527,4 +10527,29 @@ balanced-match brace-expansion -------- License used by Dependencies -MIT License as above \ No newline at end of file +MIT License as above + +------------------ END OF DEPENDENCY LICENSE -------------------- + +Dependency: Apache Netbeans-l10n +================================ + +------------------ START OF DEPENDENCY LICENSE -------------------- +Apache 2.0 License as above + +-------- Copyrights +Apache NetBeans-l10n +Copyright 2017-2024 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +The code is based on NetBeans, that has been kindly donated to the Apache +Software Foundation by Oracle. + +The code was Copyright 1997-2016 Oracle and/or its affiliates. The Initial +Developer of the Original Software was Sun Microsystems, Inc. Portions +Copyright 1997-2006 Sun Microsystems, Inc. + + +------------------ END OF DEPENDENCY LICENSE -------------------- diff --git a/build.xml b/build.xml index 62cb6429..1c18d7ce 100644 --- a/build.xml +++ b/build.xml @@ -31,6 +31,16 @@ + + + patches/l10n/adding-ja-and-zh_CN.diff + + + + + + + patches/6330.diff @@ -60,6 +70,10 @@ + + + + @@ -93,7 +107,7 @@ - + @@ -113,6 +127,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -204,17 +269,38 @@ - - ${patches} + + + ${git-patches-list} - + - + - + + + + + + + + + + + + + + + + + + + + + import java.util.Arrays; @@ -222,28 +308,47 @@ import java.util.List; import java.util.stream.Collectors; public class Reverse { - public static void main(String[] args) { - List<String> patches = Arrays.asList(args[0].split(" ")); - Collections.reverse(patches); - System.out.print(patches.stream().collect(Collectors.joining(" "))); - } + public static void main(String[] args) { + List<String> patches = Arrays.asList(args[0].split(" ")); + Collections.reverse(patches); + System.out.print(patches.stream().collect(Collectors.joining(" "))); + } } - + ${reverse.patches} - + + + + + + + + + + + + + + + + + + + + diff --git a/patches/l10n/adding-ja-and-zh_CN.diff b/patches/l10n/adding-ja-and-zh_CN.diff new file mode 100644 index 00000000..f6356030 --- /dev/null +++ b/patches/l10n/adding-ja-and-zh_CN.diff @@ -0,0 +1,560 @@ +diff --git a/.gitignore b/.gitignore +index 6f0f29522..f19cedae2 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -9,3 +9,4 @@ + /l10nantext/dist/ + /l10nantext/nbproject/private/ + /l10nantext/nbproject/genfiles.properties ++.idea +diff --git a/locale_ja/build.xml b/locale_ja/build.xml +new file mode 100644 +index 000000000..9fb26158d +--- /dev/null ++++ b/locale_ja/build.xml +@@ -0,0 +1,57 @@ ++ ++ ++ ++ Builds, tests, and runs the project org.apache.netbeans.l10n.ja. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/locale_ja/manifest.mf b/locale_ja/manifest.mf +new file mode 100644 +index 000000000..3eaceaebe +--- /dev/null ++++ b/locale_ja/manifest.mf +@@ -0,0 +1,6 @@ ++Manifest-Version: 1.0 ++AutoUpdate-Show-In-Client: true ++OpenIDE-Module: org.apache.netbeans.l10n.ja ++OpenIDE-Module-Localizing-Bundle: org/apache/netbeans/l10n/ja/Bundle.properties ++OpenIDE-Module-Specification-Version: 0.0.1 ++ +diff --git a/locale_ja/nbproject/build-impl.xml b/locale_ja/nbproject/build-impl.xml +new file mode 100644 +index 000000000..8e084c16d +--- /dev/null ++++ b/locale_ja/nbproject/build-impl.xml +@@ -0,0 +1,41 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/locale_ja/nbproject/platform.properties b/locale_ja/nbproject/platform.properties +new file mode 100644 +index 000000000..eb5ae3bf6 +--- /dev/null ++++ b/locale_ja/nbproject/platform.properties +@@ -0,0 +1,25 @@ ++# 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. ++cluster.path=\ ++ ${nbplatform.active.dir}/extide:\ ++ ${nbplatform.active.dir}/ide:\ ++ ${nbplatform.active.dir}/java:\ ++ ${nbplatform.active.dir}/nb:\ ++ ${nbplatform.active.dir}/platform:\ ++ ${nbplatform.active.dir}/harness:\ ++ ${nbplatform.active.dir}/webcommon ++nbplatform.active=default +\ No newline at end of file +diff --git a/locale_ja/nbproject/project.properties b/locale_ja/nbproject/project.properties +new file mode 100644 +index 000000000..4cdfb33e0 +--- /dev/null ++++ b/locale_ja/nbproject/project.properties +@@ -0,0 +1,18 @@ ++# 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. ++javac.source=1.8 ++javac.compilerargs=-Xlint -Xlint:-serial +\ No newline at end of file +diff --git a/locale_ja/nbproject/project.xml b/locale_ja/nbproject/project.xml +new file mode 100644 +index 000000000..4e1eb911f +--- /dev/null ++++ b/locale_ja/nbproject/project.xml +@@ -0,0 +1,32 @@ ++ ++ ++ ++ org.netbeans.modules.apisupport.project ++ ++ ++ org.apache.netbeans.l10n.ja ++ ++ ++ ++ ++ ++ +diff --git a/locale_ja/src/org/apache/netbeans/l10n/ja/Bundle.properties b/locale_ja/src/org/apache/netbeans/l10n/ja/Bundle.properties +new file mode 100644 +index 000000000..b18b59a1c +--- /dev/null ++++ b/locale_ja/src/org/apache/netbeans/l10n/ja/Bundle.properties +@@ -0,0 +1,17 @@ ++# 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. ++OpenIDE-Module-Name=Japanese +diff --git a/locale_zh_CN/build.xml b/locale_zh_CN/build.xml +new file mode 100644 +index 000000000..3b5bec0e7 +--- /dev/null ++++ b/locale_zh_CN/build.xml +@@ -0,0 +1,57 @@ ++ ++ ++ ++ Builds, tests, and runs the project org.apache.netbeans.l10n.zh_CN. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/locale_zh_CN/manifest.mf b/locale_zh_CN/manifest.mf +new file mode 100644 +index 000000000..e705996ba +--- /dev/null ++++ b/locale_zh_CN/manifest.mf +@@ -0,0 +1,6 @@ ++Manifest-Version: 1.0 ++AutoUpdate-Show-In-Client: true ++OpenIDE-Module: org.apache.netbeans.l10n.zh_CN ++OpenIDE-Module-Localizing-Bundle: org/apache/netbeans/l10n/zh_CN/Bundle.properties ++OpenIDE-Module-Specification-Version: 0.0.1 ++ +diff --git a/locale_zh_CN/nbproject/build-impl.xml b/locale_zh_CN/nbproject/build-impl.xml +new file mode 100644 +index 000000000..dc542d5bf +--- /dev/null ++++ b/locale_zh_CN/nbproject/build-impl.xml +@@ -0,0 +1,41 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/locale_zh_CN/nbproject/platform.properties b/locale_zh_CN/nbproject/platform.properties +new file mode 100644 +index 000000000..eb5ae3bf6 +--- /dev/null ++++ b/locale_zh_CN/nbproject/platform.properties +@@ -0,0 +1,25 @@ ++# 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. ++cluster.path=\ ++ ${nbplatform.active.dir}/extide:\ ++ ${nbplatform.active.dir}/ide:\ ++ ${nbplatform.active.dir}/java:\ ++ ${nbplatform.active.dir}/nb:\ ++ ${nbplatform.active.dir}/platform:\ ++ ${nbplatform.active.dir}/harness:\ ++ ${nbplatform.active.dir}/webcommon ++nbplatform.active=default +\ No newline at end of file +diff --git a/locale_zh_CN/nbproject/project.properties b/locale_zh_CN/nbproject/project.properties +new file mode 100644 +index 000000000..4cdfb33e0 +--- /dev/null ++++ b/locale_zh_CN/nbproject/project.properties +@@ -0,0 +1,18 @@ ++# 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. ++javac.source=1.8 ++javac.compilerargs=-Xlint -Xlint:-serial +\ No newline at end of file +diff --git a/locale_zh_CN/nbproject/project.xml b/locale_zh_CN/nbproject/project.xml +new file mode 100644 +index 000000000..eec7b3954 +--- /dev/null ++++ b/locale_zh_CN/nbproject/project.xml +@@ -0,0 +1,32 @@ ++ ++ ++ ++ org.netbeans.modules.apisupport.project ++ ++ ++ org.apache.netbeans.l10n.zh_CN ++ ++ ++ ++ ++ ++ +diff --git a/locale_zh_CN/src/org/apache/netbeans/l10n/zh_CN/Bundle.properties b/locale_zh_CN/src/org/apache/netbeans/l10n/zh_CN/Bundle.properties +new file mode 100644 +index 000000000..744e7e91e +--- /dev/null ++++ b/locale_zh_CN/src/org/apache/netbeans/l10n/zh_CN/Bundle.properties +@@ -0,0 +1,17 @@ ++# 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. ++OpenIDE-Module-Name=Simplified Chinese +diff --git a/netbeans-l10n-zip/src/ja/java/java-lsp-server/java-lsp-server/org/netbeans/modules/java/lsp/server/protocol/Bundle_ja.properties b/netbeans-l10n-zip/src/ja/java/java-lsp-server/java-lsp-server/org/netbeans/modules/java/lsp/server/protocol/Bundle_ja.properties +new file mode 100755 +index 000000000..88b6b4608 +--- /dev/null ++++ b/netbeans-l10n-zip/src/ja/java/java-lsp-server/java-lsp-server/org/netbeans/modules/java/lsp/server/protocol/Bundle_ja.properties +@@ -0,0 +1,29 @@ ++# 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. ++OpenIDE-Module-Name=Java LSP Server ++OpenIDE-Module-Display-Category=Java ++OpenIDE-Module-Short-Description=Java LSP Server ++ ++LBL_Run={0} \u3092\u5B9F\u884C ++LBL_Debug={0} \u3092\u30C7\u30D0\u30C3\u30B0 ++LBL_RunWith={0} \u3092\u6307\u5B9A\u3057\u3066 {1} \u3092\u5B9F\u884C ++LBL_DebugWith={0} \u3092\u6307\u5B9A\u3057\u3066 {1} \u3092\u30C7\u30D0\u30C3\u30B0 ++LBL_TestMethod={0} \u3092\u30C6\u30B9\u30C8 ++LBL_ProfileMethod={0} \u3092\u30D7\u30ED\u30D5\u30A1\u30A4\u30EA\u30F3\u30B0 ++LBL_Clean=\u6D88\u53BB ++LBL_Build=\u4F5C\u6210 ++LBL_ContinuousMode=\u9023\u7D9A\u30E2\u30FC\u30C9 +diff --git a/netbeans-l10n-zip/src/zh_CN/java/java-lsp-server/java-lsp-server/org/netbeans/modules/java/lsp/server/protocol/Bundle_zh_CN.properties b/netbeans-l10n-zip/src/zh_CN/java/java-lsp-server/java-lsp-server/org/netbeans/modules/java/lsp/server/protocol/Bundle_zh_CN.properties +new file mode 100755 +index 000000000..97a321575 +--- /dev/null ++++ b/netbeans-l10n-zip/src/zh_CN/java/java-lsp-server/java-lsp-server/org/netbeans/modules/java/lsp/server/protocol/Bundle_zh_CN.properties +@@ -0,0 +1,29 @@ ++# 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. ++OpenIDE-Module-Name=Java LSP Server ++OpenIDE-Module-Display-Category=Java ++OpenIDE-Module-Short-Description=Java LSP Server ++ ++LBL_Run=\u8FD0\u884C {0} ++LBL_Debug=\u8C03\u8BD5 {0}, ++LBL_RunWith=\u4F7F\u7528 {0} \u8FD0\u884C {1} ++LBL_DebugWith=\u4F7F\u7528 {0} \u8C03\u8BD5 {1} ++LBL_TestMethod=\u6D4B\u8BD5 {0} ++LBL_ProfileMethod=\u6982\u8981\u5206\u6790 {0} ++LBL_Clean=\u6E05\u9664 ++LBL_Build=\u6784\u5EFA ++LBL_ContinuousMode=\u8FDE\u7EED\u6A21\u5F0F +\ No newline at end of file diff --git a/vscode/l10n/bundle.l10n.ja.json b/vscode/l10n/bundle.l10n.ja.json index 1291b3e0..77ff7104 100755 --- a/vscode/l10n/bundle.l10n.ja.json +++ b/vscode/l10n/bundle.l10n.ja.json @@ -1,9 +1,7 @@ { "jdk.downloader.heading": "JDKダウンローダ", - "jdk.downloader.html.details":"

このツールは、Oracle No-Fee Terms and Conditionsの最新のOracle Java SE JDKまたは、クラスパス例外付きGNU Public Licenseに基づいたOracle OpenJDKビルドのいずれかをダウンロードできます

次に、インストールおよび構成をかわりに処理します。

これにより、この拡張によって提供されたすべての機能を最大限活用できます。

", - "jdk.downloader.button.label.oracleJdk": "Oracle Java SE JDKのダウンロード", "jdk.downloader.label.or": "または", "jdk.downloader.button.label.openJdk": "Oracle OpenJDKのダウンロード", @@ -16,12 +14,20 @@ "jdk.downloader.label.detectedMachineArchitecture": "検出済マシン・アーキテクチャ", "jdk.downloader.button.label.downloadAndInstall": "インストールおよび設定の開始", "jdk.downloader.label.selectOpenJdkVersion": "Oracle OpenJDKバージョンの選択", + "jdk.downloader.message.downloadProgressBar":"{jdkType} {jdkVersion}のダウンロード中", "jdk.downloader.message.downloadingAndCompletingSetup": "{jdkType} {jdkVersion}の設定をダウンロードおよび完了しています...", - "jdk.downloader.error_message.whileSavingFile": "ファイルの保存中にエラーが発生しました:{error}", + "jdk.downloader.message.downloadCompleted":"{osType}用の{jdkType} {jdkVersion}のダウンロードが完了しました。", + "jdk.downloader.error_message.whileSavingFile": "ファイルの保存中にエラーが発生しました: {error}", "jdk.downloader.error_message.whileDownloading": "ダウンロード中にエラーが発生しました: {jdkType} {error}", "jdk.downloader.message.downloadFailed": "{osType}用の{jdkType} {jdkVersion}のダウンロードに失敗しました。間違ったチェックサム。", "jdk.downloader.error_message.downloadFailedHttpError": "HTTPエラー{statusCode} - {statusMessage}", "jdk.downloader.error_message.anyError": "エラー: {error}", + "jdk.downloader.error_message.installationCleanup": "インストールのクリーンアップ中にエラーが発生しました", + "jdk.downloader.error_message.extractionError": "{jdkType} {jdkVersion}の抽出に失敗しました", + "jdk.downloader.error_message.findDownloadedJDK": "ダウンロード・ディレクトリに{jdkVersion}のインスタンスがありません", + "jdk.downloader.error_message.installingJDK": "JDKのインストール中にエラーが発生しました: {error}", + "jdk.downloader.error_message.generateDownloadUrl": "ダウンロードURLまたはパスの生成に失敗しました。", + "jdk.downloader.error_message.errorLoadingPage": "JDKダウンローダ・ページのロード中にエラーが発生しました。", "jdk.downloader.error_message.jdkNewDirectoryIssueCannotInstall":"{jdkType} {jdkVersion}をインストールできません。{newDirName}を削除できません", "jdk.downloader.message.confirmation.directoryExistsStillWantToDelete":"{name}はすでに存在しています。削除して新しいコンテンツを作成しますか。", "jdk.downloader.message.confirmation.yes":"はい", @@ -65,6 +71,8 @@ "jdk.extension.javaSupport.message.needAdditionalSupport": "追加のJavaサポートが必要です", "jdk.extension.runConfig.label.updateExistingLaunchJson": "既存のlaunch.jsonファイルの更新", "jdk.extension.runConfig.warning_message.renamedDebugConfig": "Java 8+デバッグ構成はJava+に名前変更されています", + "jdk.extension.runConfig.default.label":"", + "jdk.extension.runConfig.example.label":"例: {data}", "jdk.extension.runConfig.arguments.label": "引数:", "jdk.extension.runConfig.arguments.prompt": "引数のカスタマイズ", "jdk.extension.runConfig.vmoptions.label": "VMオプション:", @@ -72,5 +80,19 @@ "jdk.extension.runConfig.env.label": "環境:", "jdk.extension.runConfig.env.prompt": "環境変数のカスタマイズ", "jdk.extension.runConfig.wrkdir.label": "作業ディレクトリ:", - "jdk.extension.runConfig.wrkdir.prompt": "作業ディレクトリのカスタマイズ" - } \ No newline at end of file + "jdk.extension.runConfig.wrkdir.prompt": "作業ディレクトリのカスタマイズ", + "jdk.extenstion.notInstalled.label":"拡張機能がインストールされませんでした。", + "jdk.extenstion.error_msg.clientNotAvailable":"クライアントを使用できません", + "jdk.extenstion.progressBar.error_msg.cannotRun":"{lsCommand}を実行できません。クライアントは{client}です", + "jdk.extenstion.error_msg.doesntSupportNewTeamplate":"クライアント{client}では、「テンプレートから新規作成」はサポートされていません", + "jdk.extenstion.error_msg.doesntSupportNewProject":"クライアント{client}では、新規プロジェクトはサポートされていません", + "jdk.extenstion.error_msg.doesntSupportGoToTest":"クライアント{client}では、「テストへ移動」はサポートされていません", + "jdk.extenstion.error_msg.noSuperImpl":"スーパークラスの実装が見つかりません", + "jdk.extenstion.error_msg.cacheDeletionError":"キャッシュの削除中にエラーが発生しました", + "jdk.extenstion.message.cacheDeleted":"キャッシュが正常に削除されました", + "jdk.extenstion.cache.error_msg.cannotFindWrkSpacePath":"ワークスペース・パスが見つかりません", + "jdk.extenstion.debugger.error_msg.debugAdapterNotInitialized":"Oracle Java SEのデバッグ・サーバー・アダプタが、まだ初期化されていません。しばらく待ってから再試行してください。", + "jdk.workspace.new.prompt": "新しいファイルを生成するディレクトリのパスを入力してください", + "jdk.extension.utils.error_message.failedHttpsRequest": "{url}の取得に失敗しました({statusCode})", + "jdk.extension.error_msg.notEnabled": "{SERVER_NAME}が有効化されていません" +} \ No newline at end of file diff --git a/vscode/l10n/bundle.l10n.zh-cn.json b/vscode/l10n/bundle.l10n.zh-cn.json index 3dd07fc2..998ad937 100755 --- a/vscode/l10n/bundle.l10n.zh-cn.json +++ b/vscode/l10n/bundle.l10n.zh-cn.json @@ -1,4 +1,5 @@ { + "jdk.downloader.heading": "JDK 下载程序", "jdk.downloader.html.details":"

使用此工具,您可以遵循 Oracle 免费条款和条件下载最新的 Oracle Java SE JDK,或者依照 GNU 公共许可证(包含 ClassPath 例外条款)下载 Oracle OpenJDK 工作版本

之后,它将代表您处理安装和配置。

这样,您可以充分利用此扩展提供的所有功能。

", "jdk.downloader.button.label.oracleJdk": "下载 Oracle Java SE JDK", @@ -13,12 +14,20 @@ "jdk.downloader.label.detectedMachineArchitecture": "检测到的计算机体系结构", "jdk.downloader.button.label.downloadAndInstall": "安装并启动设置", "jdk.downloader.label.selectOpenJdkVersion": "选择 Oracle OpenJDK 版本", + "jdk.downloader.message.downloadProgressBar":"{jdkType} {jdkVersion} 正在下载", "jdk.downloader.message.downloadingAndCompletingSetup": "正在下载 {jdkType} {jdkVersion} 并完成其设置...", + "jdk.downloader.message.downloadCompleted":"适用于 {osType} 的 {jdkType} {jdkVersion} 下载已完成!", "jdk.downloader.error_message.whileSavingFile": "保存文件时出错:{error}", - "jdk.downloader.error_message.whileDownloading": "下载 {jdkType} 时出错 {error}", + "jdk.downloader.error_message.whileDownloading": "下载 {jdkType} 时出错:{error}", "jdk.downloader.message.downloadFailed": "适用于 {osType} 的 {jdkType} {jdkVersion} 下载失败,校验和错误。", "jdk.downloader.error_message.downloadFailedHttpError": "HTTP 错误 {statusCode} - {statusMessage}", "jdk.downloader.error_message.anyError": "错误:{error}", + "jdk.downloader.error_message.installationCleanup": "清除安装时出错", + "jdk.downloader.error_message.extractionError": "提取 {jdkType} {jdkVersion} 失败", + "jdk.downloader.error_message.findDownloadedJDK": "下载目录中未找到 {jdkVersion} 的实例", + "jdk.downloader.error_message.installingJDK": "安装 JDK 时出错:{error}", + "jdk.downloader.error_message.generateDownloadUrl": "无法生成下载 URL 或路径。", + "jdk.downloader.error_message.errorLoadingPage": "加载 JDK 下载程序页面时出错。", "jdk.downloader.error_message.jdkNewDirectoryIssueCannotInstall":"无法安装 {jdkType} {jdkVersion}。无法删除 {newDirName}", "jdk.downloader.message.confirmation.directoryExistsStillWantToDelete":"{name} 已存在。是否要将其删除并使用新内容创建?", "jdk.downloader.message.confirmation.yes":"是", @@ -62,6 +71,8 @@ "jdk.extension.javaSupport.message.needAdditionalSupport": "需要其他 Java 支持", "jdk.extension.runConfig.label.updateExistingLaunchJson": "更新现有 launch.json 文件", "jdk.extension.runConfig.warning_message.renamedDebugConfig": "Java 8+ 调试配置已重命名为 Java+", + "jdk.extension.runConfig.default.label":"", + "jdk.extension.runConfig.example.label":"示例: {data}", "jdk.extension.runConfig.arguments.label": "参数:", "jdk.extension.runConfig.arguments.prompt": "定制参数", "jdk.extension.runConfig.vmoptions.label": "VM 选项:", @@ -69,5 +80,19 @@ "jdk.extension.runConfig.env.label": "环境:", "jdk.extension.runConfig.env.prompt": "定制环境变量", "jdk.extension.runConfig.wrkdir.label": "工作目录:", - "jdk.extension.runConfig.wrkdir.prompt": "定制工作目录" - } \ No newline at end of file + "jdk.extension.runConfig.wrkdir.prompt": "定制工作目录", + "jdk.extenstion.notInstalled.label":"未安装扩展。", + "jdk.extenstion.error_msg.clientNotAvailable":"客户端不可用", + "jdk.extenstion.progressBar.error_msg.cannotRun":"无法运行 {lsCommand};客户端为 {client}", + "jdk.extenstion.error_msg.doesntSupportNewTeamplate":"客户端 {client} 不支持从模板新建", + "jdk.extenstion.error_msg.doesntSupportNewProject":"客户端 {client} 不支持新项目", + "jdk.extenstion.error_msg.doesntSupportGoToTest":"客户端 {client} 不支持转至测试", + "jdk.extenstion.error_msg.noSuperImpl":"未找到超级实现", + "jdk.extenstion.error_msg.cacheDeletionError":"删除高速缓存时出错", + "jdk.extenstion.message.cacheDeleted":"已成功删除高速缓存", + "jdk.extenstion.cache.error_msg.cannotFindWrkSpacePath":"找不到工作区路径", + "jdk.extenstion.debugger.error_msg.debugAdapterNotInitialized":"Oracle Java SE 调试服务器适配器尚未初始化。请稍候,然后重试。", + "jdk.workspace.new.prompt": "输入生成新文件的目录路径", + "jdk.extension.utils.error_message.failedHttpsRequest": "无法获取 {url} ({statusCode})", + "jdk.extension.error_msg.notEnabled": "{SERVER_NAME} 未启用" +} \ No newline at end of file diff --git a/vscode/package.nls.ja.json b/vscode/package.nls.ja.json index 4a2ff124..53ed9bb7 100755 --- a/vscode/package.nls.ja.json +++ b/vscode/package.nls.ja.json @@ -1,6 +1,7 @@ { "jdk.node.properties.edit": "プロパティ", "jdk.views.run.config": "実行構成", + "jdk.views.explorer.projects": "プロジェクト", "jdk.workspace.compile": "ワークスペースのコンパイル", "jdk.workspace.clean": "ワークスペースの消去", "jdk.workspace.new": "テンプレートから新規作成...", @@ -24,6 +25,7 @@ "jdk.open.test": "テスト/テスト済のクラスへ移動...", "jdk.delete.cache": "このワークスペースのOracle Java拡張キャッシュの削除", "jdk.configuration.specifyJdk.description": "Oracle Visual Studio Code拡張機能のJDKを指定します", + "jdk.configuration.specifyProjectJdk.description": "ユーザーのプロジェクトが実行されるJDKを指定します。値はデフォルトでjdk.jdkhomeに設定されます", "jdk.configuration.verbose.description": "Oracle Visual Studio Code拡張機能からの詳細メッセージを有効化します", "jdk.configuration.userdir.description": "設定およびキャッシュをワークスペースごとにグローバルまたはローカルとして保持しますか。", "jdk.configuration.userdir.description.shareData": "すべてのワークスペース間でデータを共有します(より効果的)", @@ -44,6 +46,7 @@ "jdk.configuration.runConfig.env.description": "環境変数", "jdk.configuration.runConfig.cwd.description": "作業ディレクトリ", "jdk.configuration.disableNbJavac.description": "拡張オプション: nb-javacライブラリを無効化すると、選択したJDKからのjavacが使用されます。選択したJDKは少なくともJDK 22である必要があります。", + "jdk.configuration.disableProjectSearchLimit.description": "拡張オプション: プロジェクト情報が含まれているフォルダの検索に対する制限を無効化します。", "jdk.debugger.configuration.mainClass.description": "プログラムのメイン・クラスへの絶対パス。", "jdk.debugger.configuration.classPaths.description": "JVMの起動のためのクラスパス。", "jdk.debugger.configuration.console.description": "プログラムを起動する指定されたコンソール。", diff --git a/vscode/package.nls.zh-cn.json b/vscode/package.nls.zh-cn.json index 8489277f..c5e7fffa 100755 --- a/vscode/package.nls.zh-cn.json +++ b/vscode/package.nls.zh-cn.json @@ -1,6 +1,7 @@ { "jdk.node.properties.edit": "属性", "jdk.views.run.config": "运行配置", + "jdk.views.explorer.projects": "项目", "jdk.workspace.compile": "编译工作区", "jdk.workspace.clean": "清除工作区", "jdk.workspace.new": "从模板新建...", @@ -24,6 +25,7 @@ "jdk.open.test": "转至测试/测试的类...", "jdk.delete.cache": "删除此工作区的 Oracle Java 扩展高速缓存", "jdk.configuration.specifyJdk.description": "指定适用于 Oracle Visual Studio Code 扩展的 JDK", + "jdk.configuration.specifyProjectJdk.description": "指定将在其上运行用户项目的 JDK。默认值为 jdk.jdkhome", "jdk.configuration.verbose.description": "启用来自 Oracle Visual Studio Code 扩展的详细消息", "jdk.configuration.userdir.description": "将设置和高速缓存保留为“全局”还是按工作区保留为“本地”?", "jdk.configuration.userdir.description.shareData": "在所有工作区之间共享数据(更有效)", @@ -44,6 +46,7 @@ "jdk.configuration.runConfig.env.description": "环境变量", "jdk.configuration.runConfig.cwd.description": "工作目录", "jdk.configuration.disableNbJavac.description": "高级选项:禁用 nb-javac 库,将使用来自所选 JDK 的 javac。所选 JDK 必须至少为 JDK 22。", + "jdk.configuration.disableProjectSearchLimit.description": "高级选项:禁用在包含项目信息的文件夹中搜索的限制。", "jdk.debugger.configuration.mainClass.description": "程序主类的绝对路径。", "jdk.debugger.configuration.classPaths.description": "用于启动 JVM 的类路径。", "jdk.debugger.configuration.console.description": "用于启动程序的指定控制台。", diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index 9e83f3e9..8213ca7a 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -330,6 +330,7 @@ class InitialPromise extends Promise { } export function activate(context: ExtensionContext): VSNetBeansAPI { + deactivated=false; let log = vscode.window.createOutputChannel(SERVER_NAME); var clientResolve : (x : NbLanguageClient) => void; @@ -546,6 +547,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { vscode.window.showErrorMessage(l10n.value("jdk.extension.cache.message.noUserDir")); } })); + context.subscriptions.push(vscode.commands.registerCommand(COMMAND_PREFIX + ".download.jdk", async () => { const jdkDownloaderView = new JdkDownloaderView(log); @@ -986,7 +988,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex stdOut = null; } } - let extras : string[] = ["--modules", "--list", "-J-XX:PerfMaxStringConstLength=10240"]; + let extras : string[] = ["--modules", "--list", "-J-XX:PerfMaxStringConstLength=10240","--locale",l10n.nbLocaleCode()]; if (isDarkColorTheme()) { extras.push('--laf', 'com.formdev.flatlaf.FlatDarkLaf'); } diff --git a/vscode/src/localiser.ts b/vscode/src/localiser.ts index 6dd26ae8..69a2f68c 100644 --- a/vscode/src/localiser.ts +++ b/vscode/src/localiser.ts @@ -6,16 +6,20 @@ import * as l10nLib from '@vscode/l10n' import * as vscode from 'vscode'; import { ORACLE_VSCODE_EXTENSION_ID } from './constants'; -const DEFAULT_BUNDLE_FILE = 'l10n/bundle.l10n.en.json'; +const DEFAULT_LANGAUGE = "en"; +const DEFAULT_BUNDLE_FILE = `l10n/bundle.l10n.${DEFAULT_LANGAUGE}.json`; type TranslatorFn = typeof vscode.l10n.t export interface l10n { value(key: string, placeholderMap?: Record): string + nbLocaleCode():string } + class l10Wrapper implements l10n { private defaultTranslation: TranslatorFn; + constructor(extensionId: string, defaultBundlePath: string) { let defaultBundleAbsoluteFsPath = vscode.Uri.file(`${vscode.extensions.getExtension(extensionId)?.extensionPath}/${defaultBundlePath}`).fsPath l10nLib.config({ @@ -29,6 +33,16 @@ class l10Wrapper implements l10n { const isPresentInBundle = valueFromBundle !== key; return isPresentInBundle ? valueFromBundle : this.defaultTranslation(key, placeholderMap); } + nbLocaleCode(){ + const vscodeLanguage = vscode.env.language; + if (!vscodeLanguage) return DEFAULT_LANGAUGE; + const localeParts = vscodeLanguage.split(/[-_]/g); + if (localeParts.length > 1) { + localeParts[1] = localeParts[1].toUpperCase(); + } + var nbFormatLocale = localeParts.join(":"); + return nbFormatLocale; + } } From f474f97210fbe18e3a46eab9273c648685f026d8 Mon Sep 17 00:00:00 2001 From: Achal Talati Date: Tue, 17 Sep 2024 20:37:53 +0530 Subject: [PATCH 18/18] JDK 23 download option added in JDK Downloader --- vscode/src/constants.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vscode/src/constants.ts b/vscode/src/constants.ts index e1778d5f..19a1a7f0 100644 --- a/vscode/src/constants.ts +++ b/vscode/src/constants.ts @@ -19,10 +19,10 @@ export const JDK_RELEASES_TRACK_URL = `https://www.java.com/releases/releases.js export const ORACLE_JDK_BASE_DOWNLOAD_URL = `https://download.oracle.com/java`; -export const ORACLE_JDK_DOWNLOAD_VERSIONS = ['22','21']; +export const ORACLE_JDK_DOWNLOAD_VERSIONS = ['23','21']; export const OPEN_JDK_VERSION_DOWNLOAD_LINKS: { [key: string]: string } = { - "22": "https://download.java.net/java/GA/jdk22.0.2/c9ecb94cd31b495da20a27d4581645e8/9/GPL/openjdk-22.0.2" + "23": "https://download.java.net/java/GA/jdk23/3c5b90190c68498b986a97f276efd28a/37/GPL/openjdk-23" }; export const ORACLE_VSCODE_EXTENSION_ID = 'oracle.oracle-java';

This tool enables you to download either the latest Oracle Java SE JDK with Oracle No-Fee Terms and Conditions or the Oracle OpenJDK builds under the GNU Public License with ClassPath Exception