Merge pull request #2272 from pepness/jakartaee8-javadoc
[NETBEANS-4635] - Add JakartaEE 8 Platform module
diff --git a/README.md b/README.md
index f359efa..e95338f 100644
--- a/README.md
+++ b/README.md
@@ -29,8 +29,8 @@
* TravisCI:
* [![Build Status](https://travis-ci.org/apache/netbeans.svg?branch=master)](https://travis-ci.org/apache/netbeans)
* Apache Jenkins:
- * Linux: [![Build Status](https://builds.apache.org/view/M-R/view/NetBeans/job/netbeans-linux/badge/icon)](https://builds.apache.org/view/M-R/view/NetBeans/job/netbeans-linux/)
- * Windows: [![Build Status](https://builds.apache.org/view/M-R/view/NetBeans/job/netbeans-windows/badge/icon)](https://builds.apache.org/view/M-R/view/NetBeans/job/netbeans-windows/)
+ * Linux: [![Build Status](https://ci-builds.apache.org/job/Netbeans/job/netbeans-linux/badge/icon)](https://ci-builds.apache.org/job/Netbeans/job/netbeans-linux/)
+ * Windows: [![Build Status](https://ci-builds.apache.org/job/Netbeans/job/netbeans-windows/badge/icon)](https://ci-builds.apache.org/job/Netbeans/job/netbeans-windows)
### Requirements
diff --git a/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/UnconfiguredHint.java b/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/UnconfiguredHint.java
index 76b1911..59d9f77 100644
--- a/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/UnconfiguredHint.java
+++ b/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/UnconfiguredHint.java
@@ -87,12 +87,14 @@
return ;
}
List<ErrorDescription> errors = new ArrayList<>();
- String ccls = Utils.settings().get(Utils.KEY_CCLS_PATH, null);
- if (ccls == null || !new File(ccls).canExecute() || !new File(ccls).isFile()) {
- errors.add(ErrorDescriptionFactory.createErrorDescription(Severity.WARNING, "ccls not configured!", Collections.singletonList(new ConfigureCCLS()), doc, 0));
+ String ccls = Utils.getCCLSPath();
+ String clangd = Utils.getCLANGDPath();
+ if ((ccls == null || !new File(ccls).canExecute() || !new File(ccls).isFile()) &&
+ (clangd == null || !new File(clangd).canExecute() || !new File(clangd).isFile())) {
+ errors.add(ErrorDescriptionFactory.createErrorDescription(Severity.WARNING, "Neither ccls nor clangd configured!", Collections.singletonList(new ConfigureCCLS()), doc, 0));
} else {
Project prj = FileOwnerQuery.getOwner(file);
- if (LanguageServerImpl.getProjectSettings(prj) == null) {
+ if (prj != null && LanguageServerImpl.getCompileCommandsDir(prj) == null) {
errors.add(ErrorDescriptionFactory.createErrorDescription(Severity.WARNING, "compile commands not configured", doc, 0));
}
}
diff --git a/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/Utils.java b/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/Utils.java
index 0b3199d..a617786 100644
--- a/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/Utils.java
+++ b/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/Utils.java
@@ -18,8 +18,19 @@
*/
package org.netbeans.modules.cpplite.editor;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import java.util.prefs.Preferences;
+import org.openide.filesystems.FileUtil;
import org.openide.util.NbPreferences;
+import org.openide.util.Parameters;
/**
*
@@ -27,8 +38,91 @@
*/
public class Utils {
public static final String KEY_CCLS_PATH = "ccls";
+ public static final String KEY_CLANGD_PATH = "clangd";
+
+ private static final String[] CCLS_NAMES = new String[] {"ccls"};
+ private static final String[] CLANGD_NAMES = new String[] {"clangd-10", "clangd", "clangd-9"};
public static Preferences settings() {
return NbPreferences.forModule(Utils.class);
}
+
+ private static List<String> cclsAutodetectedPaths;
+
+ public static synchronized String getCCLSPath() {
+ String path = settings().get(KEY_CCLS_PATH, null);
+ if (path == null || path.isEmpty()) {
+ if (cclsAutodetectedPaths == null) {
+ cclsAutodetectedPaths = findFileOnUsersPath(CCLS_NAMES);
+ }
+ if (!cclsAutodetectedPaths.isEmpty()) {
+ path = cclsAutodetectedPaths.get(0);
+ }
+ }
+ if (path == null || path.isEmpty()) {
+ return null;
+ }
+ return path;
+ }
+
+ private static List<String> clangdAutodetectedPaths;
+
+ public static synchronized String getCLANGDPath() {
+ String path = settings().get(KEY_CLANGD_PATH, null);
+ if (path == null || path.isEmpty()) {
+ if (clangdAutodetectedPaths == null) {
+ clangdAutodetectedPaths = findFileOnUsersPath(CLANGD_NAMES);
+ }
+ if (!clangdAutodetectedPaths.isEmpty()) {
+ path = clangdAutodetectedPaths.get(0);
+ }
+ }
+ if (path == null || path.isEmpty()) {
+ return null;
+ }
+ return path;
+ }
+
+ //TODO: copied from webcommon/javascript.nodejs/src/org/netbeans/modules/javascript/nodejs/util/FileUtils.java:
+ /**
+ * Find all the files (absolute path) with the given "filename" on user's PATH.
+ * <p>
+ * This method is suitable for *nix as well as windows.
+ * @param filenames the name of a file to find, more names can be provided.
+ * @return list of absolute paths of found files (order preserved according to input names).
+ * @see #findFileOnUsersPath(String)
+ */
+ public static List<String> findFileOnUsersPath(String... filenames) {
+ Parameters.notNull("filenames", filenames); // NOI18N
+
+ String path = System.getenv("PATH"); // NOI18N
+ LOGGER.log(Level.FINE, "PATH: [{0}]", path);
+ if (path == null) {
+ return Collections.<String>emptyList();
+ }
+ // on linux there are usually duplicities in PATH
+ Set<String> dirs = new LinkedHashSet<>(Arrays.asList(path.split(File.pathSeparator)));
+ LOGGER.log(Level.FINE, "PATH dirs: {0}", dirs);
+ List<String> found = new ArrayList<>(dirs.size() * filenames.length);
+ for (String filename : filenames) {
+ Parameters.notNull("filename", filename); // NOI18N
+ for (String dir : dirs) {
+ File file = new File(dir, filename);
+ if (file.isFile()) {
+ String absolutePath = FileUtil.normalizeFile(file).getAbsolutePath();
+ LOGGER.log(Level.FINE, "File ''{0}'' found", absolutePath);
+ // not optimal but should be ok
+ if (!found.contains(absolutePath)) {
+ LOGGER.log(Level.FINE, "File ''{0}'' added to found files", absolutePath);
+ found.add(absolutePath);
+ }
+ }
+ }
+ }
+ LOGGER.log(Level.FINE, "Found files: {0}", found);
+ return found;
+ }
+
+ private static final Logger LOGGER = Logger.getLogger(Utils.class.getName());
+
}
diff --git a/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/lsp/LanguageServerImpl.java b/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/lsp/LanguageServerImpl.java
index 03dc969..ff58afe 100644
--- a/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/lsp/LanguageServerImpl.java
+++ b/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/lsp/LanguageServerImpl.java
@@ -18,12 +18,14 @@
*/
package org.netbeans.modules.cpplite.editor.lsp;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ProcessBuilder.Redirect;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -31,6 +33,8 @@
import java.util.logging.Logger;
import java.util.prefs.PreferenceChangeEvent;
import java.util.prefs.PreferenceChangeListener;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
import org.netbeans.api.editor.mimelookup.MimeRegistration;
import org.netbeans.api.editor.mimelookup.MimeRegistrations;
import org.netbeans.api.project.Project;
@@ -38,11 +42,11 @@
import org.netbeans.modules.cpplite.editor.file.MIMETypes;
import org.netbeans.modules.lsp.client.spi.ServerRestarter;
import org.netbeans.modules.lsp.client.spi.LanguageServerProvider;
-import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.netbeans.modules.cpplite.editor.spi.CProjectConfigurationProvider;
import org.netbeans.modules.cpplite.editor.spi.CProjectConfigurationProvider.ProjectConfiguration;
import org.openide.filesystems.FileUtil;
+import org.openide.modules.Places;
/**
*
@@ -74,32 +78,45 @@
Utils.settings().addPreferenceChangeListener(new PreferenceChangeListener() {
@Override
public void preferenceChange(PreferenceChangeEvent evt) {
- if (evt.getKey() == null || Utils.KEY_CCLS_PATH.equals(evt.getKey())) {
+ if (evt.getKey() == null || Utils.KEY_CCLS_PATH.equals(evt.getKey()) || Utils.KEY_CLANGD_PATH.equals(evt.getKey())) {
+ prj2Server.remove(prj);
restarter.restart();
Utils.settings().removePreferenceChangeListener(this);
}
}
});
- String ccls = Utils.settings().get(Utils.KEY_CCLS_PATH, null);
- if (ccls != null) {
+ String ccls = Utils.getCCLSPath();
+ String clangd = Utils.getCLANGDPath();
+ if (ccls != null || clangd != null) {
return prj2Server.computeIfAbsent(prj, (Project p) -> {
try {
List<String> command = new ArrayList<>();
- command.add(ccls);
- List<String> cat = getProjectSettings(prj);
-
- if (cat != null) {
- StringBuilder initOpt = new StringBuilder();
- initOpt.append("--init={\"compilationDatabaseCommand\":\"");
- String sep = "";
- for (String c : cat) {
- initOpt.append(sep);
- initOpt.append(c);
- sep = " ";
+ CProjectConfigurationProvider config = getProjectSettings(prj);
+ config.addChangeListener(new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ prj2Server.remove(prj);
+ restarter.restart();
+ config.removeChangeListener(this);
}
- initOpt.append("\"}");
- command.add(initOpt.toString());
+ });
+ File compileCommandDirs = getCompileCommandsDir(config);
+
+ if (compileCommandDirs != null) {
+ if (ccls != null) {
+ command.add(ccls);
+ StringBuilder initOpt = new StringBuilder();
+ initOpt.append("--init={\"compilationDatabaseDirectory\":\"");
+ initOpt.append(compileCommandDirs.getAbsolutePath());
+ initOpt.append("\"}");
+ command.add(initOpt.toString());
+ } else {
+ command.add(clangd);
+ command.add("--compile-commands-dir=" + compileCommandDirs.getAbsolutePath());
+ command.add("--clang-tidy");
+ command.add("--completion-style=detailed");
+ }
Process process = new ProcessBuilder(command).redirectError(Redirect.INHERIT).start();
return LanguageServerDescription.create(new CopyInput(process.getInputStream(), System.err), new CopyOutput(process.getOutputStream(), System.err), process);
}
@@ -113,25 +130,60 @@
return null;
}
- public static List<String> getProjectSettings(Project prj) {
- CProjectConfigurationProvider configProvider = prj.getLookup().lookup(CProjectConfigurationProvider.class);
-
- if (configProvider != null) {
- ProjectConfiguration config = configProvider.getProjectConfiguration();
- if (config != null && config.commandJsonCommand != null) {
- return configProvider.getProjectConfiguration().commandJsonCommand;
- } else if (config != null && configProvider.getProjectConfiguration().commandJsonPath != null) {
- //TODO: Linux independent!
- return Arrays.asList("cat", configProvider.getProjectConfiguration().commandJsonPath);
- }
- return null;
- } else if (prj.getProjectDirectory().getFileObject("compile_commands.json") != null) {
- //TODO: Linux independent!
- return Arrays.asList("cat", FileUtil.toFile(prj.getProjectDirectory().getFileObject("compile_commands.json")).getAbsolutePath());
- } else {
- return null;
- }
+ public static File getCompileCommandsDir(Project prj) {
+ return getCompileCommandsDir(getProjectSettings(prj));
}
+
+ private static CProjectConfigurationProvider getProjectSettings(Project prj) {
+ CProjectConfigurationProvider configProvider = prj.getLookup().lookup(CProjectConfigurationProvider.class);
+ if (configProvider == null) {
+ configProvider = new CProjectConfigurationProvider() {
+ @Override
+ public ProjectConfiguration getProjectConfiguration() {
+ return new ProjectConfiguration(new File(FileUtil.toFile(prj.getProjectDirectory()), "compile_commands.json").getAbsolutePath());
+ }
+ @Override
+ public void addChangeListener(ChangeListener listener) {
+ }
+ @Override
+ public void removeChangeListener(ChangeListener listener) {
+ }
+ };
+ }
+ return configProvider;
+ }
+
+ private static int tempDirIndex = 0;
+
+ private static File getCompileCommandsDir(CProjectConfigurationProvider configProvider) {
+ ProjectConfiguration config = configProvider.getProjectConfiguration();
+
+ if (config.commandJsonCommand != null || configProvider.getProjectConfiguration().commandJsonPath != null) {
+ File tempFile = Places.getCacheSubfile("cpplite/compile_commands/" + tempDirIndex++ + "/compile_commands.json");
+ if (config.commandJsonCommand != null) {
+ try {
+ new ProcessBuilder(config.commandJsonCommand).redirectOutput(tempFile).redirectError(Redirect.INHERIT).start().waitFor();
+ } catch (IOException | InterruptedException ex) {
+ LOG.log(Level.WARNING, null, ex);
+ return null;
+ }
+ } else {
+ File commandsPath = new File(configProvider.getProjectConfiguration().commandJsonPath);
+ if (commandsPath.canRead()) {
+ try (InputStream in = new FileInputStream(commandsPath);
+ OutputStream out = new FileOutputStream(tempFile)) {
+ FileUtil.copy(in, out);
+ } catch (IOException ex) {
+ LOG.log(Level.WARNING, null, ex);
+ return null;
+ }
+ }
+ }
+ return tempFile.getParentFile();
+ }
+ return null;
+ }
+
private static class CopyInput extends InputStream {
private final InputStream delegate;
diff --git a/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/lsp/options/Bundle.properties b/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/lsp/options/Bundle.properties
index ae14b37..48ee5b5 100644
--- a/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/lsp/options/Bundle.properties
+++ b/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/lsp/options/Bundle.properties
@@ -16,5 +16,9 @@
# under the License.
CPPLitePanel.jLabel1.text=CCLS Location:
-CPPLitePanel.jButton1.text=...
CPPLitePanel.cclsPath.text=
+CPPLitePanel.jLabel2.text=clangd Location:
+CPPLitePanel.jLabel3.text=<html><body>Please provide a path to either the <a href="https://github.com/MaskRay/ccls">ccls</a> or the <a href="https://clangd.llvm.org/">clangd</a> language protocol servers.\n<br>\nThese will be used by the editor to provide features like code completion.
+CPPLitePanel.clangdPath.text=
+CPPLitePanel.cclsBrowse.text=...
+CPPLitePanel.clangdBrowse.text=...
diff --git a/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/lsp/options/CPPLitePanel.form b/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/lsp/options/CPPLitePanel.form
index e5d211c..95574a8 100644
--- a/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/lsp/options/CPPLitePanel.form
+++ b/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/lsp/options/CPPLitePanel.form
@@ -37,21 +37,50 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
- <Group type="102" alignment="0" attributes="0">
- <Component id="jLabel1" min="-2" max="-2" attributes="0"/>
- <EmptySpace min="-2" max="-2" attributes="0"/>
- <Component id="cclsPath" max="32767" attributes="0"/>
- <EmptySpace min="-2" max="-2" attributes="0"/>
- <Component id="jButton1" min="-2" max="-2" attributes="0"/>
+ <Group type="102" attributes="0">
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Group type="102" attributes="0">
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Component id="jLabel1" alignment="0" min="-2" max="-2" attributes="0"/>
+ <Component id="jLabel2" alignment="0" min="-2" max="-2" attributes="0"/>
+ </Group>
+ <EmptySpace max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Component id="cclsPath" max="32767" attributes="0"/>
+ <Component id="clangdPath" max="32767" attributes="0"/>
+ </Group>
+ <EmptySpace max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Component id="clangdBrowse" min="-2" max="-2" attributes="0"/>
+ <Component id="cclsBrowse" min="-2" max="-2" attributes="0"/>
+ </Group>
+ </Group>
+ <Group type="102" attributes="0">
+ <Component id="jLabel3" min="-2" max="-2" attributes="0"/>
+ <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
+ </Group>
+ </Group>
+ <EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
- <Group type="103" groupAlignment="3" attributes="0">
- <Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/>
- <Component id="cclsPath" alignment="3" min="-2" max="-2" attributes="0"/>
- <Component id="jButton1" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Group type="102" alignment="1" attributes="0">
+ <Component id="jLabel3" min="-2" max="-2" attributes="0"/>
+ <EmptySpace type="separate" max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="3" attributes="0">
+ <Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="cclsPath" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="cclsBrowse" alignment="3" min="-2" max="-2" attributes="0"/>
+ </Group>
+ <EmptySpace max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Component id="jLabel2" min="-2" max="-2" attributes="0"/>
+ <Component id="clangdPath" min="-2" max="-2" attributes="0"/>
+ <Component id="clangdBrowse" min="-2" max="-2" attributes="0"/>
+ </Group>
+ <EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@@ -71,10 +100,44 @@
</Property>
</Properties>
</Component>
- <Component class="javax.swing.JButton" name="jButton1">
+ <Component class="javax.swing.JButton" name="cclsBrowse">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
- <ResourceString bundle="org/netbeans/modules/cpplite/editor/lsp/options/Bundle.properties" key="CPPLitePanel.jButton1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ <ResourceString bundle="org/netbeans/modules/cpplite/editor/lsp/options/Bundle.properties" key="CPPLitePanel.cclsBrowse.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ </Properties>
+ <Events>
+ <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cclsBrowseActionPerformed"/>
+ </Events>
+ </Component>
+ <Component class="javax.swing.JLabel" name="jLabel2">
+ <Properties>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/cpplite/editor/lsp/options/Bundle.properties" key="CPPLitePanel.jLabel2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JTextField" name="clangdPath">
+ <Properties>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/cpplite/editor/lsp/options/Bundle.properties" key="CPPLitePanel.clangdPath.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JButton" name="clangdBrowse">
+ <Properties>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/cpplite/editor/lsp/options/Bundle.properties" key="CPPLitePanel.clangdBrowse.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ </Properties>
+ <Events>
+ <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="clangdBrowseActionPerformed"/>
+ </Events>
+ </Component>
+ <Component class="javax.swing.JLabel" name="jLabel3">
+ <Properties>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/cpplite/editor/lsp/options/Bundle.properties" key="CPPLitePanel.jLabel3.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
</Property>
</Properties>
</Component>
diff --git a/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/lsp/options/CPPLitePanel.java b/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/lsp/options/CPPLitePanel.java
index a24edfd..864d1c9 100644
--- a/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/lsp/options/CPPLitePanel.java
+++ b/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/lsp/options/CPPLitePanel.java
@@ -18,6 +18,9 @@
*/
package org.netbeans.modules.cpplite.editor.lsp.options;
+import java.io.File;
+import javax.swing.JFileChooser;
+import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import org.netbeans.modules.cpplite.editor.Utils;
@@ -29,7 +32,7 @@
CPPLitePanel(CPPLiteOptionsPanelController controller) {
this.controller = controller;
initComponents();
- cclsPath.getDocument().addDocumentListener(new DocumentListener() {
+ DocumentListener pathsModified = new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
controller.changed();
@@ -42,8 +45,9 @@
public void changedUpdate(DocumentEvent e) {
controller.changed();
}
- });
- // TODO listen to changes in form fields and call controller.changed()
+ };
+ cclsPath.getDocument().addDocumentListener(pathsModified);
+ clangdPath.getDocument().addDocumentListener(pathsModified);
}
/**
@@ -56,40 +60,101 @@
jLabel1 = new javax.swing.JLabel();
cclsPath = new javax.swing.JTextField();
- jButton1 = new javax.swing.JButton();
+ cclsBrowse = new javax.swing.JButton();
+ jLabel2 = new javax.swing.JLabel();
+ clangdPath = new javax.swing.JTextField();
+ clangdBrowse = new javax.swing.JButton();
+ jLabel3 = new javax.swing.JLabel();
org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(CPPLitePanel.class, "CPPLitePanel.jLabel1.text")); // NOI18N
cclsPath.setText(org.openide.util.NbBundle.getMessage(CPPLitePanel.class, "CPPLitePanel.cclsPath.text")); // NOI18N
- org.openide.awt.Mnemonics.setLocalizedText(jButton1, org.openide.util.NbBundle.getMessage(CPPLitePanel.class, "CPPLitePanel.jButton1.text")); // NOI18N
+ org.openide.awt.Mnemonics.setLocalizedText(cclsBrowse, org.openide.util.NbBundle.getMessage(CPPLitePanel.class, "CPPLitePanel.cclsBrowse.text")); // NOI18N
+ cclsBrowse.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cclsBrowseActionPerformed(evt);
+ }
+ });
+
+ org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(CPPLitePanel.class, "CPPLitePanel.jLabel2.text")); // NOI18N
+
+ clangdPath.setText(org.openide.util.NbBundle.getMessage(CPPLitePanel.class, "CPPLitePanel.clangdPath.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(clangdBrowse, org.openide.util.NbBundle.getMessage(CPPLitePanel.class, "CPPLitePanel.clangdBrowse.text")); // NOI18N
+ clangdBrowse.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ clangdBrowseActionPerformed(evt);
+ }
+ });
+
+ org.openide.awt.Mnemonics.setLocalizedText(jLabel3, org.openide.util.NbBundle.getMessage(CPPLitePanel.class, "CPPLitePanel.jLabel3.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
- .addComponent(jLabel1)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(cclsPath)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(jButton1))
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel1)
+ .addComponent(jLabel2))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(cclsPath)
+ .addComponent(clangdPath))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(clangdBrowse)
+ .addComponent(cclsBrowse)))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jLabel3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(0, 0, Short.MAX_VALUE)))
+ .addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
- .addComponent(jLabel1)
- .addComponent(cclsPath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addComponent(jButton1))
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addComponent(jLabel3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(18, 18, 18)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel1)
+ .addComponent(cclsPath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(cclsBrowse))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel2)
+ .addComponent(clangdPath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(clangdBrowse))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
}// </editor-fold>//GEN-END:initComponents
+ private void cclsBrowseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cclsBrowseActionPerformed
+ showFileChooser(cclsPath);
+ }//GEN-LAST:event_cclsBrowseActionPerformed
+
+ private void clangdBrowseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clangdBrowseActionPerformed
+ showFileChooser(clangdPath);
+ }//GEN-LAST:event_clangdBrowseActionPerformed
+
+ private void showFileChooser(JTextField path) {
+ JFileChooser fc = new JFileChooser();
+ fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
+ fc.setSelectedFile(new File(path.getText()));
+ if (fc.showDialog(this, "Select") == JFileChooser.APPROVE_OPTION) {
+ path.setText(fc.getSelectedFile().getAbsolutePath());
+ }
+ }
void load() {
- cclsPath.setText(Utils.settings().get(Utils.KEY_CCLS_PATH, ""));
+ cclsPath.setText(Utils.getCCLSPath());
+ clangdPath.setText(Utils.getCLANGDPath());
}
void store() {
Utils.settings().put(Utils.KEY_CCLS_PATH, cclsPath.getText());
+ Utils.settings().put(Utils.KEY_CLANGD_PATH, clangdPath.getText());
}
boolean valid() {
@@ -98,8 +163,12 @@
}
// Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JButton cclsBrowse;
private javax.swing.JTextField cclsPath;
- private javax.swing.JButton jButton1;
+ private javax.swing.JButton clangdBrowse;
+ private javax.swing.JTextField clangdPath;
private javax.swing.JLabel jLabel1;
+ private javax.swing.JLabel jLabel2;
+ private javax.swing.JLabel jLabel3;
// End of variables declaration//GEN-END:variables
}
diff --git a/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/spi/CProjectConfigurationProvider.java b/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/spi/CProjectConfigurationProvider.java
index 5faacaf..a5ad8c5 100644
--- a/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/spi/CProjectConfigurationProvider.java
+++ b/cpplite/cpplite.editor/src/org/netbeans/modules/cpplite/editor/spi/CProjectConfigurationProvider.java
@@ -19,6 +19,7 @@
package org.netbeans.modules.cpplite.editor.spi;
import java.util.List;
+import javax.swing.event.ChangeListener;
/**
*
@@ -27,6 +28,8 @@
public interface CProjectConfigurationProvider {
public ProjectConfiguration getProjectConfiguration();
+ public void addChangeListener(ChangeListener listener);
+ public void removeChangeListener(ChangeListener listener);
//TODO: factory, accessor
//TODO: listen on changes
diff --git a/cpplite/cpplite.project/src/org/netbeans/modules/cpplite/project/CPPLiteCProjectConfigurationProvider.java b/cpplite/cpplite.project/src/org/netbeans/modules/cpplite/project/CPPLiteCProjectConfigurationProvider.java
index 76e1b94..49b6327 100644
--- a/cpplite/cpplite.project/src/org/netbeans/modules/cpplite/project/CPPLiteCProjectConfigurationProvider.java
+++ b/cpplite/cpplite.project/src/org/netbeans/modules/cpplite/project/CPPLiteCProjectConfigurationProvider.java
@@ -18,8 +18,12 @@
*/
package org.netbeans.modules.cpplite.project;
+import java.util.prefs.PreferenceChangeEvent;
+import java.util.prefs.PreferenceChangeListener;
import java.util.prefs.Preferences;
+import javax.swing.event.ChangeListener;
import org.netbeans.modules.cpplite.editor.spi.CProjectConfigurationProvider;
+import org.openide.util.ChangeSupport;
/**
*
@@ -27,12 +31,33 @@
*/
public class CPPLiteCProjectConfigurationProvider implements CProjectConfigurationProvider {
+ private final ChangeSupport cs = new ChangeSupport(this);
private final Preferences mainPrefs;
public CPPLiteCProjectConfigurationProvider(Preferences mainPrefs) {
this.mainPrefs = mainPrefs;
+ this.mainPrefs.addPreferenceChangeListener(new PreferenceChangeListener() {
+ @Override
+ public void preferenceChange(PreferenceChangeEvent evt) {
+ if (evt.getKey() == null ||
+ CPPLiteProject.KEY_COMPILE_COMMANDS.equals(evt.getKey()) ||
+ CPPLiteProject.KEY_COMPILE_COMMANDS_EXECUTABLE.equals(evt.getKey())) {
+ cs.fireChange();
+ }
+ }
+ });
}
-
+
+ @Override
+ public void addChangeListener(ChangeListener listener) {
+ cs.addChangeListener(listener);
+ }
+
+ @Override
+ public void removeChangeListener(ChangeListener listener) {
+ cs.removeChangeListener(listener);
+ }
+
@Override
public ProjectConfiguration getProjectConfiguration() {
String path = mainPrefs.get(CPPLiteProject.KEY_COMPILE_COMMANDS, null);
diff --git a/ide/editor/src/org/netbeans/modules/editor/resources/completion/record.png b/ide/editor/src/org/netbeans/modules/editor/resources/completion/record.png
new file mode 100644
index 0000000..220e6f3
--- /dev/null
+++ b/ide/editor/src/org/netbeans/modules/editor/resources/completion/record.png
Binary files differ
diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/LSPBindings.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/LSPBindings.java
index a572e48..99adf1a 100644
--- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/LSPBindings.java
+++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/LSPBindings.java
@@ -28,10 +28,12 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
@@ -61,6 +63,7 @@
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.netbeans.modules.lsp.client.bindings.LanguageClientImpl;
+import org.netbeans.modules.lsp.client.bindings.TextDocumentSyncServerCapabilityHandler;
import org.netbeans.modules.lsp.client.options.MimeTypeInfo;
import org.netbeans.modules.lsp.client.spi.ServerRestarter;
import org.netbeans.modules.lsp.client.spi.LanguageServerProvider;
@@ -71,6 +74,7 @@
import org.openide.util.Exceptions;
import org.openide.util.NbBundle.Messages;
import org.openide.util.RequestProcessor;
+import org.openide.util.RequestProcessor.Task;
import org.openide.util.lookup.Lookups;
/**
@@ -85,6 +89,7 @@
private static final Map<Project, Map<String, LSPBindings>> project2MimeType2Server = new WeakHashMap<>();
private static final Map<FileObject, Map<String, LSPBindings>> workspace2Extension2Server = new HashMap<>();
private final Map<FileObject, Map<BackgroundTask, RequestProcessor.Task>> backgroundTasks = new WeakHashMap<>();
+ private final Set<FileObject> openedFiles = new HashSet<>();
public static synchronized LSPBindings getBindings(FileObject file) {
for (Entry<FileObject, Map<String, LSPBindings>> e : workspace2Extension2Server.entrySet()) {
@@ -120,8 +125,15 @@
if (p != null) {
LSPBindings b = project2MimeType2Server.getOrDefault(p, Collections.emptyMap()).remove(mimeType);
- if (b != null && b.process != null) {
- b.process.destroy();
+ if (b != null) {
+ try {
+ b.server.shutdown().get();
+ } catch (InterruptedException | ExecutionException ex) {
+ LOG.log(Level.FINE, null, ex);
+ }
+ if (b.process != null) {
+ b.process.destroy();
+ }
}
}
}
@@ -147,6 +159,7 @@
b = new LSPBindings(server, result, LanguageServerProviderAccessor.getINSTANCE().getProcess(desc));
lci.setBindings(b);
LanguageServerProviderAccessor.getINSTANCE().setBindings(desc, b);
+ TextDocumentSyncServerCapabilityHandler.refreshOpenedFilesInServers();
return b;
} catch (InterruptedException | ExecutionException ex) {
LOG.log(Level.WARNING, null, ex);
@@ -264,7 +277,7 @@
if (bindings == null)
return ;
- RequestProcessor.Task req = bindings.backgroundTasks.computeIfAbsent(file, f -> new LinkedHashMap<>()).remove(task);
+ RequestProcessor.Task req = bindings.backgroundTasksMapFor(file).remove(task);
if (req != null) {
req.cancel();
@@ -285,7 +298,7 @@
if (bindings == null)
return ;
- RequestProcessor.Task req = bindings.backgroundTasks.computeIfAbsent(file, f -> Collections.emptyMap()).get(task);
+ RequestProcessor.Task req = bindings.backgroundTasksMapFor(file).get(task);
if (req != null) {
WORKER.post(req, DELAY);
@@ -293,7 +306,15 @@
}
public void scheduleBackgroundTasks(FileObject file) {
- backgroundTasks.computeIfAbsent(file, f -> new IdentityHashMap<>()).values().stream().forEach(this::scheduleBackgroundTask);
+ backgroundTasksMapFor(file).values().stream().forEach(this::scheduleBackgroundTask);
+ }
+
+ private Map<BackgroundTask, Task> backgroundTasksMapFor(FileObject file) {
+ return backgroundTasks.computeIfAbsent(file, f -> new IdentityHashMap<>());
+ }
+
+ public Set<FileObject> getOpenedFiles() {
+ return openedFiles;
}
public interface BackgroundTask {
diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CompletionProviderImpl.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CompletionProviderImpl.java
index 4a1ea3e..f3c0fb6 100644
--- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CompletionProviderImpl.java
+++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CompletionProviderImpl.java
@@ -54,11 +54,15 @@
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.netbeans.api.editor.completion.Completion;
import org.netbeans.api.editor.mimelookup.MimeRegistration;
+import org.netbeans.api.lexer.Token;
+import org.netbeans.api.lexer.TokenHierarchy;
+import org.netbeans.api.lexer.TokenSequence;
import org.netbeans.editor.BaseDocument;
import org.netbeans.editor.Utilities;
import org.netbeans.modules.editor.NbEditorUtilities;
import org.netbeans.modules.lsp.client.LSPBindings;
import org.netbeans.modules.lsp.client.Utils;
+import org.netbeans.modules.textmate.lexer.TextmateTokenId;
import org.netbeans.spi.editor.completion.CompletionDocumentation;
import org.netbeans.spi.editor.completion.CompletionProvider;
import org.netbeans.spi.editor.completion.CompletionResultSet;
@@ -223,8 +227,9 @@
public void processKeyEvent(KeyEvent ke) {
if (ke.getID() == KeyEvent.KEY_TYPED) {
String commitText = String.valueOf(ke.getKeyChar());
+ List<String> commitCharacters = i.getCommitCharacters();
- if (i.getCommitCharacters().contains(commitText)) {
+ if (commitCharacters != null && commitCharacters.contains(commitText)) {
commit(commitText);
ke.consume();
if (isTriggerCharacter(server, commitText)) {
diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/NavigatorPanelImpl.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/NavigatorPanelImpl.java
index 6c22f2e..a47072f 100644
--- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/NavigatorPanelImpl.java
+++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/NavigatorPanelImpl.java
@@ -170,7 +170,7 @@
}
private static Children createChildren(String currentFileUri, DocumentSymbol sym) {
- if (sym.getChildren().isEmpty()) {
+ if (sym.getChildren() == null || sym.getChildren().isEmpty()) {
return LEAF;
}
return new Keys<DocumentSymbol>() {
diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/TextDocumentSyncServerCapabilityHandler.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/TextDocumentSyncServerCapabilityHandler.java
index bab8e44..f4aa6bd 100644
--- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/TextDocumentSyncServerCapabilityHandler.java
+++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/TextDocumentSyncServerCapabilityHandler.java
@@ -86,39 +86,7 @@
Document doc = opened.getDocument();
- WORKER.post(() -> {
- LSPBindings server = LSPBindings.getBindings(file);
-
- if (server == null)
- return ; //ignore
-
- doc.putProperty(HyperlinkProviderImpl.class, Boolean.TRUE);
-
- String uri = Utils.toURI(file);
- String[] text = new String[1];
-
- doc.render(() -> {
- try {
- text[0] = doc.getText(0, doc.getLength());
- } catch (BadLocationException ex) {
- Exceptions.printStackTrace(ex);
- text[0] = "";
- }
- });
-
- TextDocumentItem textDocumentItem = new TextDocumentItem(uri,
- FileUtil.getMIMEType(file),
- 0,
- text[0]);
-
- server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(textDocumentItem));
- if (opened.getClientProperty(MarkOccurrences.class) == null) {
- MarkOccurrences mo = new MarkOccurrences(opened);
- LSPBindings.addBackgroundTask(file, mo);
- opened.putClientProperty(MarkOccurrences.class, mo);
- }
- server.scheduleBackgroundTasks(file);
- });
+ ensureOpenedInServer(opened);
doc.addDocumentListener(new DocumentListener() { //XXX: listener
int version; //XXX: proper versioning!
@@ -190,7 +158,6 @@
if (typingModification && oldText.isEmpty() && event.length == 1) {
if (newText.equals("}") || newText.equals("\n")) {
- System.err.println("going to compute indent:");
List<TextEdit> edits = new ArrayList<>();
doc.render(() -> {
if (documentVersion != DocumentUtilities.getDocumentVersion(doc))
@@ -216,12 +183,68 @@
}
}
+ private void ensureOpenedInServer(JTextComponent opened) {
+ FileObject file = NbEditorUtilities.getFileObject(opened.getDocument());
+
+ if (file == null)
+ return; //ignore
+
+ Document doc = opened.getDocument();
+ WORKER.post(() -> {
+ LSPBindings server = LSPBindings.getBindings(file);
+
+ if (server == null)
+ return ; //ignore
+
+ if (!server.getOpenedFiles().add(file)) {
+ //already opened:
+ return ;
+ }
+
+ doc.putProperty(HyperlinkProviderImpl.class, Boolean.TRUE);
+
+ String uri = Utils.toURI(file);
+ String[] text = new String[1];
+
+ doc.render(() -> {
+ try {
+ text[0] = doc.getText(0, doc.getLength());
+ } catch (BadLocationException ex) {
+ Exceptions.printStackTrace(ex);
+ text[0] = "";
+ }
+ });
+
+ TextDocumentItem textDocumentItem = new TextDocumentItem(uri,
+ FileUtil.getMIMEType(file),
+ 0,
+ text[0]);
+
+ server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(textDocumentItem));
+ if (opened.getClientProperty(MarkOccurrences.class) == null) {
+ MarkOccurrences mo = new MarkOccurrences(opened);
+ LSPBindings.addBackgroundTask(file, mo);
+ opened.putClientProperty(MarkOccurrences.class, mo);
+ }
+ server.scheduleBackgroundTasks(file);
+ });
+ }
+
+ public static void refreshOpenedFilesInServers() {
+ SwingUtilities.invokeLater(() -> {
+ assert SwingUtilities.isEventDispatchThread();
+ for (JTextComponent c : EditorRegistry.componentList()) {
+ h.ensureOpenedInServer(c);
+ }
+ });
+ }
+
+ private static final TextDocumentSyncServerCapabilityHandler h = new TextDocumentSyncServerCapabilityHandler();
@OnStart
public static class Init implements Runnable {
@Override
public void run() {
- TextDocumentSyncServerCapabilityHandler h = new TextDocumentSyncServerCapabilityHandler();
EditorRegistry.addPropertyChangeListener(evt -> h.handleChange());
SwingUtilities.invokeLater(() -> h.handleChange());
}
diff --git a/java/gradle.java/src/org/netbeans/modules/gradle/java/customizer/Bundle.properties b/java/gradle.java/src/org/netbeans/modules/gradle/java/customizer/Bundle.properties
index 8089bb4..53c6c77 100644
--- a/java/gradle.java/src/org/netbeans/modules/gradle/java/customizer/Bundle.properties
+++ b/java/gradle.java/src/org/netbeans/modules/gradle/java/customizer/Bundle.properties
@@ -31,3 +31,4 @@
CompileOptionsPanel.cbIncludeOpenProjects.text=Include Open Projects
CompileOptionsPanel.lbIncludeOpenProjects.text=<html>Create a composite project from this project and the other open Gradle projects by generating '--include-build' parameters.
SourceSetPanel.jScrollPane4.TabConstraints.tabTitle=Annotation Processors
+SourceSetPanel.jScrollPane6.TabConstraints.tabTitle=Compiler Args
diff --git a/java/gradle.java/src/org/netbeans/modules/gradle/java/customizer/SourceSetPanel.form b/java/gradle.java/src/org/netbeans/modules/gradle/java/customizer/SourceSetPanel.form
index 9fec81e..2d24624 100644
--- a/java/gradle.java/src/org/netbeans/modules/gradle/java/customizer/SourceSetPanel.form
+++ b/java/gradle.java/src/org/netbeans/modules/gradle/java/customizer/SourceSetPanel.form
@@ -134,8 +134,8 @@
<SubComponents>
<Component class="javax.swing.JTree" name="trSources">
<Properties>
- <Property name="model" type="javax.swing.tree.TreeModel" editor="org.netbeans.modules.form.editors2.TreeModelEditor">
- <TreeModel code=""/>
+ <Property name="model" type="javax.swing.tree.TreeModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+ <Connection code="sourcesModel" type="code"/>
</Property>
<Property name="rootVisible" type="boolean" value="false"/>
</Properties>
@@ -211,6 +211,32 @@
</Component>
</SubComponents>
</Container>
+ <Container class="javax.swing.JScrollPane" name="jScrollPane6">
+ <AuxValues>
+ <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+ </AuxValues>
+ <Constraints>
+ <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
+ <JTabbedPaneConstraints tabName="Compiler Args">
+ <Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/gradle/java/customizer/Bundle.properties" key="SourceSetPanel.jScrollPane6.TabConstraints.tabTitle" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ </JTabbedPaneConstraints>
+ </Constraint>
+ </Constraints>
+
+ <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+ <SubComponents>
+ <Component class="javax.swing.JTree" name="trCompilerArgs">
+ <Properties>
+ <Property name="model" type="javax.swing.tree.TreeModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+ <Connection code="argumentsModel" type="code"/>
+ </Property>
+ <Property name="rootVisible" type="boolean" value="false"/>
+ </Properties>
+ </Component>
+ </SubComponents>
+ </Container>
</SubComponents>
</Container>
<Component class="javax.swing.JTextField" name="tfSourceLevel">
diff --git a/java/gradle.java/src/org/netbeans/modules/gradle/java/customizer/SourceSetPanel.java b/java/gradle.java/src/org/netbeans/modules/gradle/java/customizer/SourceSetPanel.java
index 1f6c370..f4d4e98 100644
--- a/java/gradle.java/src/org/netbeans/modules/gradle/java/customizer/SourceSetPanel.java
+++ b/java/gradle.java/src/org/netbeans/modules/gradle/java/customizer/SourceSetPanel.java
@@ -51,7 +51,10 @@
final Path relativeRoot;
final GradleJavaSourceSet sourceSet;
- DefaultMutableTreeNode sourcesRoot = new DefaultMutableTreeNode(new Object());
+ private final DefaultMutableTreeNode sourcesRoot = new DefaultMutableTreeNode(new Object());
+ private final DefaultTreeModel sourcesModel = new DefaultTreeModel(sourcesRoot, true);
+ private final DefaultMutableTreeNode argumentsRoot = new DefaultMutableTreeNode(new Object());
+ private final DefaultTreeModel argumentsModel = new DefaultTreeModel(argumentsRoot, true);
/**
* Creates new form SourceSetPanel
@@ -82,20 +85,21 @@
for (GradleJavaSourceSet.SourceType type : GradleJavaSourceSet.SourceType.values()) {
if (!sourceSet.getSourceDirs(type).isEmpty()) {
DefaultMutableTreeNode typeNode = new DefaultMutableTreeNode(type);
- sourcesRoot.add(typeNode);
+ sourcesModel.insertNodeInto(typeNode, sourcesRoot, sourcesRoot.getChildCount());
for (File dir : sourceSet.getSourceDirs(type)) {
- typeNode.add(new DefaultMutableTreeNode(dir, false));
+ sourcesModel.insertNodeInto(new DefaultMutableTreeNode(dir, false), typeNode, typeNode.getChildCount());
}
+ trSources.expandPath(new TreePath(typeNode.getPath()));
+ }
+ if (!sourceSet.getCompilerArgs(type).isEmpty()) {
+ DefaultMutableTreeNode typeNode = new DefaultMutableTreeNode(type, true);
+ argumentsModel.insertNodeInto(typeNode, argumentsRoot, argumentsRoot.getChildCount());
+ for (String compilerArg : sourceSet.getCompilerArgs(type)) {
+ argumentsModel.insertNodeInto(new DefaultMutableTreeNode(compilerArg, false), typeNode, typeNode.getChildCount());
+ }
+ trCompilerArgs.expandPath(new TreePath(typeNode.getPath()));
}
}
- trSources.setModel(new DefaultTreeModel(sourcesRoot, true));
- DefaultMutableTreeNode currentNode = sourcesRoot;
- do {
- if (currentNode.getLevel() <= 1) {
- trSources.expandPath(new TreePath(currentNode.getPath()));
- }
- currentNode = currentNode.getNextNode();
- } while (currentNode != null);
trSources.setCellRenderer(new MyTreeCellRenderer());
DefaultListModel<File> compileModel = new DefaultListModel<>();
@@ -141,6 +145,8 @@
lsRuntime = new javax.swing.JList<>();
jScrollPane4 = new javax.swing.JScrollPane();
lsAnnotationProcessors = new javax.swing.JList<>();
+ jScrollPane6 = new javax.swing.JScrollPane();
+ trCompilerArgs = new javax.swing.JTree();
tfSourceLevel = new javax.swing.JTextField();
tfOutputResources = new javax.swing.JTextField();
tfOutputClasses = new javax.swing.JTextField();
@@ -153,8 +159,7 @@
jTabbedPane1.setTabPlacement(javax.swing.JTabbedPane.BOTTOM);
- javax.swing.tree.DefaultMutableTreeNode treeNode1 = new javax.swing.tree.DefaultMutableTreeNode("root");
- trSources.setModel(new javax.swing.tree.DefaultTreeModel(treeNode1));
+ trSources.setModel(sourcesModel);
trSources.setRootVisible(false);
jScrollPane1.setViewportView(trSources);
@@ -172,6 +177,12 @@
jTabbedPane1.addTab(org.openide.util.NbBundle.getMessage(SourceSetPanel.class, "SourceSetPanel.jScrollPane4.TabConstraints.tabTitle"), jScrollPane4); // NOI18N
+ trCompilerArgs.setModel(argumentsModel);
+ trCompilerArgs.setRootVisible(false);
+ jScrollPane6.setViewportView(trCompilerArgs);
+
+ jTabbedPane1.addTab(org.openide.util.NbBundle.getMessage(SourceSetPanel.class, "SourceSetPanel.jScrollPane6.TabConstraints.tabTitle"), jScrollPane6); // NOI18N
+
tfSourceLevel.setEditable(false);
tfSourceLevel.setText(org.openide.util.NbBundle.getMessage(SourceSetPanel.class, "SourceSetPanel.tfSourceLevel.text")); // NOI18N
@@ -278,6 +289,7 @@
private javax.swing.JScrollPane jScrollPane2;
private javax.swing.JScrollPane jScrollPane3;
private javax.swing.JScrollPane jScrollPane4;
+ private javax.swing.JScrollPane jScrollPane6;
private javax.swing.JTabbedPane jTabbedPane1;
private javax.swing.JList<File> lsAnnotationProcessors;
private javax.swing.JList<File> lsCompile;
@@ -285,6 +297,7 @@
private javax.swing.JTextField tfOutputClasses;
private javax.swing.JTextField tfOutputResources;
private javax.swing.JTextField tfSourceLevel;
+ private javax.swing.JTree trCompilerArgs;
private javax.swing.JTree trSources;
// End of variables declaration//GEN-END:variables
}
diff --git a/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/FileBuiltQueryImpl.java b/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/FileBuiltQueryImpl.java
index a66390b..2fbc76f 100644
--- a/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/FileBuiltQueryImpl.java
+++ b/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/FileBuiltQueryImpl.java
@@ -32,8 +32,10 @@
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.event.ChangeListener;
+import org.netbeans.api.java.source.SourceUtils;
import org.netbeans.api.project.Project;
import org.netbeans.api.queries.FileBuiltQuery;
+import static org.netbeans.modules.gradle.java.api.GradleJavaSourceSet.SourceType.JAVA;
import org.netbeans.spi.project.ProjectServiceProvider;
import org.netbeans.spi.project.ui.ProjectOpenedHook;
import org.netbeans.spi.queries.FileBuiltQueryImplementation;
@@ -119,8 +121,13 @@
if (sourceSet != null) {
String relFile = sourceSet.relativePath(f);
String relClass = relFile.substring(0, relFile.lastIndexOf('.')) + ".class"; //NOI18N
+ String moduleRoot = null;
+ File moduleInfo = sourceSet.findResource("module-info.java", false, JAVA); //NOI18N
+ if (moduleInfo != null && sourceSet.getCompilerArgs(JAVA).contains("--module-source-path")) {
+ moduleRoot = SourceUtils.parseModuleName(FileUtil.toFileObject(moduleInfo));
+ }
try {
- ret = new StatusImpl(file, sourceSet.getOutputClassDirs(), relClass);
+ ret = new StatusImpl(file, sourceSet.getOutputClassDirs(), relClass, moduleRoot);
} catch (DataObjectNotFoundException ex) {}
}
@@ -147,6 +154,7 @@
private final DataObject source;
private final Set<File> roots;
private final String relClass;
+ private final String moduleName;
private final PropertyChangeListener pcl = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
@@ -180,13 +188,15 @@
};
boolean status;
- public StatusImpl(FileObject source, Set<File> roots, String relClass) throws DataObjectNotFoundException {
+ public StatusImpl(FileObject source, Set<File> roots, String relClass, String moduleName) throws DataObjectNotFoundException {
this.roots = roots;
this.relClass = relClass;
this.source = DataObject.find(source);
+ this.moduleName = moduleName;
this.source.addPropertyChangeListener(WeakListeners.propertyChange(pcl, this.source));
for (File root : roots) {
- FileUtil.addFileChangeListener(listener, FileUtil.normalizeFile(new File(root, relClass)));
+ File moduleRoot = moduleName == null ? root : new File(root, moduleName);
+ FileUtil.addFileChangeListener(listener, FileUtil.normalizeFile(new File(moduleRoot, relClass)));
}
checkBuilt();
}
@@ -211,7 +221,8 @@
boolean built = false;
if (fo != null) {
for (File root : roots) {
- File target = FileUtil.normalizeFile(new File(root, relClass));
+ File moduleRoot = moduleName == null ? root : new File(root, moduleName);
+ File target = FileUtil.normalizeFile(new File(moduleRoot, relClass));
if (target.exists()) {
long sourceTime = fo.lastModified().getTime();
long targetTime = target.lastModified();
diff --git a/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleSourceForBinary.java b/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleSourceForBinary.java
index 2222875..4a5a4ff 100644
--- a/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleSourceForBinary.java
+++ b/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleSourceForBinary.java
@@ -19,6 +19,8 @@
package org.netbeans.modules.gradle.java.queries;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import org.netbeans.modules.gradle.java.api.GradleJavaSourceSet;
import org.netbeans.modules.gradle.java.api.GradleJavaSourceSet.SourceType;
import static org.netbeans.modules.gradle.java.api.GradleJavaSourceSet.SourceType.*;
@@ -38,12 +40,15 @@
import javax.swing.event.ChangeListener;
import org.netbeans.api.java.queries.SourceForBinaryQuery;
import org.netbeans.api.project.Project;
+import static org.netbeans.modules.gradle.api.NbGradleProject.Quality.*;
import org.netbeans.spi.java.queries.SourceForBinaryQueryImplementation;
import org.netbeans.spi.java.queries.SourceForBinaryQueryImplementation2;
import org.netbeans.spi.project.ProjectServiceProvider;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
+import org.openide.util.ChangeSupport;
import org.openide.util.Utilities;
+import org.openide.util.WeakListeners;
/**
*
@@ -65,15 +70,17 @@
if (ret == null) {
try {
NbGradleProject watcher = NbGradleProject.get(project);
- if (watcher.getQuality().atLeast(NbGradleProject.Quality.FULL)) {
+ if (watcher.getQuality().atLeast(FALLBACK)) {
GradleJavaProject prj = GradleJavaProject.get(project);
switch (binaryRoot.getProtocol()) {
case "file": { //NOI18N
File root = FileUtil.normalizeFile(Utilities.toFile(binaryRoot.toURI()));
for (GradleJavaSourceSet ss : prj.getSourceSets().values()) {
+ File outputDir = ss.getCompilerArgs(JAVA).contains("--module-source-path") ? //NOI18N
+ root.getParentFile() : root;
for (File dir : ss.getOutputClassDirs()) {
- if (root.equals(dir)) {
- ret = new Res(project, ss.getName(), EnumSet.of(JAVA, GROOVY, SCALA));
+ if (outputDir.equals(dir)) {
+ ret = new Res(project, ss.getName(), EnumSet.of(JAVA, GROOVY, SCALA, GENERATED));
break;
}
}
@@ -118,11 +125,19 @@
private final Project project;
private final String sourceSet;
private final Set<SourceType> sourceTypes;
+ private final PropertyChangeListener listener;
+ private final ChangeSupport support = new ChangeSupport(this);
public Res(Project project, String sourceSet, Set<SourceType> sourceTypes) {
this.project = project;
this.sourceSet = sourceSet;
this.sourceTypes = sourceTypes;
+ listener = (PropertyChangeEvent evt) -> {
+ if (NbGradleProject.PROP_PROJECT_INFO.equals(evt.getPropertyName())) {
+ support.fireChange();
+ }
+ };
+ NbGradleProject.get(project).addPropertyChangeListener(WeakListeners.propertyChange(listener, project));
}
@Override
@@ -152,10 +167,16 @@
@Override
public void addChangeListener(ChangeListener l) {
+ synchronized (support) {
+ support.addChangeListener(l);
+ }
}
@Override
public void removeChangeListener(ChangeListener l) {
+ synchronized (support) {
+ support.removeChangeListener(l);
+ }
}
}
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 b6bbb7f..ef40212 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
@@ -199,7 +199,7 @@
private static final String WHILE_KEYWORD = "while"; //NOI18N
private static final String WITH_KEYWORD = "with"; //NOI18N
private static final String YIELD_KEYWORD = "yield"; //NOI18N
-
+ private static final String RECORD_KEYWORD = "record"; //NOI18N
private static final String JAVA_LANG_CLASS = "java.lang.Class"; //NOI18N
private static final String JAVA_LANG_OBJECT = "java.lang.Object"; //NOI18N
private static final String JAVA_LANG_ITERABLE = "java.lang.Iterable"; //NOI18N
@@ -237,9 +237,9 @@
private static final SourceVersion SOURCE_VERSION_RELEASE_10;
private static final SourceVersion SOURCE_VERSION_RELEASE_11;
private static final SourceVersion SOURCE_VERSION_RELEASE_13;
-
+ private static final SourceVersion SOURCE_VERSION_RELEASE_14;
static {
- SourceVersion r10, r11, r13;
+ SourceVersion r10, r11, r13, r14;
try {
r10 = SourceVersion.valueOf("RELEASE_10");
@@ -256,10 +256,16 @@
} catch (IllegalArgumentException ex) {
r13 = null;
}
+ try {
+ r14 = SourceVersion.valueOf("RELEASE_14");
+ } catch (IllegalArgumentException ex) {
+ r14 = null;
+ }
SOURCE_VERSION_RELEASE_10 = r10;
SOURCE_VERSION_RELEASE_11 = r11;
SOURCE_VERSION_RELEASE_13 = r13;
+ SOURCE_VERSION_RELEASE_14 = r14;
}
private final ItemFactory<T> itemFactory;
@@ -502,6 +508,8 @@
default:
if (path.getLeaf().getKind().toString().equals(TreeShims.SWITCH_EXPRESSION)) {
insideSwitch(env);
+ } else if (TreeShims.isRecord(path.getLeaf())) {
+ insideRecord(env);
}
break;
}
@@ -726,7 +734,11 @@
addPackages(env, null, false);
}
if (options.contains(Options.ALL_COMPLETION)) {
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE), null);
+ EnumSet<ElementKind> classKinds = EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE);
+ if (isRecordSupported(env)) {
+ classKinds.add(TreeShims.getRecordKind());
+ }
+ addTypes(env, classKinds, null);
} else {
hasAdditionalClasses = true;
}
@@ -748,7 +760,7 @@
int idx = headerText.indexOf('{'); //NOI18N
if (idx >= 0) {
addKeywordsForClassBody(env);
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER), null);
+ addClassTypes(env, null);
addElementCreators(env);
return;
}
@@ -866,7 +878,7 @@
addClassModifiers(env, cls.getModifiers().getFlags());
} else {
addMemberModifiers(env, cls.getModifiers().getFlags(), false);
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER), null);
+ addClassTypes(env, null);
}
}
@@ -919,7 +931,7 @@
}
boolean isLocal = !TreeUtilities.CLASS_TREE_KINDS.contains(parent.getKind());
addMemberModifiers(env, var.getModifiers().getFlags(), isLocal);
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER), null);
+ addClassTypes(env, null);
ModifiersTree mods = var.getModifiers();
if (mods.getFlags().isEmpty() && mods.getAnnotations().isEmpty()) {
addElementCreators(env);
@@ -1023,7 +1035,7 @@
switch (lastToken.token().id()) {
case LPAREN:
addMemberModifiers(env, Collections.<Modifier>emptySet(), true);
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER), null);
+ addClassTypes(env, null);
break;
case RPAREN:
Tree mthParent = path.getParentPath().getLeaf();
@@ -1062,13 +1074,13 @@
case GTGTGT:
addPrimitiveTypeKeywords(env);
addKeyword(env, VOID_KEYWORD, SPACE, false);
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER), null);
+ addClassTypes(env, null);
break;
case COMMA:
switch (state) {
case 3:
addMemberModifiers(env, Collections.<Modifier>emptySet(), true);
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER), null);
+ addClassTypes(env, null);
break;
case 4:
if (!options.contains(Options.ALL_COMPLETION) && mth.getBody() != null) {
@@ -1110,7 +1122,7 @@
switch (state) {
case 0:
addMemberModifiers(env, mth.getModifiers().getFlags(), false);
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER), null);
+ addClassTypes(env, null);
break;
case 1:
if (((TypeParameterTree) lastTree).getBounds().isEmpty()) {
@@ -1183,10 +1195,10 @@
addClassModifiers(env, m);
} else if (parent.getKind() != Tree.Kind.VARIABLE || grandParent == null || TreeUtilities.CLASS_TREE_KINDS.contains(grandParent.getKind())) {
addMemberModifiers(env, m, false);
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER), null);
+ addClassTypes(env, null);
} else if (parent.getKind() == Tree.Kind.VARIABLE && grandParent.getKind() == Tree.Kind.METHOD) {
addMemberModifiers(env, m, true);
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER), null);
+ addClassTypes(env, null);
} else {
localResult(env);
addKeywordsForBlock(env);
@@ -1296,7 +1308,7 @@
if (pos >= 0 && pos < offset) {
insideExpression(env, new TreePath(env.getPath(), att.getUnderlyingType()));
} else {
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER), null);
+ addClassTypes(env, null);
}
}
@@ -1452,7 +1464,7 @@
}
}
}
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER), null);
+ addClassTypes(env, null);
break;
case QUESTION:
addKeyword(env, EXTENDS_KEYWORD, SPACE, false);
@@ -1471,7 +1483,7 @@
String text = env.getController().getText().substring(blockPos, offset);
if (text.indexOf('{') < 0) { //NOI18N
addMemberModifiers(env, Collections.singleton(STATIC), false);
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER), null);
+ addClassTypes(env, null);
return;
}
StatementTree last = null;
@@ -1592,9 +1604,11 @@
break;
case LT:
case COMMA:
+ addClassTypes(env, null);
+ break;
case EXTENDS:
case SUPER:
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER), null);
+ addClassTypes(env, null);
break;
}
} else if (lastNonWhitespaceTokenId != JavaTokenId.STAR) {
@@ -1922,7 +1936,7 @@
break;
case LT:
case COMMA:
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER), null);
+ addClassTypes(env, null);
break;
}
}
@@ -1941,7 +1955,7 @@
case COMMA:
if (let.getParameters().isEmpty()
|| env.getController().getTrees().getSourcePositions().getStartPosition(path.getCompilationUnit(), let.getParameters().get(0).getType()) >= 0) {
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER), null);
+ addClassTypes(env, null);
addPrimitiveTypeKeywords(env);
addKeyword(env, FINAL_KEYWORD, SPACE, false);
}
@@ -1984,7 +1998,7 @@
}
addLocalMembersAndVars(env);
addValueKeywords(env);
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER), null);
+ addClassTypes(env,null);
addPrimitiveTypeKeywords(env);
}
@@ -2057,7 +2071,11 @@
env.insideNew();
}
if (encl == null) {
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE), base);
+ EnumSet<ElementKind> classKinds = EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE);
+ if (isRecordSupported(env)) {
+ classKinds.add(TreeShims.getRecordKind());
+ }
+ addTypes(env, classKinds, base);
} else {
TypeMirror enclType = controller.getTrees().getTypeMirror(new TreePath(path, nc.getEnclosingExpression()));
if (enclType != null && enclType.getKind() == TypeKind.DECLARED) {
@@ -2074,7 +2092,7 @@
}
addLocalMembersAndVars(env);
addValueKeywords(env);
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER), null);
+ addClassTypes(env,null);
addPrimitiveTypeKeywords(env);
break;
case GT:
@@ -2241,7 +2259,7 @@
TokenSequence<JavaTokenId> last = findLastNonWhitespaceToken(env, fl, offset);
if (last != null && last.token().id() == JavaTokenId.LPAREN) {
addLocalFieldsAndVars(env);
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER), null);
+ addClassTypes(env,null);
addPrimitiveTypeKeywords(env);
}
} else {
@@ -2471,7 +2489,7 @@
}
}
addLocalMembersAndVars(env);
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER), null);
+ addClassTypes(env,null);
addPrimitiveTypeKeywords(env);
addValueKeywords(env);
} else {
@@ -2483,7 +2501,7 @@
InstanceOfTree iot = (InstanceOfTree) env.getPath().getLeaf();
TokenSequence<JavaTokenId> ts = findLastNonWhitespaceToken(env, iot, env.getOffset());
if (ts != null && ts.token().id() == JavaTokenId.INSTANCEOF) {
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER), null);
+ addClassTypes(env, null);
}
}
@@ -3008,10 +3026,193 @@
}
}
}
+
+ private void addClassTypes(final Env env, DeclaredType baseType) throws IOException{
+ EnumSet<ElementKind> classKinds = EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER);
+ if (isRecordSupported(env)) {
+ classKinds.add(TreeShims.getRecordKind());
+ }
+ addTypes(env, classKinds, baseType);
+ }
+
+ private boolean isRecordSupported(final Env env) {
+ return (SOURCE_VERSION_RELEASE_14 != null && env.getController().getSourceVersion().compareTo(SOURCE_VERSION_RELEASE_14) >= 0);
+ }
+
+ private void insideRecord(Env env) throws IOException {
+ int offset = env.getOffset();
+ env.insideClass();
+ TreePath path = env.getPath();
+ ClassTree cls = (ClassTree) path.getLeaf();
+ CompilationController controller = env.getController();
+ SourcePositions sourcePositions = env.getSourcePositions();
+ CompilationUnitTree root = env.getRoot();
+ int startPos = (int) sourcePositions.getEndPosition(root, cls.getModifiers());
+ if (startPos <= 0) {
+ startPos = (int) sourcePositions.getStartPosition(root, cls);
+ }
+ String headerText = controller.getText().substring(startPos, offset);
+ int idx = headerText.indexOf('{'); //NOI18N
+ if (idx >= 0) {
+ addKeywordsForClassBody(env);
+ addClassTypes(env, null);
+ addElementCreators(env);
+ return;
+ }
+ TreeUtilities tu = controller.getTreeUtilities();
+ Tree lastImpl = null;
+ for (Tree impl : cls.getImplementsClause()) {
+ int implPos = (int) sourcePositions.getEndPosition(root, impl);
+ if (implPos == Diagnostic.NOPOS || offset <= implPos) {
+ break;
+ }
+ lastImpl = impl;
+ startPos = implPos;
+ }
+ if (lastImpl != null) {
+ TokenSequence<JavaTokenId> last = findLastNonWhitespaceToken(env, startPos, offset);
+ if (last != null && last.token().id() == JavaTokenId.COMMA) {
+ controller.toPhase(Phase.ELEMENTS_RESOLVED);
+ env.addToExcludes(controller.getTrees().getElement(path));
+ addTypes(env, EnumSet.of(INTERFACE, ANNOTATION_TYPE), null);
+ }
+ return;
+ }
+ List<? extends Tree> members = cls.getMembers();
+
+ Tree lastParam = null;
+ for (Tree member : members) {
+ if (member.getKind() == Tree.Kind.VARIABLE) {
+ ModifiersTree modifiers = ((VariableTree) member).getModifiers();
+ Set<Modifier> modifierSet = modifiers.getFlags();
+
+ if (!modifierSet.contains(Modifier.STATIC)) {
+ int paramPos = (int) sourcePositions.getEndPosition(root, member);
+ if (paramPos == Diagnostic.NOPOS || offset <= paramPos) {
+ break;
+ }
+ lastParam = member;
+ startPos = paramPos;
+ }
+ }
+
+ if (lastParam != null) {
+ TokenSequence<JavaTokenId> first = findFirstNonWhitespaceToken(env, startPos, offset);
+ if (first != null && first.token().id() == JavaTokenId.COMMA) {
+ controller.toPhase(Phase.ELEMENTS_RESOLVED);
+ env.addToExcludes(controller.getTrees().getElement(path));
+ addTypes(env, EnumSet.of(INTERFACE, ANNOTATION_TYPE), null);
+ return;
+ }
+ if (first != null && first.token().id() == JavaTokenId.RPAREN) {
+ first = nextNonWhitespaceToken(first);
+ if (!tu.isInterface(cls) && first.token().id() == JavaTokenId.LBRACE) {
+ addKeyword(env, IMPLEMENTS_KEYWORD, SPACE, false);
+ }
+
+ }
+ return;
+ }
+
+ }
+
+ TypeParameterTree lastTypeParam = null;
+ for (TypeParameterTree tp : cls.getTypeParameters()) {
+ int tpPos = (int) sourcePositions.getEndPosition(root, tp);
+ if (tpPos == Diagnostic.NOPOS || offset <= tpPos) {
+ break;
+ }
+ lastTypeParam = tp;
+ startPos = tpPos;
+ }
+
+ TokenSequence<JavaTokenId> lastNonWhitespaceToken = findLastNonWhitespaceToken(env, startPos, offset);
+ if (lastNonWhitespaceToken != null) {
+ switch (lastNonWhitespaceToken.token().id()) {
+ case LPAREN:
+ addMemberModifiers(env, Collections.<Modifier>emptySet(), true);
+ addClassTypes(env, null);
+ break;
+ case IMPLEMENTS:
+ controller.toPhase(Phase.ELEMENTS_RESOLVED);
+ env.addToExcludes(controller.getTrees().getElement(path));
+ addTypes(env, EnumSet.of(INTERFACE, ANNOTATION_TYPE), null);
+ break;
+ case RPAREN:
+ if (!tu.isAnnotation(cls)) {
+ if (!tu.isInterface(cls)) {
+ addKeyword(env, IMPLEMENTS_KEYWORD, SPACE, false);
+ }
+ }
+ break;
+ }
+ return;
+ }
+
+ if (lastTypeParam != null) {
+ TokenSequence<JavaTokenId> first = findFirstNonWhitespaceToken(env, startPos, offset);
+
+ if (first != null && (first.token().id() == JavaTokenId.GT
+ || first.token().id() == JavaTokenId.GTGT
+ || first.token().id() == JavaTokenId.GTGTGT)) {
+ first = nextNonWhitespaceToken(first);
+
+ TokenSequence<JavaTokenId> last = findLastNonWhitespaceToken(env, first.offset(), offset);
+ TokenSequence<JavaTokenId> old = first;
+ first = nextNonWhitespaceToken(first);
+ if (last != null && first.token().id() == last.token().id()) {
+ first = nextNonWhitespaceToken(first);
+ } else {
+ first = old;
+ }
+
+ if (first != null && first.offset() < offset) {
+ if (first.token().id() == JavaTokenId.EXTENDS) {
+ controller.toPhase(Phase.ELEMENTS_RESOLVED);
+ env.afterExtends();
+ env.addToExcludes(controller.getTrees().getElement(path));
+ addTypes(env, tu.isInterface(cls) ? EnumSet.of(INTERFACE, ANNOTATION_TYPE) : EnumSet.of(CLASS), null);
+ return;
+ }
+ if (first.token().id() == JavaTokenId.IMPLEMENTS) {
+ controller.toPhase(Phase.ELEMENTS_RESOLVED);
+ env.addToExcludes(controller.getTrees().getElement(path));
+ addTypes(env, EnumSet.of(INTERFACE, ANNOTATION_TYPE), null);
+ return;
+ }
+ } else if (!tu.isAnnotation(cls)) {
+
+ if (!tu.isInterface(cls) && first.token().id() == JavaTokenId.LBRACE) {
+ addKeyword(env, IMPLEMENTS_KEYWORD, SPACE, false);
+ } else if (!tu.isInterface(cls) && first.token().id() == JavaTokenId.RPAREN) {
+ controller.toPhase(Phase.ELEMENTS_RESOLVED);
+ env.addToExcludes(controller.getTrees().getElement(path));
+ addTypes(env, EnumSet.of(INTERFACE, ANNOTATION_TYPE), null);
+ }
+ return;
+ }
+ } else if (lastTypeParam.getBounds().isEmpty()) {
+ addKeyword(env, EXTENDS_KEYWORD, SPACE, false);
+ }
+ return;
+ }
+
+ lastNonWhitespaceToken = findLastNonWhitespaceToken(env, (int) sourcePositions.getStartPosition(root, cls), offset);
+ if (lastNonWhitespaceToken != null && lastNonWhitespaceToken.token().id() == JavaTokenId.AT) {
+ addKeyword(env, INTERFACE_KEYWORD, SPACE, false);
+ addTypes(env, EnumSet.of(ANNOTATION_TYPE), null);
+ } else if (path.getParentPath().getLeaf().getKind() == Tree.Kind.COMPILATION_UNIT) {
+ addClassModifiers(env, cls.getModifiers().getFlags());
+ } else {
+ addMemberModifiers(env, cls.getModifiers().getFlags(), false);
+ addClassTypes(env, null);
+ }
+
+ }
private void localResult(Env env) throws IOException {
addLocalMembersAndVars(env);
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER), null);
+ addClassTypes(env, null);
addPrimitiveTypeKeywords(env);
}
@@ -3092,7 +3293,7 @@
break;
}
}
- addTypes(env, EnumSet.of(CLASS, INTERFACE, ENUM, ANNOTATION_TYPE, TYPE_PARAMETER), null);
+ addClassTypes(env, null);
}
private void addLocalMembersAndVars(final Env env) throws IOException {
@@ -3793,6 +3994,9 @@
}
private void addPackageContent(final Env env, PackageElement pe, EnumSet<ElementKind> kinds, DeclaredType baseType, boolean insideNew, boolean srcOnly) throws IOException {
+ if (isRecordSupported(env)) {
+ kinds.add(TreeShims.getRecordKind());
+ }
Set<? extends TypeMirror> smartTypes = options.contains(Options.ALL_COMPLETION) ? null : getSmartTypes(env);
CompilationController controller = env.getController();
Elements elements = controller.getElements();
@@ -4306,6 +4510,9 @@
kws.add(ENUM_KEYWORD);
kws.add(FINAL_KEYWORD);
kws.add(INTERFACE_KEYWORD);
+ if (isRecordSupported(env)) {
+ kws.add(RECORD_KEYWORD);
+ }
}
boolean beforeAnyClass = true;
boolean beforePublicClass = true;
@@ -4374,6 +4581,9 @@
&& env.getController().getTreeUtilities().getPathElementOfKind(Tree.Kind.INTERFACE, env.getPath()) != null) {
results.add(itemFactory.createKeywordItem(DEFAULT_KEYWORD, SPACE, anchorOffset, false));
}
+ if (isRecordSupported(env)) {
+ results.add(itemFactory.createKeywordItem(RECORD_KEYWORD, SPACE, anchorOffset, false));
+ }
addPrimitiveTypeKeywords(env);
}
@@ -4389,6 +4599,9 @@
results.add(itemFactory.createKeywordItem(kw, SPACE, anchorOffset, false));
}
}
+ if (isRecordSupported(env)) {
+ results.add(itemFactory.createKeywordItem(RECORD_KEYWORD, SPACE, anchorOffset, false));
+ }
if (Utilities.startsWith(RETURN_KEYWORD, prefix)) {
TreePath tp = env.getController().getTreeUtilities().getPathElementOfKind(EnumSet.of(Tree.Kind.METHOD, Tree.Kind.LAMBDA_EXPRESSION), env.getPath());
String postfix = SPACE;
@@ -4573,6 +4786,9 @@
kws.add(CLASS_KEYWORD);
kws.add(INTERFACE_KEYWORD);
kws.add(ENUM_KEYWORD);
+ if (isRecordSupported(env)) {
+ kws.add(RECORD_KEYWORD);
+ }
for (String kw : kws) {
if (Utilities.startsWith(kw, prefix)) {
results.add(itemFactory.createKeywordItem(kw, SPACE, anchorOffset, false));
@@ -4627,6 +4843,9 @@
kws.add(CLASS_KEYWORD);
kws.add(INTERFACE_KEYWORD);
kws.add(ENUM_KEYWORD);
+ if (isRecordSupported(env)) {
+ kws.add(RECORD_KEYWORD);
+ }
}
for (String kw : kws) {
if (Utilities.startsWith(kw, prefix)) {
diff --git a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/override.pass b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/override.pass
new file mode 100644
index 0000000..be68102
--- /dev/null
+++ b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/override.pass
@@ -0,0 +1 @@
+Override
diff --git a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/record.pass b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/record.pass
new file mode 100644
index 0000000..d4141f0
--- /dev/null
+++ b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/record.pass
@@ -0,0 +1 @@
+record
diff --git a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/typesRecordLocalMembersAndVars.pass b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/typesRecordLocalMembersAndVars.pass
new file mode 100644
index 0000000..7bc4924
--- /dev/null
+++ b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/typesRecordLocalMembersAndVars.pass
@@ -0,0 +1,8 @@
+R
+R1
+Readable
+ReflectiveOperationException
+Runnable
+Runtime
+RuntimeException
+RuntimePermission
\ No newline at end of file
diff --git a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/typesRecordStaticMembersAndVars.pass b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/typesRecordStaticMembersAndVars.pass
new file mode 100644
index 0000000..7bc4924
--- /dev/null
+++ b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/typesRecordStaticMembersAndVars.pass
@@ -0,0 +1,8 @@
+R
+R1
+Readable
+ReflectiveOperationException
+Runnable
+Runtime
+RuntimeException
+RuntimePermission
\ No newline at end of file
diff --git a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/14/typesRecordLocalMembersAndVars.pass b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/14/typesRecordLocalMembersAndVars.pass
new file mode 100644
index 0000000..6acc0ac
--- /dev/null
+++ b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/14/typesRecordLocalMembersAndVars.pass
@@ -0,0 +1,7 @@
+Readable
+Record
+ReflectiveOperationException
+Runnable
+Runtime
+RuntimeException
+RuntimePermission
\ No newline at end of file
diff --git a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/14/typesRecordStaticMembersAndVars.pass b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/14/typesRecordStaticMembersAndVars.pass
new file mode 100644
index 0000000..6acc0ac
--- /dev/null
+++ b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/14/typesRecordStaticMembersAndVars.pass
@@ -0,0 +1,7 @@
+Readable
+Record
+ReflectiveOperationException
+Runnable
+Runtime
+RuntimeException
+RuntimePermission
\ No newline at end of file
diff --git a/java/java.lsp.server/vscode/src/VerifyJDK11.java b/java/java.completion/test/unit/data/org/netbeans/modules/java/completion/data/Records.java
similarity index 76%
copy from java/java.lsp.server/vscode/src/VerifyJDK11.java
copy to java/java.completion/test/unit/data/org/netbeans/modules/java/completion/data/Records.java
index 80e5928..e1cb7ae 100644
--- a/java/java.lsp.server/vscode/src/VerifyJDK11.java
+++ b/java/java.completion/test/unit/data/org/netbeans/modules/java/completion/data/Records.java
@@ -16,9 +16,19 @@
* specific language governing permissions and limitations
* under the License.
*/
-public class VerifyJDK11 {
- public static void main(String[] args) {
- if (Runtime.version().compareTo(Runtime.Version.parse("11-build")) < 0)
- throw new IllegalStateException("Not JDK 11+.");
- }
+package test;
+
+public class Test {
+
+ public record Records <R extends Number, R1 > ( ) {
+
+ public static
}
+
+ public static void main(String args )
+ {
+ record Record1(@Ov ) {}
+
+ record Rec( ) {}
+ }
+ }
diff --git a/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaCompletionTask114FeaturesTest.java b/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaCompletionTask114FeaturesTest.java
index 1f4b212..4cdfe27 100644
--- a/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaCompletionTask114FeaturesTest.java
+++ b/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaCompletionTask114FeaturesTest.java
@@ -48,7 +48,36 @@
public void testBindingUse() throws Exception {
performTest("GenericMethodInvocation", 1231, "boolean b = argO instanceof String str && st", "BindingUse.pass", SOURCE_LEVEL);
}
+
+ public void testBeforeLeftRecordBraces() throws Exception {
+ performTest("Records", 896, null, "implementsKeyword.pass", SOURCE_LEVEL);
+ }
+
+ public void testBeforeRecParamsLeftParen() throws Exception {
+ performTest("Records", 892, null, "empty.pass", SOURCE_LEVEL);
+ }
+
+ public void testInsideRecParams() throws Exception {
+ performTest("Records", 894, "R", "typesRecordLocalMembersAndVars.pass", SOURCE_LEVEL);
+ }
+
+ public void testAfterTypeParamInRecParam() throws Exception {
+ performTest("Records", 890, null, "extendsKeyword.pass", SOURCE_LEVEL);
+ }
+
+ public void testInsideRecAfterStaticKeyWord() throws Exception {
+ performTest("Records", 918, "R", "typesRecordStaticMembersAndVars.pass", SOURCE_LEVEL);
+ }
+
+ public void testAnnotationInRecordParam() throws Exception {
+ performTest("Records", 999, null, "override.pass", SOURCE_LEVEL);
+ }
+
+ public void testRecordKeywordInsideClass() throws Exception {
+ performTest("Records", 1014, "rec", "record.pass", SOURCE_LEVEL);
+ }
+
public void noop() {
}
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 4fd3a3a..f50ccdb 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
@@ -201,7 +201,7 @@
Context resolved = resolveContext(controller, doc, offset, goToSource, false);
if (resolved == null) {
- target[0] = new GoToTarget(-1, null, null, null, false);
+ target[0] = new GoToTarget(-1, -1, null, null, null, false);
return;
}
@@ -210,7 +210,7 @@
if (url != null) {
HtmlBrowser.URLDisplayer.getDefault().showURL(url);
} else {
- target[0] = new GoToTarget(-1, null, null, null, false);
+ target[0] = new GoToTarget(-1, -1, null, null, null, false);
}
} else {
target[0] = computeGoToTarget(controller, resolved, offset);
@@ -255,20 +255,23 @@
if (startPos != (-1)) {
//check if the caret is inside the declaration itself, as jump in this case is not very usefull:
if (isCaretInsideDeclarationName(controller, tree, elpath, offset)) {
- return new GoToTarget(-1, null, null, null, false);
+ return new GoToTarget(-1, -1, null, null, null, false);
} else {
+ long endPos = controller.getTrees().getSourcePositions().getEndPosition(controller.getCompilationUnit(), tree);
//#71272: it is necessary to translate the offset:
return new GoToTarget(controller.getSnapshot().getOriginalOffset((int) startPos),
+ controller.getSnapshot().getOriginalOffset((int) endPos),
null,
null,
controller.getElementUtilities().getElementName(resolved.resolved, false).toString(),
true);
}
} else {
- return new GoToTarget(-1, null, null, null, false);
+ return new GoToTarget(-1, -1, null, null, null, false);
}
} else {
return new GoToTarget(-1,
+ -1,
controller.getClasspathInfo(),
ElementHandle.create(resolved.resolved),
controller.getElementUtilities().getElementName(resolved.resolved, false).toString(),
@@ -278,13 +281,15 @@
public static final class GoToTarget {
public final int offsetToOpen;
+ public final int endPos;
public final ClasspathInfo cpInfo;
public final ElementHandle elementToOpen;
public final String displayNameForError;
public final boolean success;
- public GoToTarget(int offsetToOpen, ClasspathInfo cpInfo, ElementHandle elementToOpen, String displayNameForError, boolean success) {
+ public GoToTarget(int offsetToOpen, int endPos, ClasspathInfo cpInfo, ElementHandle elementToOpen, String displayNameForError, boolean success) {
this.offsetToOpen = offsetToOpen;
+ this.endPos = endPos;
this.cpInfo = cpInfo;
this.elementToOpen = elementToOpen;
this.displayNameForError = displayNameForError;
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 39c1387..181e5e0 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
@@ -120,7 +120,9 @@
case ANNOTATION_TYPE:
return new AnnotationTypeItem(info, elem, type, 0, substitutionOffset, referencesCount, isDeprecated, insideNew, addSimpleName, smartType, autoImportEnclosingType, whiteList);
default:
- throw new IllegalArgumentException("kind=" + elem.getKind());
+ if(elem.getKind().name().equals(TreeShims.RECORD))
+ return new RecordItem(info, elem, type, 0, substitutionOffset, referencesCount, isDeprecated, insideNew, addSimpleName, smartType, autoImportEnclosingType, whiteList);
+ else throw new IllegalArgumentException("kind=" + elem.getKind());
}
}
@@ -1313,7 +1315,25 @@
return icon;
}
}
+
+ static class RecordItem extends ClassItem {
+ private static final String RECORD = "org/netbeans/modules/editor/resources/completion/record.png"; // NOI18N
+ private static ImageIcon icon;
+
+ private RecordItem(CompilationInfo info, TypeElement elem, DeclaredType type, int dim, int substitutionOffset, ReferencesCount referencesCount, boolean isDeprecated, boolean insideNew, boolean addSimpleName, boolean smartType, boolean autoImport, WhiteListQuery.WhiteList whiteList) {
+ super(info, elem, type, dim, substitutionOffset, referencesCount, isDeprecated, insideNew, false, addSimpleName, smartType, autoImport, whiteList);
+ }
+
+ @Override
+ protected ImageIcon getBaseIcon() {
+ if (icon == null) {
+ icon = ImageUtilities.loadImageIcon(RECORD, false);
+ }
+ return icon;
+ }
+ }
+
static class AnnotationTypeItem extends ClassItem {
private static final String ANNOTATION = "org/netbeans/modules/editor/resources/completion/annotation_type.png"; // NOI18N
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 d8867d0..af6d8cc 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
@@ -76,7 +76,7 @@
int caretOffset = context.isBackwardDelete() ? context.getOffset() - 1 : context.getOffset();
if (removedChar == '\"') {
if ((ts.token().id() == JavaTokenId.STRING_LITERAL && ts.offset() == caretOffset) ||
- (ts.token().id() == JavaTokenId.MULTILINE_STRING_LITERAL && ts.offset() <= caretOffset - 2)) {
+ (ts.token().id() == JavaTokenId.MULTILINE_STRING_LITERAL && ts.offset() == caretOffset - 2)) {
context.getDocument().remove(caretOffset, 1);
}
} else if (removedChar == '\'') {
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 3518d95..fa87409d 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
@@ -1375,6 +1375,18 @@
ctx.assertDocumentTextEquals("t(\"\"\"\n |\"\"\")");
}
+ public void testTextBlock6() throws Exception {
+ try {
+ SourceVersion.valueOf("RELEASE_13");
+ } catch (IllegalArgumentException ex) {
+ //OK, skip test:
+ return ;
+ }
+ Context ctx = new Context(new JavaKit(), "t(\"\"\"\n|\"\n\"\"\")");
+ ctx.typeChar('\f');
+ ctx.assertDocumentTextEquals("t(\"\"\"\n\n\"\"\")");
+ }
+
public void testCorrectHandlingOfStringEscapes184059() throws Exception {
assertTrue(isInsideString("foo\n\"bar|\""));
assertTrue(isInsideString("foo\n\"bar\\\"|\""));
diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/errors/AddParameterOrLocalFix.java b/java/java.hints/src/org/netbeans/modules/java/hints/errors/AddParameterOrLocalFix.java
index a6b59f0..62ce234 100644
--- a/java/java.hints/src/org/netbeans/modules/java/hints/errors/AddParameterOrLocalFix.java
+++ b/java/java.hints/src/org/netbeans/modules/java/hints/errors/AddParameterOrLocalFix.java
@@ -31,7 +31,6 @@
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePath;
import org.netbeans.api.java.source.support.ErrorAwareTreePathScanner;
-import java.io.IOException;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Set;
@@ -44,42 +43,35 @@
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
-import org.netbeans.api.java.source.Task;
import org.netbeans.api.java.source.CompilationInfo;
-import org.netbeans.api.java.source.JavaSource;
-import org.netbeans.api.java.source.JavaSource.Phase;
import org.netbeans.api.java.source.TreeMaker;
-import org.netbeans.api.java.source.TreePathHandle;
import org.netbeans.api.java.source.TreeUtilities;
import org.netbeans.api.java.source.TypeMirrorHandle;
import org.netbeans.api.java.source.WorkingCopy;
import org.netbeans.modules.java.hints.errors.ErrorFixesFakeHint.FixKind;
-import org.netbeans.spi.editor.hints.ChangeInfo;
-import org.netbeans.spi.editor.hints.Fix;
import org.openide.filesystems.FileObject;
import org.netbeans.modules.java.hints.infrastructure.ErrorHintsProvider;
import org.openide.util.NbBundle;
import static org.netbeans.modules.java.hints.errors.Utilities.isEnhancedForLoopIdentifier;
-import org.netbeans.spi.editor.hints.EnhancedFix;
+import org.netbeans.spi.java.hints.JavaFix;
/**
*
* @author Jan Lahoda
*/
-public class AddParameterOrLocalFix implements EnhancedFix {
+public class AddParameterOrLocalFix extends JavaFix {
private FileObject file;
private TypeMirrorHandle type;
private String name;
private ElementKind kind;
- private TreePathHandle[] tpHandle;
-
public AddParameterOrLocalFix(CompilationInfo info,
TypeMirror type, String name,
ElementKind kind,
int /*!!!Position*/ unresolvedVariable) {
+ super(info, info.getTreeUtilities().pathFor(unresolvedVariable + 1), getSortText(kind, name));
this.file = info.getFileObject();
if (type.getKind() == TypeKind.NULL || type.getKind() == TypeKind.NONE) {
TypeElement te = info.getElements().getTypeElement("java.lang.Object"); // NOI18N
@@ -94,10 +86,6 @@
}
this.name = name;
this.kind = kind;
-
- TreePath treePath = info.getTreeUtilities().pathFor(unresolvedVariable + 1);
- tpHandle = new TreePathHandle[1];
- tpHandle[0] = TreePathHandle.create(treePath, info);
}
public String getText() {
@@ -110,75 +98,66 @@
}
}
- public ChangeInfo implement() throws IOException {
- //use the original cp-info so it is "sure" that the proposedType can be resolved:
- JavaSource js = JavaSource.forFileObject(file);
+ @Override
+ protected void performRewrite(TransformationContext ctx) throws Exception {
+ WorkingCopy working = ctx.getWorkingCopy();
+ TypeMirror proposedType = type.resolve(working);
- js.runModificationTask(new Task<WorkingCopy>() {
- public void run(final WorkingCopy working) throws IOException {
- working.toPhase(Phase.RESOLVED);
+ if (proposedType == null) {
+ ErrorHintsProvider.LOG.log(Level.INFO, "Cannot resolve proposed type."); // NOI18N
+ return;
+ }
- TypeMirror proposedType = type.resolve(working);
+ TreeMaker make = working.getTreeMaker();
- if (proposedType == null) {
- ErrorHintsProvider.LOG.log(Level.INFO, "Cannot resolve proposed type."); // NOI18N
+ //TreePath tp = working.getTreeUtilities().pathFor(unresolvedVariable + 1);
+ //Use TreePathHandle instead of position supplied as field (#143318)
+ TreePath tp = ctx.getPath();
+ if (tp == null || tp.getLeaf().getKind() != Kind.IDENTIFIER)
+ return;
+
+ switch (kind) {
+ case PARAMETER:
+ TreePath targetPath = findMethod(tp);
+
+ if (targetPath == null) {
+ Logger.getLogger("global").log(Level.WARNING, "Add parameter - cannot find the method."); // NOI18N
return;
}
- TreeMaker make = working.getTreeMaker();
+ MethodTree targetTree = (MethodTree) targetPath.getLeaf();
- //TreePath tp = working.getTreeUtilities().pathFor(unresolvedVariable + 1);
- //Use TreePathHandle instead of position supplied as field (#143318)
- TreePath tp = tpHandle[0].resolve(working);
- if (tp == null || tp.getLeaf().getKind() != Kind.IDENTIFIER)
+ Element el = working.getTrees().getElement(targetPath);
+ if (el == null) {
return;
-
- switch (kind) {
- case PARAMETER:
- TreePath targetPath = findMethod(tp);
-
- if (targetPath == null) {
- Logger.getLogger("global").log(Level.WARNING, "Add parameter - cannot find the method."); // NOI18N
- return;
- }
-
- MethodTree targetTree = (MethodTree) targetPath.getLeaf();
-
- Element el = working.getTrees().getElement(targetPath);
- if (el == null) {
- return;
- }
- int index = targetTree.getParameters().size();
-
- if (el != null && (el.getKind() == ElementKind.METHOD || el.getKind() == ElementKind.CONSTRUCTOR)) {
- ExecutableElement ee = (ExecutableElement) el;
-
- if (ee.isVarArgs()) {
- index = ee.getParameters().size() - 1;
- }
- }
-
- MethodTree result = make.insertMethodParameter(targetTree, index, make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), name, make.Type(proposedType), null));
-
- working.rewrite(targetTree, result);
- break;
- case LOCAL_VARIABLE:
- if (ErrorFixesFakeHint.isCreateLocalVariableInPlace(ErrorFixesFakeHint.getPreferences(working.getFileObject(), FixKind.CREATE_LOCAL_VARIABLE)) || isEnhancedForLoopIdentifier(tp)) {
- resolveLocalVariable(working, tp, make, proposedType);
- } else {
- resolveLocalVariable55(working, tp, make, proposedType);
- }
- break;
- case RESOURCE_VARIABLE:
- resolveResourceVariable(working, tp, make, proposedType);
- break;
- default:
- throw new IllegalStateException(kind.name());
}
- }
- }).commit();
-
- return null;
+ int index = targetTree.getParameters().size();
+
+ if (el != null && (el.getKind() == ElementKind.METHOD || el.getKind() == ElementKind.CONSTRUCTOR)) {
+ ExecutableElement ee = (ExecutableElement) el;
+
+ if (ee.isVarArgs()) {
+ index = ee.getParameters().size() - 1;
+ }
+ }
+
+ MethodTree result = make.insertMethodParameter(targetTree, index, make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), name, make.Type(proposedType), null));
+
+ working.rewrite(targetTree, result);
+ break;
+ case LOCAL_VARIABLE:
+ if (ErrorFixesFakeHint.isCreateLocalVariableInPlace(ErrorFixesFakeHint.getPreferences(working.getFileObject(), FixKind.CREATE_LOCAL_VARIABLE)) || isEnhancedForLoopIdentifier(tp)) {
+ resolveLocalVariable(working, tp, make, proposedType);
+ } else {
+ resolveLocalVariable55(working, tp, make, proposedType);
+ }
+ break;
+ case RESOURCE_VARIABLE:
+ resolveResourceVariable(working, tp, make, proposedType);
+ break;
+ default:
+ throw new IllegalStateException(kind.name());
+ }
}
/** In case statement is an Assignment, replace it with variable declaration */
@@ -504,16 +483,15 @@
return hash;
}
- @Override
- public CharSequence getSortText() {
+ private static String getSortText(ElementKind kind, String name) {
//see usage at org.netbeans.modules.editor.hints.FixData.getSortText(org.netbeans.spi.editor.hints.Fix):java.lang.CharSequence
//creates ordering top to bottom: create resource>local variable>create field>create parameter
//see org.netbeans.modules.java.hints.errors.CreateFieldFix.getSortText():java.lang.CharSequence
switch (kind) {
- case PARAMETER: return "Create 7000 " + getText();
- case LOCAL_VARIABLE: return "Create 5000 " + getText();
- case RESOURCE_VARIABLE: return "Create 3000 " + getText();
+ case PARAMETER: return "Create 7000 " + name;
+ case LOCAL_VARIABLE: return "Create 5000 " + name;
+ case RESOURCE_VARIABLE: return "Create 3000 " + name;
default:
throw new IllegalStateException();
}
diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/errors/CreateElement.java b/java/java.hints/src/org/netbeans/modules/java/hints/errors/CreateElement.java
index 0f02b90..a9e0895 100644
--- a/java/java.hints/src/org/netbeans/modules/java/hints/errors/CreateElement.java
+++ b/java/java.hints/src/org/netbeans/modules/java/hints/errors/CreateElement.java
@@ -494,11 +494,11 @@
int identifierPos = (int) info.getTrees().getSourcePositions().getStartPosition(info.getCompilationUnit(), errorPath.getLeaf());
if (ee != null && fixTypes.contains(ElementKind.PARAMETER) && !Utilities.isMethodHeaderInsideGuardedBlock(info, (MethodTree) firstMethod.getLeaf()))
- result.add(new AddParameterOrLocalFix(info, type, simpleName, ElementKind.PARAMETER, identifierPos));
+ result.add(new AddParameterOrLocalFix(info, type, simpleName, ElementKind.PARAMETER, identifierPos).toEditorFix());
if ((firstMethod != null || firstInitializer != null) && fixTypes.contains(ElementKind.LOCAL_VARIABLE) && ErrorFixesFakeHint.enabled(ErrorFixesFakeHint.FixKind.CREATE_LOCAL_VARIABLE))
- result.add(new AddParameterOrLocalFix(info, type, simpleName, ElementKind.LOCAL_VARIABLE, identifierPos));
+ result.add(new AddParameterOrLocalFix(info, type, simpleName, ElementKind.LOCAL_VARIABLE, identifierPos).toEditorFix());
if (fixTypes.contains(ElementKind.RESOURCE_VARIABLE))
- result.add(new AddParameterOrLocalFix(info, type, simpleName, ElementKind.RESOURCE_VARIABLE, identifierPos));
+ result.add(new AddParameterOrLocalFix(info, type, simpleName, ElementKind.RESOURCE_VARIABLE, identifierPos).toEditorFix());
}
return result;
diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/errors/ImportClass.java b/java/java.hints/src/org/netbeans/modules/java/hints/errors/ImportClass.java
index 11ae366..15018a2 100644
--- a/java/java.hints/src/org/netbeans/modules/java/hints/errors/ImportClass.java
+++ b/java/java.hints/src/org/netbeans/modules/java/hints/errors/ImportClass.java
@@ -532,7 +532,7 @@
}
- static final class FixImport extends FixBase {
+ public static final class FixImport extends FixBase {
private final @NullAllowed TreePathHandle replacePathHandle;
private final @NullAllowed String suffix;
private final boolean statik;
@@ -741,5 +741,10 @@
s.save();
}
}
+
+ public ElementHandle<Element> getToImport() {
+ return toImport;
+ }
+
}
}
diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/suggestions/ExpectedTypeResolver.java b/java/java.hints/src/org/netbeans/modules/java/hints/suggestions/ExpectedTypeResolver.java
index 8bf7951..a2424e5 100644
--- a/java/java.hints/src/org/netbeans/modules/java/hints/suggestions/ExpectedTypeResolver.java
+++ b/java/java.hints/src/org/netbeans/modules/java/hints/suggestions/ExpectedTypeResolver.java
@@ -25,6 +25,7 @@
import com.sun.source.tree.AssertTree;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.BinaryTree;
+import com.sun.source.tree.BindingPatternTree;
import com.sun.source.tree.BlockTree;
import com.sun.source.tree.BreakTree;
import com.sun.source.tree.CaseTree;
@@ -67,6 +68,7 @@
import com.sun.source.tree.RequiresTree;
import com.sun.source.tree.ReturnTree;
import com.sun.source.tree.Scope;
+import com.sun.source.tree.SwitchExpressionTree;
import com.sun.source.tree.SwitchTree;
import com.sun.source.tree.SynchronizedTree;
import com.sun.source.tree.ThrowTree;
@@ -81,6 +83,7 @@
import com.sun.source.tree.VariableTree;
import com.sun.source.tree.WhileLoopTree;
import com.sun.source.tree.WildcardTree;
+import com.sun.source.tree.YieldTree;
import com.sun.source.util.SourcePositions;
import com.sun.source.util.TreePath;
import com.sun.tools.javac.code.Type.CapturedType;
@@ -1388,4 +1391,16 @@
public List<? extends TypeMirror> visitUses(UsesTree node, Object p) {
return null;
}
+
+ public List<? extends TypeMirror> visitBindingPattern(BindingPatternTree bpt, Object p) {
+ return null;
+ }
+
+ public List<? extends TypeMirror> visitSwitchExpression(SwitchExpressionTree set, Object p) {
+ return null;
+ }
+
+ public List<? extends TypeMirror> visitYield(YieldTree yt, Object p) {
+ return null;
+ }
}
diff --git a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/AddParameterOrLocalFixTest.java b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/AddParameterOrLocalFixTest.java
index 1e5d163..8c8b876 100644
--- a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/AddParameterOrLocalFixTest.java
+++ b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/AddParameterOrLocalFixTest.java
@@ -25,6 +25,7 @@
import org.netbeans.api.java.source.CompilationInfo;
import org.netbeans.modules.java.hints.errors.ErrorFixesFakeHint.FixKind;
import org.netbeans.modules.java.hints.infrastructure.ErrorHintsTestBase;
+import org.netbeans.modules.java.hints.spiimpl.JavaFixImpl;
import org.netbeans.spi.editor.hints.Fix;
/**
@@ -473,7 +474,7 @@
List<Fix> result= new LinkedList<Fix>();
for (Fix f : fixes) {
- if (f instanceof AddParameterOrLocalFix) {
+ if (f instanceof JavaFixImpl && ((JavaFixImpl) f).jf instanceof AddParameterOrLocalFix) {
result.add(f);
}
}
@@ -483,6 +484,6 @@
@Override
protected String toDebugString(CompilationInfo info, Fix f) {
- return ((AddParameterOrLocalFix) f).toDebugString(info);
+ return ((AddParameterOrLocalFix) ((JavaFixImpl) f).jf).toDebugString(info);
}
}
diff --git a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/CreateElementTest.java b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/CreateElementTest.java
index 981399d..5b54e6b 100644
--- a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/CreateElementTest.java
+++ b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/CreateElementTest.java
@@ -25,6 +25,7 @@
import java.util.Set;
import javax.swing.text.Document;
import org.netbeans.modules.java.hints.infrastructure.HintsTestBase;
+import org.netbeans.modules.java.hints.spiimpl.JavaFixImpl;
import org.netbeans.spi.editor.hints.Fix;
import org.openide.cookies.EditorCookie;
import org.openide.loaders.DataObject;
@@ -457,8 +458,8 @@
real.add(((CreateFieldFix) f).toDebugString(info));
continue;
}
- if (f instanceof AddParameterOrLocalFix) {
- real.add(((AddParameterOrLocalFix) f).toDebugString(info));
+ if (f instanceof JavaFixImpl && ((JavaFixImpl) f).jf instanceof AddParameterOrLocalFix) {
+ real.add(((AddParameterOrLocalFix) ((JavaFixImpl) f).jf).toDebugString(info));
continue;
}
if (f instanceof CreateMethodFix) {
diff --git a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/infrastructure/ErrorHintsTestBase.java b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/infrastructure/ErrorHintsTestBase.java
index 4921a3c..8f58303 100644
--- a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/infrastructure/ErrorHintsTestBase.java
+++ b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/infrastructure/ErrorHintsTestBase.java
@@ -45,6 +45,7 @@
import org.netbeans.modules.editor.java.JavaKit;
import org.netbeans.modules.java.JavaDataLoader;
import org.netbeans.modules.java.hints.spi.ErrorRule;
+import org.netbeans.modules.java.hints.spiimpl.JavaFixImpl;
import org.netbeans.modules.java.source.indexing.JavaCustomIndexer;
import org.netbeans.modules.java.source.indexing.TransactionContext;
import org.netbeans.modules.java.source.parsing.JavacParserFactory;
diff --git a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/ConvertToPatternInstanceOfTest.java b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/ConvertToPatternInstanceOfTest.java
index 14ea3d4..1982bbd 100644
--- a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/ConvertToPatternInstanceOfTest.java
+++ b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/ConvertToPatternInstanceOfTest.java
@@ -18,9 +18,9 @@
*/
package org.netbeans.modules.java.hints.jdk;
-import javax.lang.model.SourceVersion;
import org.netbeans.junit.NbTestCase;
import org.netbeans.modules.java.hints.test.api.HintTest;
+import javax.lang.model.SourceVersion;
/**
*
@@ -44,6 +44,8 @@
" return -1;\n" +
" }\n" +
"}\n")
+ .sourceLevel(SourceVersion.latest().name())
+ .options("--enable-preview")
.run(ConvertToPatternInstanceOf.class)
.findWarning("3:8-3:10:verifier:" + Bundle.ERR_ConvertToPatternInstanceOf())
.applyFix()
@@ -72,6 +74,8 @@
" }\n" +
" }\n" +
"}\n")
+ .sourceLevel(SourceVersion.latest().name())
+ .options("--enable-preview")
.run(ConvertToPatternInstanceOf.class)
.findWarning("3:8-3:10:verifier:" + Bundle.ERR_ConvertToPatternInstanceOf())
.applyFix()
@@ -99,6 +103,8 @@
" return -1;\n" +
" }\n" +
"}\n")
+ .sourceLevel(SourceVersion.latest().name())
+ .options("--enable-preview")
.run(ConvertToPatternInstanceOf.class)
.findWarning("3:8-3:10:verifier:" + Bundle.ERR_ConvertToPatternInstanceOf())
.applyFix()
@@ -125,6 +131,8 @@
" return -1;\n" +
" }\n" +
"}\n")
+ .sourceLevel(SourceVersion.latest().name())
+ .options("--enable-preview")
.run(ConvertToPatternInstanceOf.class)
.findWarning("3:8-3:10:verifier:" + Bundle.ERR_ConvertToPatternInstanceOf())
.applyFix()
@@ -140,15 +148,4 @@
"}\n");
}
- @Override
- protected void runTest() throws Throwable {
- try {
- SourceVersion.valueOf("RELEASE_14");
- } catch (IllegalArgumentException ex) {
- //OK, skip test
- return ;
- }
- super.runTest();
- }
-
}
diff --git a/java/java.lsp.server/build.xml b/java/java.lsp.server/build.xml
index 56cfe67..e70d4d1 100644
--- a/java/java.lsp.server/build.xml
+++ b/java/java.lsp.server/build.xml
@@ -80,7 +80,7 @@
<exec executable="${build.dir}/vsce/node_modules/vsce/out/vsce" failonerror="true" dir="${basedir}/vscode">
<arg value="package" />
<arg value="--out" />
- <arg value="${build.dir}/apache-netbeans-java-0.0.1.vsix" />
+ <arg value="${build.dir}/apache-netbeans-java-0.1.0.vsix" />
</exec>
<delete dir="${build.dir}/vscode" />
@@ -88,7 +88,7 @@
<delete dir="${build.dir}/vscode-mandatory" />
<mkdir dir="${build.dir}/vscode-mandatory/licenses" />
- <unzip dest="${build.dir}/vscode" src="${build.dir}/apache-netbeans-java-0.0.1.vsix" />
+ <unzip dest="${build.dir}/vscode" src="${build.dir}/apache-netbeans-java-0.1.0.vsix" />
<taskdef name="createlicensesummary" classname="org.netbeans.nbbuild.extlibs.CreateLicenseSummary" classpath="${nbantext.jar}"/>
@@ -110,6 +110,6 @@
includeAllFiles="true"
/>
- <zip destfile="${build.dir}/apache-netbeans-java-0.0.1.vsix" basedir="${build.dir}/vscode-mandatory/" update="true" />
+ <zip destfile="${build.dir}/apache-netbeans-java-0.1.0.vsix" basedir="${build.dir}/vscode-mandatory/" update="true" />
</target>
</project>
diff --git a/java/java.lsp.server/nbproject/project.xml b/java/java.lsp.server/nbproject/project.xml
index c5277b7..af6a370 100644
--- a/java/java.lsp.server/nbproject/project.xml
+++ b/java/java.lsp.server/nbproject/project.xml
@@ -323,6 +323,11 @@
<compile-dependency/>
</test-dependency>
<test-dependency>
+ <code-name-base>org.netbeans.modules.java.source.base</code-name-base>
+ <compile-dependency/>
+ <test/>
+ </test-dependency>
+ <test-dependency>
<code-name-base>org.netbeans.modules.lexer.nbbridge</code-name-base>
</test-dependency>
<test-dependency>
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Server.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Server.java
index 218ccf8..de4455a 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Server.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Server.java
@@ -18,6 +18,7 @@
*/
package org.netbeans.modules.java.lsp.server;
+import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -114,15 +115,7 @@
LanguageServerImpl server = new LanguageServerImpl();
Launcher<LanguageClient> serverLauncher = LSPLauncher.createServerLauncher(server, in, out);
((LanguageClientAware) server).connect(serverLauncher.getRemoteProxy());
- serverLauncher.startListening();
-
- while (true) {
- try {
- Thread.sleep(100000);
- } catch (InterruptedException ex) {
- //ignore
- }
- }
+ serverLauncher.startListening().get();
}
private static class LanguageServerImpl implements LanguageServer, LanguageClientAware {
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/text/TextDocumentServiceImpl.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/text/TextDocumentServiceImpl.java
index 583fd2e..b63c19b 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/text/TextDocumentServiceImpl.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/text/TextDocumentServiceImpl.java
@@ -36,11 +36,15 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.io.UncheckedIOException;
import java.net.MalformedURLException;
import java.net.URI;
+import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
@@ -97,6 +101,7 @@
import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.Hover;
+import org.eclipse.lsp4j.InsertTextFormat;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.MarkupContent;
import org.eclipse.lsp4j.MessageParams;
@@ -123,10 +128,15 @@
import org.netbeans.api.editor.document.LineDocumentUtils;
import org.netbeans.api.java.source.CompilationController;
import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.CompilationInfo.CacheClearPolicy;
import org.netbeans.api.java.source.ElementHandle;
+import org.netbeans.api.java.source.GeneratorUtilities;
import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.java.source.ModificationResult;
import org.netbeans.api.java.source.SourceUtils;
+import org.netbeans.api.java.source.Task;
+import org.netbeans.api.java.source.TreePathHandle;
+import org.netbeans.api.java.source.WorkingCopy;
import org.netbeans.api.java.source.support.ReferencesCount;
import org.netbeans.api.java.source.ui.ElementJavadoc;
import org.netbeans.api.java.source.ui.ElementOpen;
@@ -141,6 +151,7 @@
import org.netbeans.modules.java.completion.JavaDocumentationTask;
import org.netbeans.modules.java.editor.base.semantic.MarkOccurrencesHighlighterBase;
import org.netbeans.modules.java.editor.options.MarkOccurencesSettings;
+import org.netbeans.modules.java.hints.errors.ImportClass;
import org.netbeans.modules.java.hints.infrastructure.CreatorBasedLazyFixList;
import org.netbeans.modules.java.hints.infrastructure.ErrorHintsProvider;
import org.netbeans.modules.java.hints.spiimpl.JavaFixImpl;
@@ -154,6 +165,7 @@
import org.netbeans.modules.parsing.api.UserTask;
import org.netbeans.modules.parsing.spi.ParseException;
import org.netbeans.modules.parsing.spi.SchedulerEvent;
+import org.netbeans.spi.editor.hints.EnhancedFix;
import org.netbeans.spi.editor.hints.ErrorDescription;
import org.netbeans.spi.editor.hints.Fix;
import org.netbeans.spi.editor.hints.LazyFixList;
@@ -190,7 +202,7 @@
EditorCookie ec = file.getLookup().lookup(EditorCookie.class);
Document doc = ec.openDocument();
int caret = getOffset(doc, params.getPosition());
- JavaCompletionTask<CompletionItem> task = JavaCompletionTask.create(caret, new ItemFactoryImpl(uri), EnumSet.noneOf(Options.class), () -> false);
+ JavaCompletionTask<CompletionItem> task = JavaCompletionTask.create(caret, new ItemFactoryImpl(client, uri), EnumSet.noneOf(Options.class), () -> false);
ParserManager.parse(Collections.singletonList(Source.create(doc)), task);
List<CompletionItem> result = task.getResults();
for (Iterator<CompletionItem> it = result.iterator(); it.hasNext();) {
@@ -202,8 +214,8 @@
return CompletableFuture.completedFuture(Either.<List<CompletionItem>, CompletionList>forRight(new CompletionList(result)));
} catch (IOException | ParseException ex) {
throw new IllegalStateException(ex);
+ }
}
- }
public static final class CompletionData {
public String uri;
@@ -233,9 +245,11 @@
private static class ItemFactoryImpl implements JavaCompletionTask.ItemFactory<CompletionItem> {
+ private final LanguageClient client;
private final String uri;
- public ItemFactoryImpl(String uri) {
+ public ItemFactoryImpl(LanguageClient client, String uri) {
+ this.client = client;
this.uri = uri;
}
@@ -255,7 +269,9 @@
@Override
public CompletionItem createPackageItem(String pkgFQN, int substitutionOffset, boolean inPackageStatement) {
- return null; //TODO: fill
+ CompletionItem item = new CompletionItem(pkgFQN.substring(pkgFQN.lastIndexOf('.') + 1));
+ item.setKind(CompletionItemKind.Folder);
+ return item;
}
@Override
@@ -322,7 +338,14 @@
label.append(Utilities.getTypeName(info, retType, false).toString());
CompletionItem item = new CompletionItem(label.toString());
item.setKind(elementKind2CompletionItemKind(elem.getKind()));
- item.setInsertText(elem.getSimpleName().toString());
+ StringBuilder insertText = new StringBuilder();
+ insertText.append(elem.getSimpleName());
+ insertText.append("(");
+ if (elem.getParameters().isEmpty()) {
+ insertText.append(")");
+ }
+ item.setInsertText(insertText.toString());
+ item.setInsertTextFormat(InsertTextFormat.PlainText);
setCompletionData(item, elem);
return item;
}
@@ -373,9 +396,36 @@
return null; //TODO: fill
}
+ private static final Object KEY_IMPORT_TEXT_EDITS = new Object();
+
@Override
public CompletionItem createStaticMemberItem(CompilationInfo info, DeclaredType type, Element memberElem, TypeMirror memberType, boolean multipleVersions, int substitutionOffset, boolean isDeprecated, boolean addSemicolon) {
- return null; //TODO: fill
+ //TODO: prefer static imports (but would be much slower?)
+ //TODO: should be resolveImport instead of addImports:
+ Map<Element, List<TextEdit>> imports = (Map<Element, List<TextEdit>>) info.getCachedValue(KEY_IMPORT_TEXT_EDITS);
+ if (imports == null) {
+ info.putCachedValue(KEY_IMPORT_TEXT_EDITS, imports = new HashMap<>(), CacheClearPolicy.ON_TASK_END);
+ }
+ List<TextEdit> currentClassImport = imports.computeIfAbsent(type.asElement(), toImport -> {
+ try {
+ return modify2TextEdits(JavaSource.forFileObject(info.getFileObject()), wc -> {
+ wc.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
+ wc.rewrite(info.getCompilationUnit(), GeneratorUtilities.get(wc).addImports(wc.getCompilationUnit(), new HashSet<>(Arrays.asList(toImport))));
+ });
+ } catch (IOException ex) {
+ //TODO: include stack trace:
+ client.logMessage(new MessageParams(MessageType.Error, ex.getMessage()));
+ return Collections.emptyList();
+ }
+ });
+ String label = type.asElement().getSimpleName() + "." + memberElem.getSimpleName();
+ CompletionItem item = new CompletionItem(label);
+ item.setKind(elementKind2CompletionItemKind(memberElem.getKind()));
+ item.setInsertText(label);
+ item.setInsertTextFormat(InsertTextFormat.PlainText);
+ item.setAdditionalTextEdits(currentClassImport);
+ setCompletionData(item, memberElem);
+ return item;
}
@Override
@@ -501,7 +551,7 @@
public CompletableFuture<List<? extends Location>> definition(TextDocumentPositionParams params) {
JavaSource js = getSource(params.getTextDocument().getUri());
GoToTarget[] target = new GoToTarget[1];
- LineMap[] lm = new LineMap[1];
+ LineMap[] thisFileLineMap = new LineMap[1];
try {
js.runUserActionTask(cc -> {
cc.toPhase(JavaSource.Phase.RESOLVED);
@@ -512,7 +562,7 @@
return ;
}
target[0] = GoToSupport.computeGoToTarget(cc, context, offset);
- lm[0] = cc.getCompilationUnit().getLineMap();
+ thisFileLineMap[0] = cc.getCompilationUnit().getLineMap();
}, true);
} catch (IOException ex) {
//TODO: include stack trace:
@@ -524,18 +574,21 @@
if (target[0] != null && target[0].success) {
if (target[0].offsetToOpen < 0) {
Object[] openInfo = ElementOpenAccessor.getInstance().getOpenInfo(target[0].cpInfo, target[0].elementToOpen, new AtomicBoolean());
- if (openInfo != null) {
+ if (openInfo != null && (int) openInfo[1] != (-1) && (int) openInfo[2] != (-1) && openInfo[3] != null) {
FileObject file = (FileObject) openInfo[0];
int start = (int) openInfo[1];
int end = (int) openInfo[2];
+ LineMap lm = (LineMap) openInfo[3];
result.add(new Location(toUri(file),
- new Range(createPosition(lm[0], start),
- createPosition(lm[0], end))));
+ new Range(createPosition(lm, start),
+ createPosition(lm, end))));
}
} else {
- Position pos = createPosition(js.getFileObjects().iterator().next(), target[0].offsetToOpen);
+ int start = target[0].offsetToOpen;
+ int end = target[0].endPos;
result.add(new Location(params.getTextDocument().getUri(),
- new Range(pos, pos)));
+ new Range(createPosition(thisFileLineMap[0], start),
+ createPosition(thisFileLineMap[0], end))));
}
}
return CompletableFuture.completedFuture(result);
@@ -684,6 +737,7 @@
continue;
}
+ TreePathHandle[] topLevelHandle = new TreePathHandle[1];
LazyFixList lfl = err.getFixes();
if (lfl instanceof CreatorBasedLazyFixList) {
@@ -691,36 +745,51 @@
js.runUserActionTask(cc -> {
cc.toPhase(JavaSource.Phase.RESOLVED);
((CreatorBasedLazyFixList) lfl).compute(cc, new AtomicBoolean());
+ topLevelHandle[0] = TreePathHandle.create(new TreePath(cc.getCompilationUnit()), cc);
}, true);
} catch (IOException ex) {
//TODO: include stack trace:
client.logMessage(new MessageParams(MessageType.Error, ex.getMessage()));
}
}
- List<Fix> fixes = lfl.getFixes();
+ List<Fix> fixes = sortFixes(lfl.getFixes());
//TODO: ordering
for (Fix f : fixes) {
+ if (f instanceof ImportClass.FixImport) {
+ //TODO: FixImport is not a JavaFix, create one. Is there a better solution?
+ String text = f.getText();
+ CharSequence sortText = ((ImportClass.FixImport) f).getSortText();
+ ElementHandle<Element> toImport = ((ImportClass.FixImport) f).getToImport();
+ f = new JavaFix(topLevelHandle[0], sortText != null ? sortText.toString() : null) {
+ @Override
+ protected String getText() {
+ return text;
+ }
+ @Override
+ protected void performRewrite(JavaFix.TransformationContext ctx) throws Exception {
+ Element resolved = toImport.resolve(ctx.getWorkingCopy());
+ if (resolved == null) {
+ return ;
+ }
+ WorkingCopy copy = ctx.getWorkingCopy();
+ CompilationUnitTree cut = GeneratorUtilities.get(copy).addImports(
+ copy.getCompilationUnit(),
+ Collections.singleton(resolved)
+ );
+ copy.rewrite(copy.getCompilationUnit(), cut);
+ }
+ }.toEditorFix();
+ }
if (f instanceof JavaFixImpl) {
try {
- LineMap[] lm = new LineMap[1];
- ModificationResult changes = js.runModificationTask(wc -> {
+ JavaFix jf = ((JavaFixImpl) f).jf;
+ List<TextEdit> edits = modify2TextEdits(js, wc -> {
wc.toPhase(JavaSource.Phase.RESOLVED);
Map<FileObject, byte[]> resourceContentChanges = new HashMap<FileObject, byte[]>();
- JavaFix jf = ((JavaFixImpl) f).jf;
JavaFixImpl.Accessor.INSTANCE.process(jf, wc, true, resourceContentChanges, /*Ignored in editor:*/new ArrayList<>());
- lm[0] = wc.getCompilationUnit().getLineMap();
});
- //TODO: full, correct and safe edit production:
- List<? extends ModificationResult.Difference> diffs = changes.getDifferences(changes.getModifiedFileObjects().iterator().next());
- List<TextEdit> edits = new ArrayList<>();
- for (ModificationResult.Difference diff : diffs) {
- String newText = diff.getNewText();
- edits.add(new TextEdit(new Range(createPosition(lm[0], diff.getStartPosition().getOffset()),
- createPosition(lm[0], diff.getEndPosition().getOffset())),
- newText != null ? newText : ""));
- }
TextDocumentEdit te = new TextDocumentEdit(new VersionedTextDocumentIdentifier(params.getTextDocument().getUri(),
-1),
edits);
@@ -740,6 +809,43 @@
return CompletableFuture.completedFuture(result);
}
+ //TODO: copied from spi.editor.hints/.../FixData:
+ private List<Fix> sortFixes(Collection<Fix> fixes) {
+ List<Fix> result = new ArrayList<Fix>(fixes);
+
+ Collections.sort(result, new FixComparator());
+
+ return result;
+ }
+
+ private static final String DEFAULT_SORT_TEXT = "\uFFFF";
+
+ private static CharSequence getSortText(Fix f) {
+ if (f instanceof EnhancedFix) {
+ return ((EnhancedFix) f).getSortText();
+ } else {
+ return DEFAULT_SORT_TEXT;
+ }
+ }
+ private static final class FixComparator implements Comparator<Fix> {
+ public int compare(Fix o1, Fix o2) {
+ return compareText(getSortText(o1), getSortText(o2));
+ }
+ }
+
+ private static int compareText(CharSequence text1, CharSequence text2) {
+ int len = Math.min(text1.length(), text2.length());
+ for (int i = 0; i < len; i++) {
+ char ch1 = text1.charAt(i);
+ char ch2 = text2.charAt(i);
+ if (ch1 != ch2) {
+ return ch1 - ch2;
+ }
+ }
+ return text1.length() - text2.length();
+ }
+ //end copied
+
@Override
public CompletableFuture<List<? extends CodeLens>> codeLens(CodeLensParams arg0) {
throw new UnsupportedOperationException("Not supported yet.");
@@ -944,7 +1050,15 @@
private static String toUri(FileObject file) {
if (FileUtil.isArchiveArtifact(file)) {
//VS code cannot open jar:file: URLs, workaround:
- File cacheDir = Places.getCacheSubfile("java-server");
+ //another workaround, should be:
+ //File cacheDir = Places.getCacheSubfile("java-server");
+ //but that locks up VS Code, using a temp directory:
+ File cacheDir;
+ try {
+ cacheDir = Files.createTempDirectory("nb-java-lsp-server").toFile();
+ } catch (IOException ex) {
+ throw new UncheckedIOException(ex);
+ }
File segments = new File(cacheDir, "segments");
Properties props = new Properties();
@@ -1015,4 +1129,26 @@
return URLMapper.findFileObject(URI.create(uri).toURL());
}
+ private static List<TextEdit> modify2TextEdits(JavaSource js, Task<WorkingCopy> task) throws IOException {
+ FileObject[] file = new FileObject[1];
+ LineMap[] lm = new LineMap[1];
+ ModificationResult changes = js.runModificationTask(wc -> {
+ task.run(wc);
+ file[0] = wc.getFileObject();
+ lm[0] = wc.getCompilationUnit().getLineMap();
+ });
+ //TODO: full, correct and safe edit production:
+ List<? extends ModificationResult.Difference> diffs = changes.getDifferences(file[0]);
+ if (diffs == null) {
+ return Collections.emptyList();
+ }
+ List<TextEdit> edits = new ArrayList<>();
+ for (ModificationResult.Difference diff : diffs) {
+ String newText = diff.getNewText();
+ edits.add(new TextEdit(new Range(createPosition(lm[0], diff.getStartPosition().getOffset()),
+ createPosition(lm[0], diff.getEndPosition().getOffset())),
+ newText != null ? newText : ""));
+ }
+ return edits;
+ }
}
diff --git a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/ServerTest.java b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/ServerTest.java
index d785465..d3a3731 100644
--- a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/ServerTest.java
+++ b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/ServerTest.java
@@ -20,6 +20,7 @@
import java.io.File;
import java.io.FileWriter;
+import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.InetAddress;
@@ -28,21 +29,21 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
-import junit.framework.Test;
-import junit.framework.TestCase;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionContext;
import org.eclipse.lsp4j.CodeActionParams;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.CompletionItem;
+import org.eclipse.lsp4j.CompletionItemKind;
import org.eclipse.lsp4j.CompletionList;
import org.eclipse.lsp4j.CompletionParams;
import org.eclipse.lsp4j.Diagnostic;
@@ -54,6 +55,7 @@
import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.InitializeParams;
import org.eclipse.lsp4j.InitializeResult;
+import org.eclipse.lsp4j.InsertTextFormat;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.MessageActionItem;
import org.eclipse.lsp4j.MessageParams;
@@ -74,19 +76,27 @@
import org.eclipse.lsp4j.launch.LSPLauncher;
import org.eclipse.lsp4j.services.LanguageClient;
import org.eclipse.lsp4j.services.LanguageServer;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.classpath.GlobalPathRegistry;
import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.project.Project;
import org.netbeans.api.sendopts.CommandLine;
-import org.netbeans.junit.NbModuleSuite;
import org.netbeans.junit.NbTestCase;
-import org.netbeans.modules.java.source.parsing.ParameterNameProviderImpl;
+import org.netbeans.modules.java.source.BootClassPathUtil;
import org.netbeans.modules.parsing.impl.indexing.implspi.CacheFolderProvider;
+import org.netbeans.spi.java.classpath.ClassPathProvider;
+import org.netbeans.spi.java.classpath.support.ClassPathSupport;
+import org.netbeans.spi.project.ProjectFactory;
+import org.netbeans.spi.project.ProjectState;
+import org.netbeans.spi.project.ui.ProjectOpenedHook;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.modules.ModuleInfo;
import org.openide.modules.Places;
-import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.Utilities;
+import org.openide.util.lookup.Lookups;
+import org.openide.util.lookup.ServiceProvider;
/**
*
@@ -219,7 +229,7 @@
assertTrue(actualItems.contains("Keyword:interface"));
server.getTextDocumentService().didChange(new DidChangeTextDocumentParams(id, Arrays.asList(new TextDocumentContentChangeEvent(new Range(new Position(0, hashCodeStart), new Position(0, hashCodeStart + "equ".length())), "equ".length(), "hashCode"))));
int closingBrace = code.lastIndexOf("}");
- server.getTextDocumentService().didChange(new DidChangeTextDocumentParams(id, Arrays.asList(new TextDocumentContentChangeEvent(new Range(new Position(0, closingBrace), new Position(0, closingBrace)), 0, "private String c(Object o) {\nreturn o;\n}"))));
+ server.getTextDocumentService().didChange(new DidChangeTextDocumentParams(id, Arrays.asList(new TextDocumentContentChangeEvent(new Range(new Position(0, closingBrace), new Position(0, closingBrace)), 0, "public String c(Object o) {\nreturn o;\n}"))));
List<Diagnostic> diagnostics = assertDiags(diags, "Error:1:0-1:9"); //errors
assertDiags(diags, "Error:1:0-1:9");//hints
List<Either<Command, CodeAction>> codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(1, 0), new Position(1, 9)), new CodeActionContext(Arrays.asList(diagnostics.get(0))))).get();
@@ -236,7 +246,7 @@
assertEquals(1, edit.getRange().getEnd().getLine());
assertEquals(7, edit.getRange().getEnd().getCharacter());
assertEquals("(String) ", edit.getNewText());
- server.getTextDocumentService().didChange(new DidChangeTextDocumentParams(id, Arrays.asList(new TextDocumentContentChangeEvent(new Range(new Position(0, closingBrace), new Position(0, closingBrace)), 0, "private void assignToSelf(Object o) { o = o; }"))));
+ server.getTextDocumentService().didChange(new DidChangeTextDocumentParams(id, Arrays.asList(new TextDocumentContentChangeEvent(new Range(new Position(0, closingBrace), new Position(0, closingBrace)), 0, "public void assignToSelf(Object o) { o = o; }"))));
assertDiags(diags, "Error:1:0-1:9");//errors
assertDiags(diags, "Error:1:0-1:9", "Warning:0:148-0:153", "Warning:0:152-0:153");//hints
}
@@ -244,7 +254,7 @@
public void testCodeActionWithRemoval() throws Exception {
File src = new File(getWorkDir(), "Test.java");
src.getParentFile().mkdirs();
- String code = "public class Test { private String c(String s) {\nreturn s.toString();\n} }";
+ String code = "public class Test { public String c(String s) {\nreturn s.toString();\n} }";
try (Writer w = new FileWriter(src)) {
w.write(code);
}
@@ -452,11 +462,19 @@
" public void method(int ppp) {\n" +
" System.err.println(field);\n" +
" System.err.println(ppp);\n" +
+ " new Other().test();\n" +
" }\n" +
"}\n";
try (Writer w = new FileWriter(src)) {
w.write(code);
}
+ File otherSrc = new File(getWorkDir(), "Other.java");
+ try (Writer w = new FileWriter(otherSrc)) {
+ w.write("/**Some source*/\n" +
+ "public class Other {\n" +
+ " public void test() { }\n" +
+ "}");
+ }
FileUtil.refreshFor(getWorkDir());
Launcher<LanguageServer> serverLauncher = LSPLauncher.createClientLauncher(new LanguageClient() {
@Override
@@ -493,13 +511,24 @@
assertEquals(src.toURI().toString(), definition.get(0).getUri());
assertEquals(1, definition.get(0).getRange().getStart().getLine());
assertEquals(4, definition.get(0).getRange().getStart().getCharacter());
+ assertEquals(1, definition.get(0).getRange().getEnd().getLine());
+ assertEquals(22, definition.get(0).getRange().getEnd().getCharacter());
pos = new Position(4, 30);
definition = server.getTextDocumentService().definition(new TextDocumentPositionParams(new TextDocumentIdentifier(src.toURI().toString()), pos)).get();
assertEquals(1, definition.size());
assertEquals(src.toURI().toString(), definition.get(0).getUri());
assertEquals(2, definition.get(0).getRange().getStart().getLine());
assertEquals(23, definition.get(0).getRange().getStart().getCharacter());
- //XXX: test jump to another file!
+ assertEquals(2, definition.get(0).getRange().getEnd().getLine());
+ assertEquals(30, definition.get(0).getRange().getEnd().getCharacter());
+ pos = new Position(5, 22);
+ definition = server.getTextDocumentService().definition(new TextDocumentPositionParams(new TextDocumentIdentifier(src.toURI().toString()), pos)).get();
+ assertEquals(1, definition.size());
+ assertEquals(otherSrc.toURI().toString(), definition.get(0).getUri());
+ assertEquals(2, definition.get(0).getRange().getStart().getLine());
+ assertEquals(4, definition.get(0).getRange().getStart().getCharacter());
+ assertEquals(2, definition.get(0).getRange().getEnd().getLine());
+ assertEquals(26, definition.get(0).getRange().getEnd().getCharacter());
}
public void testOpenProjectOpenJDK() throws Exception {
@@ -623,6 +652,202 @@
"<none>:1:26-1:29", "<none>:2:12-2:15", "<none>:3:17-3:20");
}
+ public void testAdvancedCompletion1() throws Exception {
+ File src = new File(getWorkDir(), "Test.java");
+ src.getParentFile().mkdirs();
+ try (Writer w = new FileWriter(new File(src.getParentFile(), ".test-project"))) {}
+ String code = "public class Test {\n" +
+ " private void t(String s) {\n" +
+ " \n" +
+ " }\n" +
+ "}\n";
+ try (Writer w = new FileWriter(src)) {
+ w.write(code);
+ }
+ List<Diagnostic>[] diags = new List[1];
+ CountDownLatch indexingComplete = new CountDownLatch(1);
+ Launcher<LanguageServer> serverLauncher = LSPLauncher.createClientLauncher(new LanguageClient() {
+ @Override
+ public void telemetryEvent(Object arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void publishDiagnostics(PublishDiagnosticsParams params) {
+ synchronized (diags) {
+ diags[0] = params.getDiagnostics();
+ diags.notifyAll();
+ }
+ }
+
+ @Override
+ public void showMessage(MessageParams params) {
+ if (Server.INDEXING_COMPLETED.equals(params.getMessage())) {
+ indexingComplete.countDown();
+ } else {
+ throw new UnsupportedOperationException("Unexpected message.");
+ }
+ }
+
+ @Override
+ public CompletableFuture<MessageActionItem> showMessageRequest(ShowMessageRequestParams arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void logMessage(MessageParams arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+ }, client.getInputStream(), client.getOutputStream());
+ serverLauncher.startListening();
+ LanguageServer server = serverLauncher.getRemoteProxy();
+ InitializeParams initParams = new InitializeParams();
+ initParams.setRootUri(getWorkDir().toURI().toString());
+ InitializeResult result = server.initialize(initParams).get();
+ indexingComplete.await();
+ server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(src.toURI().toString(), "java", 0, code)));
+
+ {
+ VersionedTextDocumentIdentifier id1 = new VersionedTextDocumentIdentifier(src.toURI().toString(), 1);
+ server.getTextDocumentService().didChange(new DidChangeTextDocumentParams(id1, Arrays.asList(new TextDocumentContentChangeEvent(new Range(new Position(2, 8), new Position(2, 8)), 0, "s."))));
+
+ Either<List<CompletionItem>, CompletionList> completion = server.getTextDocumentService().completion(new CompletionParams(new TextDocumentIdentifier(src.toURI().toString()), new Position(2, 8 + "s.".length()))).get();
+ assertTrue(completion.isRight());
+ Optional<CompletionItem> lengthItem = completion.getRight().getItems().stream().filter(ci -> "length() : int".equals(ci.getLabel())).findAny();
+ assertTrue(lengthItem.isPresent());
+ assertEquals(InsertTextFormat.PlainText, lengthItem.get().getInsertTextFormat());
+ assertEquals("length()", lengthItem.get().getInsertText());
+ Optional<CompletionItem> substringItem = completion.getRight().getItems().stream().filter(ci -> ci.getLabel().startsWith("substring(") && ci.getLabel().contains(",")).findAny();
+ assertTrue(substringItem.isPresent());
+ assertEquals(InsertTextFormat.PlainText, substringItem.get().getInsertTextFormat());
+ assertEquals("substring(", substringItem.get().getInsertText());
+ }
+
+ {
+ VersionedTextDocumentIdentifier id2 = new VersionedTextDocumentIdentifier(src.toURI().toString(), 1);
+ server.getTextDocumentService().didChange(new DidChangeTextDocumentParams(id2, Arrays.asList(new TextDocumentContentChangeEvent(new Range(new Position(1, 1), new Position(1, 1)), 0, "@java.lang."))));
+
+ Position afterJavaLang = new Position(1, 1 + "@java.lang.".length());
+
+ {
+ Either<List<CompletionItem>, CompletionList> completion = server.getTextDocumentService().completion(new CompletionParams(new TextDocumentIdentifier(src.toURI().toString()), afterJavaLang)).get();
+ assertTrue(completion.isRight());
+ Optional<CompletionItem> annotationItem = completion.getRight().getItems().stream().filter(ci -> "annotation".equals(ci.getLabel())).findAny();
+ assertTrue(annotationItem.isPresent());
+ assertEquals("annotation", annotationItem.get().getLabel());
+ assertEquals(CompletionItemKind.Folder, annotationItem.get().getKind());
+ }
+
+ server.getTextDocumentService().didChange(new DidChangeTextDocumentParams(id2, Arrays.asList(new TextDocumentContentChangeEvent(new Range(afterJavaLang, afterJavaLang), 0, "annotation."))));
+
+ Position afterJavaLangAnnotation = new Position(1, afterJavaLang.getCharacter() + "annotation.".length());
+
+ {
+ Either<List<CompletionItem>, CompletionList> completion = server.getTextDocumentService().completion(new CompletionParams(new TextDocumentIdentifier(src.toURI().toString()), afterJavaLangAnnotation)).get();
+ assertTrue(completion.isRight());
+ completion.getRight().getItems().stream().forEach(ci -> System.err.println(ci.getLabel()));
+ Optional<CompletionItem> targetItem = completion.getRight().getItems().stream().filter(ci -> "Target".equals(ci.getLabel())).findAny();
+ assertTrue(targetItem.isPresent());
+ assertEquals("Target", targetItem.get().getLabel()); //TODO: insert text '('!
+ assertEquals(CompletionItemKind.Interface, targetItem.get().getKind());
+ }
+
+ server.getTextDocumentService().didChange(new DidChangeTextDocumentParams(id2, Arrays.asList(new TextDocumentContentChangeEvent(new Range(afterJavaLangAnnotation, afterJavaLangAnnotation), 0, "Target("))));
+
+ Position afterTarget = new Position(1, afterJavaLangAnnotation.getCharacter() + "Target(".length());
+
+ {
+ Either<List<CompletionItem>, CompletionList> completion = server.getTextDocumentService().completion(new CompletionParams(new TextDocumentIdentifier(src.toURI().toString()), afterTarget)).get();
+ assertTrue(completion.isRight());
+ completion.getRight().getItems().stream().forEach(ci -> System.err.println(ci.getLabel()));
+ Optional<CompletionItem> methodItem = completion.getRight().getItems().stream().filter(ci -> "ElementType.METHOD".equals(ci.getLabel())).findAny();
+ assertTrue(methodItem.isPresent());
+ assertEquals(InsertTextFormat.PlainText, methodItem.get().getInsertTextFormat());
+ assertEquals("ElementType.METHOD", methodItem.get().getInsertText());
+ assertEquals(1, methodItem.get().getAdditionalTextEdits().size());
+ assertEquals(0, methodItem.get().getAdditionalTextEdits().get(0).getRange().getStart().getLine());
+ assertEquals(0, methodItem.get().getAdditionalTextEdits().get(0).getRange().getStart().getCharacter());
+ assertEquals(0, methodItem.get().getAdditionalTextEdits().get(0).getRange().getEnd().getLine());
+ assertEquals(0, methodItem.get().getAdditionalTextEdits().get(0).getRange().getEnd().getCharacter());
+ assertEquals("\nimport java.lang.annotation.ElementType;\n\n", methodItem.get().getAdditionalTextEdits().get(0).getNewText());
+ }
+
+ server.getTextDocumentService().didChange(new DidChangeTextDocumentParams(id2, Arrays.asList(new TextDocumentContentChangeEvent(new Range(new Position(0, 0), new Position(0, 0)), 0, "import java.lang.annotation.ElementType;"))));
+
+ {
+ //import already exists:
+ Either<List<CompletionItem>, CompletionList> completion = server.getTextDocumentService().completion(new CompletionParams(new TextDocumentIdentifier(src.toURI().toString()), afterTarget)).get();
+ assertTrue(completion.isRight());
+ completion.getRight().getItems().stream().forEach(ci -> System.err.println(ci.getLabel()));
+ Optional<CompletionItem> methodItem = completion.getRight().getItems().stream().filter(ci -> "ElementType.METHOD".equals(ci.getLabel())).findAny();
+ assertTrue(methodItem.isPresent());
+ assertEquals(InsertTextFormat.PlainText, methodItem.get().getInsertTextFormat());
+ assertEquals("ElementType.METHOD", methodItem.get().getInsertText());
+ assertEquals(0, methodItem.get().getAdditionalTextEdits().size());
+ }
+ }
+ }
+
+ public void testFixImports() throws Exception {
+ File src = new File(getWorkDir(), "Test.java");
+ src.getParentFile().mkdirs();
+ try (Writer w = new FileWriter(new File(src.getParentFile(), ".test-project"))) {}
+ String code = "public class Test {\n" +
+ " private void t() {\n" +
+ " List l;\n" +
+ " }\n" +
+ "}\n";
+ try (Writer w = new FileWriter(src)) {
+ w.write(code);
+ }
+ List<Diagnostic>[] diags = new List[1];
+ CountDownLatch indexingComplete = new CountDownLatch(1);
+ Launcher<LanguageServer> serverLauncher = LSPLauncher.createClientLauncher(new LanguageClient() {
+ @Override
+ public void telemetryEvent(Object arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void publishDiagnostics(PublishDiagnosticsParams params) {
+ synchronized (diags) {
+ diags[0] = params.getDiagnostics();
+ diags.notifyAll();
+ }
+ }
+
+ @Override
+ public void showMessage(MessageParams params) {
+ if (Server.INDEXING_COMPLETED.equals(params.getMessage())) {
+ indexingComplete.countDown();
+ } else {
+ throw new UnsupportedOperationException("Unexpected message.");
+ }
+ }
+
+ @Override
+ public CompletableFuture<MessageActionItem> showMessageRequest(ShowMessageRequestParams arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void logMessage(MessageParams arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+ }, client.getInputStream(), client.getOutputStream());
+ serverLauncher.startListening();
+ LanguageServer server = serverLauncher.getRemoteProxy();
+ InitializeParams initParams = new InitializeParams();
+ initParams.setRootUri(getWorkDir().toURI().toString());
+ InitializeResult result = server.initialize(initParams).get();
+ indexingComplete.await();
+ server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(src.toURI().toString(), "java", 0, code)));
+
+ Diagnostic unresolvable = assertDiags(diags, "Error:2:8-2:12").get(0);
+ List<Either<Command, CodeAction>> codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(new TextDocumentIdentifier(src.toURI().toString()), unresolvable.getRange(), new CodeActionContext(Arrays.asList(unresolvable)))).get();
+ assertEquals(2, codeActions.size());
+ }
+
private void assertHighlights(List<? extends DocumentHighlight> highlights, String... expected) {
Set<String> stringHighlights = new HashSet<>();
for (DocumentHighlight h : highlights) {
@@ -634,4 +859,79 @@
assertEquals(new HashSet<>(Arrays.asList(expected)),
stringHighlights);
}
+
+ //make sure files can access other files in the same directory:
+ @ServiceProvider(service=ClassPathProvider.class, position=100)
+ public static final class ClassPathProviderImpl implements ClassPathProvider {
+
+ @Override
+ public ClassPath findClassPath(FileObject file, String type) {
+ if (ClassPath.SOURCE.equals(type) && file.isData()) {
+ return ClassPathSupport.createClassPath(file.getParent());
+ }
+ if (ClassPath.BOOT.equals(type)) {
+ return BootClassPathUtil.getBootClassPath();
+ }
+ return null;
+ }
+
+ }
+
+ //tests may run as a project, so that indexing works properly:
+ @ServiceProvider(service=ProjectFactory.class)
+ public static class TestProjectFactory implements ProjectFactory {
+
+ @Override
+ public boolean isProject(FileObject projectDirectory) {
+ return projectDirectory.getFileObject(".test-project") != null;
+ }
+
+ @Override
+ public Project loadProject(FileObject projectDirectory, ProjectState state) throws IOException {
+ if (isProject(projectDirectory)) {
+ ClassPath source = ClassPathSupport.createClassPath(projectDirectory);
+ Lookup lookup = Lookups.fixed(new ProjectOpenedHook() {
+ @Override
+ protected void projectOpened() {
+ GlobalPathRegistry.getDefault().register(ClassPath.SOURCE, new ClassPath[] {source});
+ }
+
+ @Override
+ protected void projectClosed() {
+ GlobalPathRegistry.getDefault().unregister(ClassPath.SOURCE, new ClassPath[] {source});
+ }
+ }, new ClassPathProvider() {
+ @Override
+ public ClassPath findClassPath(FileObject file, String type) {
+ switch (type) {
+ case ClassPath.SOURCE: return source;
+ case ClassPath.BOOT: return BootClassPathUtil.getBootClassPath();
+ }
+ return null;
+ }
+ }
+ );
+ return new Project() {
+ @Override
+ public FileObject getProjectDirectory() {
+ return projectDirectory;
+ }
+
+ @Override
+ public Lookup getLookup() {
+ return lookup;
+ }
+ };
+ }
+ return null;
+ }
+
+ @Override
+ public void saveProject(Project project) throws IOException, ClassCastException {
+ }
+ }
+
+ static {
+ System.setProperty("SourcePath.no.source.filter", "true");
+ }
}
diff --git a/java/java.lsp.server/vscode/package-lock.json b/java/java.lsp.server/vscode/package-lock.json
index a124690..a06353d 100644
--- a/java/java.lsp.server/vscode/package-lock.json
+++ b/java/java.lsp.server/vscode/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "apache-netbeans-java",
- "version": "0.0.1",
+ "version": "0.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/java/java.lsp.server/vscode/package.json b/java/java.lsp.server/vscode/package.json
index acb5788..4e4b7de 100644
--- a/java/java.lsp.server/vscode/package.json
+++ b/java/java.lsp.server/vscode/package.json
@@ -3,7 +3,7 @@
"description": "An Apache NetBeans Java plugin for Visual Studio Code",
"author": "Apache NetBeans",
"license": "Apache 2.0",
- "version": "0.0.1",
+ "version": "0.1.0",
"repository": {
"type": "git",
"url": "https://github.com/apache/netbeans"
diff --git a/java/java.lsp.server/vscode/src/VerifyJDK11.java b/java/java.lsp.server/vscode/src/VerifyJDK14.java
similarity index 89%
rename from java/java.lsp.server/vscode/src/VerifyJDK11.java
rename to java/java.lsp.server/vscode/src/VerifyJDK14.java
index 80e5928..b05b308 100644
--- a/java/java.lsp.server/vscode/src/VerifyJDK11.java
+++ b/java/java.lsp.server/vscode/src/VerifyJDK14.java
@@ -16,9 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
-public class VerifyJDK11 {
+public class VerifyJDK14 {
public static void main(String[] args) {
- if (Runtime.version().compareTo(Runtime.Version.parse("11-build")) < 0)
- throw new IllegalStateException("Not JDK 11+.");
+ if (Runtime.version().compareTo(Runtime.Version.parse("14-build")) < 0)
+ throw new IllegalStateException("Not JDK 14+.");
}
}
diff --git a/java/java.lsp.server/vscode/src/extension.ts b/java/java.lsp.server/vscode/src/extension.ts
index e1f39b8..4817bc5 100644
--- a/java/java.lsp.server/vscode/src/extension.ts
+++ b/java/java.lsp.server/vscode/src/extension.ts
@@ -27,9 +27,9 @@
try {
let targetJava = specifiedJDK != null ? specifiedJDK + '/bin/java' : 'java';
- execSync(targetJava + ' ' + context.extensionPath + '/src/VerifyJDK11.java');
+ execSync(targetJava + ' ' + context.extensionPath + '/src/VerifyJDK14.java');
} catch (e) {
- window.showErrorMessage('The Java language server needs a JDK 11 to run, but none found. Please configure it under File/Preferences/Settings/Extensions/Java and restart VS Code.');
+ window.showErrorMessage('The Java language server needs a JDK 14 to run, but none found. Please configure it under File/Preferences/Settings/Extensions/Java and restart VS Code.');
return ;
}
let serverPath = path.resolve(context.extensionPath, "nb-java-lsp-server", "bin", "nb-java-lsp-server");
diff --git a/java/java.source.base/src/org/netbeans/modules/java/source/TreeShims.java b/java/java.source.base/src/org/netbeans/modules/java/source/TreeShims.java
index 5e7b042..88504aa 100644
--- a/java/java.source.base/src/org/netbeans/modules/java/source/TreeShims.java
+++ b/java/java.source.base/src/org/netbeans/modules/java/source/TreeShims.java
@@ -228,4 +228,13 @@
public static boolean isRecordComponent(ElementKind kind) {
return "RECORD_COMPONENT".equals(kind.name());
}
+
+ public static ElementKind getRecordKind() {
+ try {
+ return ElementKind.valueOf(RECORD); //NOI18N
+ } catch (IllegalArgumentException ex) {
+ return null;
+ }
+ }
+
}
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 d38785b..e113741 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
@@ -53,6 +53,7 @@
import org.netbeans.modules.java.source.BootClassPathUtil;
import org.netbeans.modules.java.source.ElementUtils;
import org.netbeans.modules.java.source.TestUtil;
+import org.netbeans.modules.java.source.TreeShims;
import org.netbeans.modules.java.source.usages.IndexUtil;
import org.netbeans.spi.java.classpath.ClassPathProvider;
import org.netbeans.spi.java.classpath.support.ClassPathSupport;
@@ -73,6 +74,7 @@
private FileObject src;
private FileObject data;
+ private String orignalNetbeansUsr;
static {
@@ -129,9 +131,15 @@
ClassPathProviderImpl.getDefault().setClassPaths(TestUtil.getBootClassPath(),
ClassPathSupport.createClassPath(new URL[0]),
ClassPathSupport.createClassPath(new FileObject[]{this.src}));
+ orignalNetbeansUsr = System.getProperty("netbeans.user");
+ System.setProperty("netbeans.user", getWorkDirPath());
}
protected void tearDown() throws Exception {
+ if (orignalNetbeansUsr != null)
+ System.setProperty("netbeans.user", orignalNetbeansUsr);
+ else
+ System.clearProperty("netbeans.user");
}
@@ -426,6 +434,9 @@
ElementKind.ENUM,
ElementKind.ANNOTATION_TYPE
}));
+ ElementKind recordKind = TreeShims.getRecordKind();
+ if (recordKind != null)
+ allowed.add(recordKind);
for (ElementKind aek : allowed) {
ElementHandle<TypeElement> eh = ElementHandle.createTypeElementHandle(aek, "org.me.Foo"); //NOI18N
assertEquals(aek, eh.getKind());
diff --git a/java/java.sourceui/manifest.mf b/java/java.sourceui/manifest.mf
index e122b88..89f5f11 100644
--- a/java/java.sourceui/manifest.mf
+++ b/java/java.sourceui/manifest.mf
@@ -1,6 +1,6 @@
Manifest-Version: 1.0
OpenIDE-Module: org.netbeans.modules.java.sourceui/1
-OpenIDE-Module-Implementation-Version: 1
+OpenIDE-Module-Implementation-Version: 2
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/java/source/ui/resources/Bundle.properties
OpenIDE-Module-Install: org/netbeans/modules/java/source/ui/JavaSourceUIModule.class
AutoUpdate-Show-In-Client: false
diff --git a/java/java.sourceui/src/org/netbeans/api/java/source/ui/ElementOpen.java b/java/java.sourceui/src/org/netbeans/api/java/source/ui/ElementOpen.java
index f16649f..7eb1057 100644
--- a/java/java.sourceui/src/org/netbeans/api/java/source/ui/ElementOpen.java
+++ b/java/java.sourceui/src/org/netbeans/api/java/source/ui/ElementOpen.java
@@ -294,8 +294,10 @@
assert fo != null;
try {
- int[] offset = getOffset(fo, handle, cancel);
- return new Object[] {fo, offset[0], offset[1]};
+ Object[] result = new Object[4];
+ result[0] = fo;
+ getOffset(fo, handle, result, cancel);
+ return result;
} catch (IOException e) {
Exceptions.printStackTrace(e);
return null;
@@ -343,8 +345,9 @@
private static final int AWT_TIMEOUT = 1000;
private static final int NON_AWT_TIMEOUT = 2000;
- private static int[] getOffset(final FileObject fo, final ElementHandle<? extends Element> handle, final AtomicBoolean cancel) throws IOException {
- final int[] result = new int[] {-1, -1};
+ private static void getOffset(final FileObject fo, final ElementHandle<? extends Element> handle, final Object[] result, final AtomicBoolean cancel) throws IOException {
+ result[1] = -1;
+ result[2] = -1;
final JavaSource js = JavaSource.forFileObject(fo);
if (js != null) {
@@ -358,6 +361,7 @@
} catch (IOException ioe) {
Exceptions.printStackTrace(ioe);
}
+ result[3] = info.getCompilationUnit().getLineMap();
Element el = handle.resolve(info);
if (el == null) {
if (!SourceUtils.isScanInProgress()) {
@@ -375,7 +379,7 @@
// Imprecise but should usually work:
Matcher m = Pattern.compile("(?m)^package (.+);$").matcher(fo.asText(/*FileEncodingQuery.getEncoding(fo).name()*/)); // NOI18N
if (m.find()) {
- result[0] = m.start();
+ result[1] = m.start();
}
return;
}
@@ -388,15 +392,14 @@
Tree elTree = v.declTree;
if (elTree != null) {
- result[0] = (int)info.getTrees().getSourcePositions().getStartPosition(cu, elTree);
- result[1] = (int)info.getTrees().getSourcePositions().getEndPosition(cu, elTree);
+ result[1] = (int)info.getTrees().getSourcePositions().getStartPosition(cu, elTree);
+ result[2] = (int)info.getTrees().getSourcePositions().getEndPosition(cu, elTree);
}
}
};
js.runUserActionTask(t, true);
}
- return result;
}
// Private innerclasses ----------------------------------------------------
diff --git a/java/libs.javacapi/external/binaries-list b/java/libs.javacapi/external/binaries-list
index d9def00..97525d3 100644
--- a/java/libs.javacapi/external/binaries-list
+++ b/java/libs.javacapi/external/binaries-list
@@ -14,4 +14,4 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-FC7D04B1F1AE92A87F113096862112BE7E6970D4 nb-javac-13-api.jar
+988279FD0A258197BF24190BE44A60BA7EA1FC3C nb-javac-14-api.jar
\ No newline at end of file
diff --git a/java/libs.javacapi/external/nb-javac-13-api-license.txt b/java/libs.javacapi/external/nb-javac-14-api-license.txt
similarity index 99%
rename from java/libs.javacapi/external/nb-javac-13-api-license.txt
rename to java/libs.javacapi/external/nb-javac-14-api-license.txt
index 5f55457..d2aec9e 100644
--- a/java/libs.javacapi/external/nb-javac-13-api-license.txt
+++ b/java/libs.javacapi/external/nb-javac-14-api-license.txt
@@ -1,6 +1,6 @@
Name: Javac Compiler API
Description: Javac Compiler API
-Version: 13
+Version: 14
License: GPL-2-CP
Origin: OpenJDK (http://hg.openjdk.java.net/)
Source: http://hg.netbeans.org/main/nb-java-x/
diff --git a/java/libs.javacapi/manifest.mf b/java/libs.javacapi/manifest.mf
index 5177edf..d1a19dd 100644
--- a/java/libs.javacapi/manifest.mf
+++ b/java/libs.javacapi/manifest.mf
@@ -2,5 +2,4 @@
OpenIDE-Module: org.netbeans.libs.javacapi
OpenIDE-Module-Implementation-Version: 3
OpenIDE-Module-Localizing-Bundle: org/netbeans/libs/javacapi/Bundle.properties
-OpenIDE-Module-Layer: org/netbeans/libs/javacapi/layer.xml
OpenIDE-Module-Requires: org.openide.modules.ModuleFormat2
diff --git a/java/libs.javacapi/nbproject/project.xml b/java/libs.javacapi/nbproject/project.xml
index 0dc3ad9..2bd65c9 100644
--- a/java/libs.javacapi/nbproject/project.xml
+++ b/java/libs.javacapi/nbproject/project.xml
@@ -40,7 +40,7 @@
</public-packages>
<class-path-extension>
<runtime-relative-path />
- <binary-origin>external/nb-javac-13-api.jar</binary-origin>
+ <binary-origin>external/nb-javac-14-api.jar</binary-origin>
</class-path-extension>
</data>
</configuration>
diff --git a/java/libs.javacapi/src/org/netbeans/libs/javacapi/Bundle.properties b/java/libs.javacapi/src/org/netbeans/libs/javacapi/Bundle.properties
index 6c99d32..2c578e0 100644
--- a/java/libs.javacapi/src/org/netbeans/libs/javacapi/Bundle.properties
+++ b/java/libs.javacapi/src/org/netbeans/libs/javacapi/Bundle.properties
@@ -19,7 +19,3 @@
The javac public API, as defined by JSR 199, JSR 269, and the com.sun.source packages defined in JDK 6.
OpenIDE-Module-Name=Javac API Wrapper
OpenIDE-Module-Short-Description=The javac public API
-
-##library file from layer
-javac-api=Java Tree API
-
diff --git a/java/libs.javacapi/src/org/netbeans/libs/javacapi/javac-library.xml b/java/libs.javacapi/src/org/netbeans/libs/javacapi/javac-library.xml
deleted file mode 100644
index 26b90d6..0000000
--- a/java/libs.javacapi/src/org/netbeans/libs/javacapi/javac-library.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.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.
-
--->
-<!DOCTYPE library PUBLIC "-//NetBeans//DTD Library Declaration 1.0//EN" "http://www.netbeans.org/dtds/library-declaration-1_0.dtd">
-
-<library version="1.0">
- <name>javac-api</name>
- <type>j2se</type>
- <localizing-bundle>org/netbeans/libs/javacapi/Bundle</localizing-bundle>
- <volume>
- <type>classpath</type>
- <resource>jar:nbinst://org.netbeans.libs.javacapi/modules/ext/nb-javac-api.jar!/</resource>
- </volume>
-</library>
diff --git a/java/libs.javacapi/src/org/netbeans/libs/javacapi/layer.xml b/java/libs.javacapi/src/org/netbeans/libs/javacapi/layer.xml
deleted file mode 100644
index dcffde7..0000000
--- a/java/libs.javacapi/src/org/netbeans/libs/javacapi/layer.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
- 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.
-
--->
-<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.2//EN" "http://www.netbeans.org/dtds/filesystem-1_2.dtd">
-<filesystem>
- <folder name="org-netbeans-api-project-libraries">
- <folder name="Libraries">
- <file name="javac-library.xml" url="javac-library.xml">
- <attr name="displayName" bundlevalue="org/netbeans/libs/javacapi/Bundle#javac-api"/>
- </file>
- </folder>
- </folder>
-</filesystem>
diff --git a/java/libs.javacimpl/external/binaries-list b/java/libs.javacimpl/external/binaries-list
index 8461b2c..d270d6c 100644
--- a/java/libs.javacimpl/external/binaries-list
+++ b/java/libs.javacimpl/external/binaries-list
@@ -14,4 +14,4 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-34E9F9C1BDC61FE7EFCCF305D70960B862DE7815 nb-javac-13-impl.jar
+BABD4BF10C42EE321AED7393BED72C579A486CD3 nb-javac-14-impl.jar
diff --git a/java/libs.javacimpl/external/nb-javac-13-impl-license.txt b/java/libs.javacimpl/external/nb-javac-14-impl-license.txt
similarity index 99%
rename from java/libs.javacimpl/external/nb-javac-13-impl-license.txt
rename to java/libs.javacimpl/external/nb-javac-14-impl-license.txt
index 7f1b8ff..99a03a9 100644
--- a/java/libs.javacimpl/external/nb-javac-13-impl-license.txt
+++ b/java/libs.javacimpl/external/nb-javac-14-impl-license.txt
@@ -1,6 +1,6 @@
Name: Javac Compiler Implementation
Description: Javac Compiler Implementation
-Version: 13
+Version: 14
License: GPL-2-CP
Origin: OpenJDK (http://hg.openjdk.java.net/)
Source: http://hg.netbeans.org/main/nb-java-x/
diff --git a/java/libs.javacimpl/nbproject/project.xml b/java/libs.javacimpl/nbproject/project.xml
index 6b0987f..c382b99 100644
--- a/java/libs.javacimpl/nbproject/project.xml
+++ b/java/libs.javacimpl/nbproject/project.xml
@@ -37,7 +37,7 @@
<public-packages/>
<class-path-extension>
<runtime-relative-path />
- <binary-origin>external/nb-javac-13-impl.jar</binary-origin>
+ <binary-origin>external/nb-javac-14-impl.jar</binary-origin>
</class-path-extension>
</data>
</configuration>
diff --git a/java/libs.springframework/nbproject/project.properties b/java/libs.springframework/nbproject/project.properties
index 63adc8c..845161d 100644
--- a/java/libs.springframework/nbproject/project.properties
+++ b/java/libs.springframework/nbproject/project.properties
@@ -92,12 +92,10 @@
modules/ext/spring-3.0/spring-aop-3.2.18.RELEASE.jar,\
modules/ext/spring-3.0/spring-aspects-3.2.18.RELEASE.jar,\
modules/ext/spring-3.0/spring-beans-3.2.18.RELEASE.jar,\
- modules/ext/spring-3.0/spring-build-src-3.2.18.RELEASE.jar,\
modules/ext/spring-3.0/spring-context-3.2.18.RELEASE.jar,\
modules/ext/spring-3.0/spring-context-support-3.2.18.RELEASE.jar,\
modules/ext/spring-3.0/spring-core-3.2.18.RELEASE.jar,\
modules/ext/spring-3.0/spring-expression-3.2.18.RELEASE.jar,\
- modules/ext/spring-3.0/spring-framework-bom-3.2.18.RELEASE.jar,\
modules/ext/spring-3.0/spring-instrument-3.2.18.RELEASE.jar,\
modules/ext/spring-3.0/spring-instrument-tomcat-3.2.18.RELEASE.jar,\
modules/ext/spring-3.0/spring-jdbc-3.2.18.RELEASE.jar,\
diff --git a/java/libs.springframework/src/org/netbeans/libs/springframework/spring-framework300.xml b/java/libs.springframework/src/org/netbeans/libs/springframework/spring-framework300.xml
index d69ff59..fd8f4b6 100644
--- a/java/libs.springframework/src/org/netbeans/libs/springframework/spring-framework300.xml
+++ b/java/libs.springframework/src/org/netbeans/libs/springframework/spring-framework300.xml
@@ -31,12 +31,10 @@
<resource>jar:nbinst://org.netbeans.libs.springframework/modules/ext/spring-3.0/spring-aop-3.2.18.RELEASE.jar!/</resource>
<resource>jar:nbinst://org.netbeans.libs.springframework/modules/ext/spring-3.0/spring-aspects-3.2.18.RELEASE.jar!/</resource>
<resource>jar:nbinst://org.netbeans.libs.springframework/modules/ext/spring-3.0/spring-beans-3.2.18.RELEASE.jar!/</resource>
- <resource>jar:nbinst://org.netbeans.libs.springframework/modules/ext/spring-3.0/spring-build-src-3.2.18.RELEASE.jar!/</resource>
<resource>jar:nbinst://org.netbeans.libs.springframework/modules/ext/spring-3.0/spring-context-3.2.18.RELEASE.jar!/</resource>
<resource>jar:nbinst://org.netbeans.libs.springframework/modules/ext/spring-3.0/spring-context-support-3.2.18.RELEASE.jar!/</resource>
<resource>jar:nbinst://org.netbeans.libs.springframework/modules/ext/spring-3.0/spring-core-3.2.18.RELEASE.jar!/</resource>
<resource>jar:nbinst://org.netbeans.libs.springframework/modules/ext/spring-3.0/spring-expression-3.2.18.RELEASE.jar!/</resource>
- <resource>jar:nbinst://org.netbeans.libs.springframework/modules/ext/spring-3.0/spring-framework-bom-3.2.18.RELEASE.jar!/</resource>
<resource>jar:nbinst://org.netbeans.libs.springframework/modules/ext/spring-3.0/spring-instrument-3.2.18.RELEASE.jar!/</resource>
<resource>jar:nbinst://org.netbeans.libs.springframework/modules/ext/spring-3.0/spring-instrument-tomcat-3.2.18.RELEASE.jar!/</resource>
<resource>jar:nbinst://org.netbeans.libs.springframework/modules/ext/spring-3.0/spring-jdbc-3.2.18.RELEASE.jar!/</resource>
diff --git a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/ui/WhereUsedPanel.java b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/ui/WhereUsedPanel.java
index 2540b8e..583ac8d 100644
--- a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/ui/WhereUsedPanel.java
+++ b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/ui/WhereUsedPanel.java
@@ -86,7 +86,10 @@
case FIELD:
case ENUM_CONSTANT:
default: {
- panel = new WhereUsedPanelVariable(parent);
+ if (kind.name().equals("RECORD")) // NOI18N
+ panel = new WhereUsedPanelClass(parent);
+ else
+ panel = new WhereUsedPanelVariable(parent);
break;
}
}
diff --git a/java/spi.java.hints/src/org/netbeans/modules/java/hints/spiimpl/Utilities.java b/java/spi.java.hints/src/org/netbeans/modules/java/hints/spiimpl/Utilities.java
index 03deb95..85c12ca 100644
--- a/java/spi.java.hints/src/org/netbeans/modules/java/hints/spiimpl/Utilities.java
+++ b/java/spi.java.hints/src/org/netbeans/modules/java/hints/spiimpl/Utilities.java
@@ -111,6 +111,8 @@
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.AnnotationValueVisitor;
@@ -1352,6 +1354,33 @@
return super.modifiersOpt(partial);
}
+
+ public JCVariableDecl formalParameter(boolean lambdaParam, boolean recordComponents) {
+ if (token.kind == TokenKind.IDENTIFIER) {
+ if (token.name().startsWith(dollar)) {
+ com.sun.tools.javac.util.Name name = token.name();
+
+ Token peeked = S.token(1);
+
+ if (peeked.kind == TokenKind.COMMA || peeked.kind == TokenKind.RPAREN) {
+ nextToken();
+ return JackpotTrees.createVariableWildcard(ctx, name);
+ }
+ }
+ }
+ JCTree.JCVariableDecl result = null;
+ try {
+ Class[] paramTypes = {boolean.class, boolean.class};
+ result = (JCTree.JCVariableDecl) MethodHandles.lookup()
+ .findSpecial(JavacParser.class, "formalParameter", MethodType.methodType(JCTree.JCVariableDecl.class, paramTypes), JackpotJavacParser.class) // NOI18N
+ .invoke(this, lambdaParam, recordComponents);
+ } catch (Throwable ex) {
+ throw new IllegalStateException(ex);
+ }
+ return result;
+
+ }
+
@Override
public JCVariableDecl formalParameter(boolean lambdaParam) {
if (token.kind == TokenKind.IDENTIFIER) {
@@ -1408,7 +1437,35 @@
}
return super.catchClause();
}
+
+ public com.sun.tools.javac.util.List<JCTree> classOrInterfaceOrRecordBodyDeclaration(com.sun.tools.javac.util.Name className, boolean isInterface, boolean isRecord) {
+ if (token.kind == TokenKind.IDENTIFIER) {
+ if (token.name().startsWith(dollar)) {
+ com.sun.tools.javac.util.Name name = token.name();
+
+ Token peeked = S.token(1);
+
+ if (peeked.kind == TokenKind.SEMI) {
+ nextToken();
+ nextToken();
+
+ return com.sun.tools.javac.util.List.<JCTree>of(F.Ident(name));
+ }
+ }
+ }
+
+ com.sun.tools.javac.util.List<JCTree> result = null;
+ Class[] argsType = {com.sun.tools.javac.util.Name.class, boolean.class, boolean.class};
+ try {
+ result = (com.sun.tools.javac.util.List<JCTree>) MethodHandles.lookup().findSpecial(JavacParser.class, "classOrInterfaceOrRecordBodyDeclaration", MethodType.methodType(com.sun.tools.javac.util.List.class, argsType), JackpotJavacParser.class) // NOI18N
+ .invoke(this, className, false, false);
+ } catch (Throwable ex) {
+ throw new IllegalStateException(ex);
+ }
+ return result;
+ }
+
@Override
public com.sun.tools.javac.util.List<JCTree> classOrInterfaceBodyDeclaration(com.sun.tools.javac.util.Name className, boolean isInterface) {
if (token.kind == TokenKind.IDENTIFIER) {
diff --git a/nb/updatecenters/extras/nbjavac.api/manifest.mf b/nb/updatecenters/extras/nbjavac.api/manifest.mf
index af2837e..00bd544 100644
--- a/nb/updatecenters/extras/nbjavac.api/manifest.mf
+++ b/nb/updatecenters/extras/nbjavac.api/manifest.mf
@@ -2,6 +2,6 @@
AutoUpdate-Show-In-Client: false
OpenIDE-Module: org.netbeans.modules.nbjavac.api
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/nbjavac/api/Bundle.properties
-OpenIDE-Module-Specification-Version: 2.1
+OpenIDE-Module-Specification-Version: 2.2
OpenIDE-Module-Hide-Classpath-Packages: com.sun.javadoc.**, com.sun.source.**, javax.annotation.processing.**, javax.lang.model.**, javax.tools.**, com.sun.tools.javac.**
OpenIDE-Module-Fragment-Host: org.netbeans.libs.javacapi
diff --git a/nb/updatecenters/extras/nbjavac.api/nbproject/project.xml b/nb/updatecenters/extras/nbjavac.api/nbproject/project.xml
index 8ebde33..a2bfb3d 100644
--- a/nb/updatecenters/extras/nbjavac.api/nbproject/project.xml
+++ b/nb/updatecenters/extras/nbjavac.api/nbproject/project.xml
@@ -28,8 +28,8 @@
<module-dependencies/>
<public-packages/>
<class-path-extension>
- <runtime-relative-path>ext/nb-javac-13-api.jar</runtime-relative-path>
- <binary-origin>release/modules/ext/nb-javac-13-api.jar</binary-origin>
+ <runtime-relative-path>ext/nb-javac-14-api.jar</runtime-relative-path>
+ <binary-origin>release/modules/ext/nb-javac-14-api.jar</binary-origin>
</class-path-extension>
</data>
</configuration>
diff --git a/nb/updatecenters/extras/nbjavac.api/release/modules/ext/nb-javac-13-api.jar.external b/nb/updatecenters/extras/nbjavac.api/release/modules/ext/nb-javac-13-api.jar.external
deleted file mode 100644
index 09a444a..0000000
--- a/nb/updatecenters/extras/nbjavac.api/release/modules/ext/nb-javac-13-api.jar.external
+++ /dev/null
@@ -1,6 +0,0 @@
-CRC:520567768
-SIZE:230395
-URL:https://netbeans.osuosl.org/binaries/FC7D04B1F1AE92A87F113096862112BE7E6970D4-nb-javac-13-api.jar
-URL:https://hg.netbeans.org/binaries/FC7D04B1F1AE92A87F113096862112BE7E6970D4-nb-javac-13-api.jar
-MessageDigest: SHA-256 eb36f3a1714010028d0da5c6be22cdb7b7cc8cc04d618351403a57c9b10adbfc
-MessageDigest: SHA-512 bdf48bef2578cd66df8c3a95e6ad1dc022ee136806674dcf791f43829d050f20b565b367b47a5524fb8d62c0f58187a1e0317429526fd219f324033830ac3e57
\ No newline at end of file
diff --git a/nb/updatecenters/extras/nbjavac.api/release/modules/ext/nb-javac-14-api.jar.external b/nb/updatecenters/extras/nbjavac.api/release/modules/ext/nb-javac-14-api.jar.external
new file mode 100644
index 0000000..e30d396
--- /dev/null
+++ b/nb/updatecenters/extras/nbjavac.api/release/modules/ext/nb-javac-14-api.jar.external
@@ -0,0 +1,6 @@
+CRC:1160927925
+SIZE:250879
+URL:https://netbeans.osuosl.org/binaries/988279FD0A258197BF24190BE44A60BA7EA1FC3C-nb-javac-14-api.jar
+URL:https://hg.netbeans.org/binaries/988279FD0A258197BF24190BE44A60BA7EA1FC3C-nb-javac-14-api.jar
+MessageDigest: SHA-256 76c02474a83a61e92952548c3b59af093d20fb9de82b2aa47a458cdfab538da7
+MessageDigest: SHA-512 9a79f44cf335045a101b43ba8481070c65a5c2f8b274aa77069a751fa2c0da40c60c0b7830373f5a100205bb7d14a54f32ca3ce8c50442568e5de1474a6d5a4a
\ No newline at end of file
diff --git a/nb/updatecenters/extras/nbjavac.impl/manifest.mf b/nb/updatecenters/extras/nbjavac.impl/manifest.mf
index a9caa64..ac72632 100644
--- a/nb/updatecenters/extras/nbjavac.impl/manifest.mf
+++ b/nb/updatecenters/extras/nbjavac.impl/manifest.mf
@@ -2,7 +2,7 @@
AutoUpdate-Show-In-Client: false
OpenIDE-Module: org.netbeans.modules.nbjavac.impl
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/nbjavac/impl/Bundle.properties
-OpenIDE-Module-Specification-Version: 2.1
+OpenIDE-Module-Specification-Version: 2.2
OpenIDE-Module-Hide-Classpath-Packages: com.sun.tools.javac.**, com.sun.tools.javadoc.**, com.sun.tools.javap.**, com.sun.tools.classfile.**, com.sun.tools.doclint.**
OpenIDE-Module-Fragment-Host: org.netbeans.libs.javacimpl
OpenIDE-Module-Provides: org.netbeans.modules.nbjavac
diff --git a/nb/updatecenters/extras/nbjavac.impl/nbproject/project.xml b/nb/updatecenters/extras/nbjavac.impl/nbproject/project.xml
index 8436f37..93ff9e6 100644
--- a/nb/updatecenters/extras/nbjavac.impl/nbproject/project.xml
+++ b/nb/updatecenters/extras/nbjavac.impl/nbproject/project.xml
@@ -35,8 +35,8 @@
</module-dependencies>
<public-packages/>
<class-path-extension>
- <runtime-relative-path>ext/nb-javac-13-impl.jar</runtime-relative-path>
- <binary-origin>release/modules/ext/nb-javac-13-impl.jar</binary-origin>
+ <runtime-relative-path>ext/nb-javac-14-impl.jar</runtime-relative-path>
+ <binary-origin>release/modules/ext/nb-javac-14-impl.jar</binary-origin>
</class-path-extension>
</data>
</configuration>
diff --git a/nb/updatecenters/extras/nbjavac.impl/release/modules/ext/nb-javac-13-impl.jar.external b/nb/updatecenters/extras/nbjavac.impl/release/modules/ext/nb-javac-13-impl.jar.external
deleted file mode 100644
index e0f65a5..0000000
--- a/nb/updatecenters/extras/nbjavac.impl/release/modules/ext/nb-javac-13-impl.jar.external
+++ /dev/null
@@ -1,6 +0,0 @@
-CRC:3482904810
-SIZE:3595235
-URL:https://netbeans.osuosl.org/binaries/34E9F9C1BDC61FE7EFCCF305D70960B862DE7815-nb-javac-13-impl.jar
-URL:https://hg.netbeans.org/binaries/34E9F9C1BDC61FE7EFCCF305D70960B862DE7815-nb-javac-13-impl.jar
-MessageDigest: SHA-256 cabea2a079534191d09cba7bfe55c0de45079a04c8ac88926b09d76ed7067879
-MessageDigest: SHA-512 55feeefd61fda43e4a9b461f405e806a751ea35c713b7fc762f149565fd8780828fcce9d73d38edfa89176f00087220bd8b99ee5135f9f551fe40e2a8768ee83
\ No newline at end of file
diff --git a/nb/updatecenters/extras/nbjavac.impl/release/modules/ext/nb-javac-14-impl.jar.external b/nb/updatecenters/extras/nbjavac.impl/release/modules/ext/nb-javac-14-impl.jar.external
new file mode 100644
index 0000000..dc51f2f
--- /dev/null
+++ b/nb/updatecenters/extras/nbjavac.impl/release/modules/ext/nb-javac-14-impl.jar.external
@@ -0,0 +1,6 @@
+CRC:1143111181
+SIZE:3683563
+URL:https://netbeans.osuosl.org/binaries/BABD4BF10C42EE321AED7393BED72C579A486CD3-nb-javac-14-impl.jar
+URL:https://hg.netbeans.org/binaries/ BABD4BF10C42EE321AED7393BED72C579A486CD3-nb-javac-14-impl.jar
+MessageDigest: SHA-256 b6381f69707fb12f2edbbf47d59189bbfbd3390f64d9e7ec9b5d90abc3ba5db4
+MessageDigest: SHA-512 8fee92cc0df7ae2ec034c027cc761d6ca87e609db68a269c993148bc0c611b0a4dd088ac740f7c81bfe8c7e32c0367965cf8187378c02348dd6b2532e46a005c
\ No newline at end of file
diff --git a/nb/updatecenters/extras/nbjavac/src/org/netbeans/modules/nbjavac/Bundle.properties b/nb/updatecenters/extras/nbjavac/src/org/netbeans/modules/nbjavac/Bundle.properties
index 9da78f6..de7fd4e 100644
--- a/nb/updatecenters/extras/nbjavac/src/org/netbeans/modules/nbjavac/Bundle.properties
+++ b/nb/updatecenters/extras/nbjavac/src/org/netbeans/modules/nbjavac/Bundle.properties
@@ -18,6 +18,6 @@
OpenIDE-Module-Display-Category=Java
OpenIDE-Module-Long-Description=\
This library improves the Java editor behavior and enables the Compile on Save features. \
- Note this plugin does not support JDK 14 language features, like records.
+ Supports JDK-14 features.
OpenIDE-Module-Name=The nb-javac Java editing support library
OpenIDE-Module-Short-Description=The nb-javac Java editing support library
diff --git a/nb/updatecenters/licenseinfo.xml b/nb/updatecenters/licenseinfo.xml
index 06e3d8e..b40fdd4 100644
--- a/nb/updatecenters/licenseinfo.xml
+++ b/nb/updatecenters/licenseinfo.xml
@@ -26,8 +26,8 @@
<comment type="COMMENT_UNSUPPORTED" />
</fileset>
<fileset>
- <file>extras/nbjavac.api/release/modules/ext/nb-javac-13-api.jar.external</file>
- <file>extras/nbjavac.impl/release/modules/ext/nb-javac-13-impl.jar.external</file>
+ <file>extras/nbjavac.api/release/modules/ext/nb-javac-14-api.jar.external</file>
+ <file>extras/nbjavac.impl/release/modules/ext/nb-javac-14-impl.jar.external</file>
<license ref="Apache-2.0-ASF" />
<comment type="COMMENT_UNSUPPORTED" />
</fileset>
diff --git a/nbbuild/templates/projectized.xml b/nbbuild/templates/projectized.xml
index 20a06c6..d315d2d 100644
--- a/nbbuild/templates/projectized.xml
+++ b/nbbuild/templates/projectized.xml
@@ -177,7 +177,7 @@
<property name="locmakenbm.brands" value="${brandings}"/>
<!-- When requires.nb.javac property is true, prepend javac-api and javac-impl on bootclasspath to allow override the default annotation
processing API located in rt.jar. -->
- <property name="bootclasspath.prepend.nb" value="${nb_all}/java/libs.javacapi/external/nb-javac-13-api.jar${path.separator}${nb_all}/java/libs.javacimpl/external/nb-javac-13-impl.jar" />
+ <property name="bootclasspath.prepend.nb" value="${nb_all}/java/libs.javacapi/external/nb-javac-14-api.jar${path.separator}${nb_all}/java/libs.javacimpl/external/nb-javac-14-impl.jar" />
<property name="bootclasspath.prepend.vanilla" value="${nb_all}/nbbuild/external/vanilla-javac-api.jar${path.separator}${nb_all}/nbbuild/external/vanilla-javac-impl.jar" />
<condition property="bootclasspath.prepend" value="${bootclasspath.prepend.nb}">
<istrue value="${requires.nb.javac.impl}"/>
diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/csl/InstantRenamerImpl.java b/php/php.editor/src/org/netbeans/modules/php/editor/csl/InstantRenamerImpl.java
index c118b8e..f0ee97a 100644
--- a/php/php.editor/src/org/netbeans/modules/php/editor/csl/InstantRenamerImpl.java
+++ b/php/php.editor/src/org/netbeans/modules/php/editor/csl/InstantRenamerImpl.java
@@ -31,6 +31,7 @@
import org.netbeans.modules.php.editor.api.elements.FieldElement;
import org.netbeans.modules.php.editor.api.elements.MethodElement;
import org.netbeans.modules.php.editor.api.elements.PhpElement;
+import org.netbeans.modules.php.editor.api.elements.TypeConstantElement;
import org.netbeans.modules.php.editor.model.Model;
import org.netbeans.modules.php.editor.model.Occurence;
import org.netbeans.modules.php.editor.model.Occurence.Accuracy;
@@ -83,6 +84,12 @@
if (phpModifiers.isPrivate()) {
return checkAll(caretOccurence);
}
+ } else if (decl instanceof TypeConstantElement) {
+ TypeConstantElement cnst = (TypeConstantElement) decl;
+ PhpModifiers phpModifiers = cnst.getPhpModifiers();
+ if (phpModifiers.isPrivate()) {
+ return checkAll(caretOccurence);
+ }
}
} else {
return checkAll(caretOccurence);
diff --git a/platform/api.htmlui/src/org/netbeans/modules/htmlui/jfx/NbBrowsers.java b/platform/api.htmlui/src/org/netbeans/modules/htmlui/jfx/NbBrowsers.java
index 6d65983..a6b561a 100644
--- a/platform/api.htmlui/src/org/netbeans/modules/htmlui/jfx/NbBrowsers.java
+++ b/platform/api.htmlui/src/org/netbeans/modules/htmlui/jfx/NbBrowsers.java
@@ -81,6 +81,7 @@
case "Windows":
return "win";
case "Darcula":
+ case "Flat Dark":
return "darcula";
}
return null;
diff --git a/platform/openide.util.lookup/apichanges.xml b/platform/openide.util.lookup/apichanges.xml
index 99c701f..f3d71c3 100644
--- a/platform/openide.util.lookup/apichanges.xml
+++ b/platform/openide.util.lookup/apichanges.xml
@@ -25,6 +25,33 @@
<apidef name="lookup">Lookup API</apidef>
</apidefs>
<changes>
+ <change id="ProxyLookupController">
+ <api name="lookup"/>
+ <summary>Add ProxyLookup.Controller to set lookups dynamically without
+ subclassing</summary>
+ <version major="8" minor="43"/>
+ <date year="2020" month="7" day="4"/>
+ <author login="tboudreau"/>
+ <compatibility addition="yes" source="compatible" semantic="compatible" binary="compatible"/>
+ <description>
+ <p>
+ One of the most common usages of <a href="@TOP@/org/openide/util/lookup/ProxyLookup.html">ProxyLookup</a>
+ is to dynamically change the set of lookups being delegated to. However the
+ <a href="@TOP@/org/openide/util/lookup/ProxyLookup.html#setLookups-org.openide.util.Lookup...-">setLookups(...)</a>
+ method is <code>protected</code>. To avoid the need to subclass
+ <a href="@TOP@/org/openide/util/lookup/ProxyLookup.html">ProxyLookup</a>
+ this change introduces
+ <a href="@TOP@/org/openide/util/lookup/ProxyLookup.Controller.html">ProxyLookup.Controller</a>
+ that gives the creator of <a href="@TOP@/org/openide/util/lookup/ProxyLookup.html">ProxyLookup</a>
+ a way to call
+ <a href="@TOP@/org/openide/util/lookup/ProxyLookup.Controller.html#setLookups-org.openide.util.Lookup...-">setLookups(...)</a>
+ without exposing the method to others having just a reference to
+ the <a href="@TOP@/org/openide/util/lookup/ProxyLookup.html">ProxyLookup</a>.
+ </p>
+ </description>
+ <class name="ProxyLookup" package="org.openide.util.lookup"/>
+ <issue number="NETBEANS-4699"/>
+ </change>
<change id="AbstractProcessorSupportedSource">
<api name="lookup"/>
<summary>Declare support for all source levels.</summary>
diff --git a/platform/openide.util.lookup/manifest.mf b/platform/openide.util.lookup/manifest.mf
index 7b0558c..372496f 100644
--- a/platform/openide.util.lookup/manifest.mf
+++ b/platform/openide.util.lookup/manifest.mf
@@ -1,5 +1,5 @@
Manifest-Version: 1.0
OpenIDE-Module: org.openide.util.lookup
OpenIDE-Module-Localizing-Bundle: org/openide/util/lookup/Bundle.properties
-OpenIDE-Module-Specification-Version: 8.42
+OpenIDE-Module-Specification-Version: 8.43
diff --git a/platform/openide.util.lookup/src/org/openide/util/lookup/ProxyLookup.java b/platform/openide.util.lookup/src/org/openide/util/lookup/ProxyLookup.java
index e18d04d..17cb46e 100644
--- a/platform/openide.util.lookup/src/org/openide/util/lookup/ProxyLookup.java
+++ b/platform/openide.util.lookup/src/org/openide/util/lookup/ProxyLookup.java
@@ -57,6 +57,25 @@
public ProxyLookup(Lookup... lookups) {
data = ImmutableInternalData.EMPTY.setLookupsNoFire(lookups, true);
}
+ /**
+ * Create a {@code ProxyLookup} whose contents can be set dynamically
+ * subclassing. The passed
+ * {@link Controller} can be later be used to call
+ * {@link Controller#setLookups} which then
+ * {@link ProxyLookup#setLookups changes} the lookups this {@code ProxyLookup}
+ * delegates to. The passed controller may
+ * only be used for <i>one</i> ProxyLookup.
+ *
+ * @param controller A {@link Controller} which can be used to set the lookups
+ * @throws IllegalStateException if the passed controller has already
+ * been attached to another ProxyLookup
+ * @since 8.43
+ */
+ @SuppressWarnings("LeakingThisInConstructor")
+ public ProxyLookup(Controller controller) {
+ this();
+ controller.setProxyLookup(this);
+ }
/**
* Create a lookup initially proxying to no others.
@@ -89,7 +108,72 @@
}
return map.keySet();
}
-
+
+ /**
+ * A controller which allows the set of lookups being proxied to be
+ * set dynamically for those who create the instance of
+ * {@link ProxyLookup}.
+ *
+ * @since 8.43
+ */
+ public static final class Controller {
+
+ private ProxyLookup consumer;
+
+ /**
+ * Creates a new controller to be attached to a {@link ProxyLookup}.
+ * @since 8.43
+ */
+ public Controller() {
+ }
+
+ /**
+ * Set the lookups on the {@link ProxyLookup} this controller controls.
+ * If called before a {@link ProxyLookup} has been attached to this
+ * controller, an IllegalStateException will be thrown.
+ *
+ * @param notifyIn an executor to notify changes in
+ * @param lookups an array of Lookups to be proxied
+ * @throws IllegalStateException if called before this instance
+ * has been passed to the constructor of (exactly one) {@link ProxyLookup}
+ * @since 8.43
+ */
+ public void setLookups(Executor notifyIn, Lookup... lookups) {
+ if (consumer == null) {
+ throw new IllegalStateException("Cannot use Controller until "
+ + "a ProxyLookup has been created with it.");
+ }
+ consumer.setLookups(notifyIn, lookups);
+ }
+
+ /**
+ * Set the lookups on the {@link ProxyLookup} this controller controls.
+ * If called before a {@link ProxyLookup} has been attached to this
+ * controller, an IllegalStateException will be thrown.
+ *
+ * @param exe An executor to notify in
+ * @param lookups An array of Lookups to be proxied
+ * @throws IllegalStateException if called before this instance
+ * has been passed to the constructor of (exactly one) {@link ProxyLookup}
+ * @since 8.43
+ */
+ public void setLookups(Lookup... lookups) {
+ if (consumer == null) {
+ throw new IllegalStateException("Cannot use Controller until "
+ + "a ProxyLookup has been created with it.");
+ }
+ setLookups(null, lookups);
+ }
+
+ void setProxyLookup(ProxyLookup lkp) {
+ if (consumer != null) {
+ throw new IllegalStateException("Controller cannot be used "
+ + "with more than one ProxyLookup.");
+ }
+ consumer = lkp;
+ }
+ }
+
/**
* Changes the delegates.
*
@@ -99,7 +183,7 @@
protected final void setLookups(Lookup... lookups) {
setLookups(null, lookups);
}
-
+
/**
* Changes the delegates immediatelly, notifies the listeners in provided
* executor, potentially later.
@@ -113,10 +197,10 @@
Set<Lookup> newL;
Set<Lookup> current;
Lookup[] old;
-
+
Map<Result,LookupListener> toRemove = new IdentityHashMap<Lookup.Result, LookupListener>();
Map<Result,LookupListener> toAdd = new IdentityHashMap<Lookup.Result, LookupListener>();
-
+
ImmutableInternalData orig;
synchronized (ProxyLookup.this) {
orig = getData();
@@ -126,7 +210,7 @@
}
arr = setData(newData, lookups, toAdd, toRemove);
}
-
+
// better to do this later than in synchronized block
for (Map.Entry<Result, LookupListener> e : toRemove.entrySet()) {
e.getKey().removeLookupListener(e.getValue());
@@ -144,7 +228,7 @@
r.collectFires(evAndListeners);
}
}
-
+
class Notify implements Runnable {
public void run() {
Iterator it = evAndListeners.iterator();
@@ -208,7 +292,7 @@
public final <T> Item<T> lookupItem(Template<T> template) {
beforeLookup(template);
- Lookup[] tmpLkps;
+ Lookup[] tmpLkps;
synchronized (ProxyLookup.this) {
tmpLkps = getData().getLookups(false);
}
@@ -256,14 +340,14 @@
}
private Collection<Reference<R>> setData(
- ImmutableInternalData newData, Lookup[] current,
+ ImmutableInternalData newData, Lookup[] current,
Map<Result,LookupListener> toAdd, Map<Result,LookupListener> toRemove
) {
assert Thread.holdsLock(ProxyLookup.this);
assert newData != null;
-
+
ImmutableInternalData previous = this.getData();
-
+
if (previous == newData) {
return Collections.emptyList();
}
@@ -314,14 +398,14 @@
private static final class R<T> extends WaitableResult<T> {
/** weak listener & result */
private final WeakResult<T> weakL;
-
+
/** list of listeners added */
private LookupListenerList listeners;
/** collection of Objects */
private Collection[] cache;
-
+
/** associated lookup */
private ImmutableInternalData data;
@@ -330,7 +414,7 @@
public R(ProxyLookup proxy, Lookup.Template<T> t) {
this.weakL = new WeakResult<T>(proxy, this, t);
}
-
+
private ProxyLookup proxy() {
return weakL.result.proxy;
}
@@ -339,7 +423,7 @@
private Result<T>[] newResults(int len) {
return new Result[len];
}
-
+
@Override
protected void finalize() {
weakL.result.run();
@@ -369,7 +453,7 @@
if (current != data) {
continue;
}
-
+
Lookup[] currentLkps = data.getLookups(false);
if (currentLkps.length != myLkps.length) {
continue BIG_LOOP;
@@ -379,8 +463,8 @@
continue BIG_LOOP;
}
}
-
- // some other thread might compute the result mean while.
+
+ // some other thread might compute the result mean while.
// if not finish the computation yourself
if (weakL.getResults() != null) {
return weakL.getResults();
@@ -548,7 +632,7 @@
public void resultChanged(LookupEvent ev) {
collectFires(null);
}
-
+
private static ThreadLocal<R<?>> IN = new ThreadLocal<>();
protected void collectFires(Collection<Object> evAndListeners) {
R<?> prev = IN.get();
@@ -563,7 +647,7 @@
IN.set(prev);
}
}
-
+
private void collImpl(Collection<Object> evAndListeners) {
boolean modified = true;
@@ -589,7 +673,7 @@
}
ll = listeners.getListenerList();
assert ll != null;
-
+
// ignore events if they arrive as a result of call to allItems
// or allInstances, bellow...
@@ -632,7 +716,7 @@
}
}
}
-
+
if (modified) {
LookupEvent ev = new LookupEvent(this);
AbstractLookup.notifyListeners(ll, ev, evAndListeners);
@@ -646,7 +730,7 @@
boolean callBeforeLookup, boolean callBeforeOnWait
) {
Template<T> template = template();
-
+
proxy().beforeLookup(callBeforeLookup, template);
Lookup.Result<T>[] arr = initResults();
@@ -691,11 +775,11 @@
synchronized (proxy()) {
Collection[] cc = getCache();
if (cc != oldCC) {
- // don't change the cache when it is based on
+ // don't change the cache when it is based on
// outdated results
return;
}
-
+
if (cc == null || cc == R.NO_CACHE) {
// initialize the cache to indicate this result is in use
setCache(cc = new Collection[3]);
@@ -707,14 +791,14 @@
cc[indexToCache] = ret;
}
}
-
+
}
}
private static final class WeakRef<T> extends WeakReference<R> implements Runnable {
final WeakResult<T> result;
final ProxyLookup proxy;
final Template<T> template;
-
+
public WeakRef(R r, WeakResult<T> result, ProxyLookup proxy, Template<T> template) {
super(r);
this.result = result;
@@ -727,17 +811,17 @@
proxy.unregisterTemplate(template);
}
}
-
-
+
+
private static final class WeakResult<T> extends WaitableResult<T> implements LookupListener, Runnable {
/** all results */
private Lookup.Result<T>[] results;
private final WeakRef<T> result;
-
+
public WeakResult(ProxyLookup proxy, R r, Template<T> t) {
this.result = new WeakRef<T>(r, this, proxy, t);
}
-
+
final void removeListeners() {
Lookup.Result<T>[] arr = this.getResults();
if (arr == null) {
@@ -822,15 +906,15 @@
return allItems();
}
} // end of WeakResult
-
+
static abstract class ImmutableInternalData extends Object {
static final ImmutableInternalData EMPTY = new EmptyInternalData();
static final Lookup[] EMPTY_ARR = new Lookup[0];
-
+
protected ImmutableInternalData() {
}
-
+
public static ImmutableInternalData create(Object lkp, Map<Template, Reference<R>> results) {
if (results.size() == 0 && lkp == EMPTY_ARR) {
return EMPTY;
@@ -839,7 +923,7 @@
Entry<Template,Reference<R>> e = results.entrySet().iterator().next();
return new SingleInternalData(lkp, e.getKey(), e.getValue());
}
-
+
return new RealInternalData(lkp, results);
}
@@ -850,7 +934,7 @@
final Collection<Reference<R>> references() {
return getResults().values();
}
-
+
final <T> ImmutableInternalData removeTemplate(ProxyLookup proxy, Template<T> template) {
if (getResults().containsKey(template)) {
HashMap<Template,Reference<R>> c = new HashMap<Template, Reference<ProxyLookup.R>>(getResults());
@@ -865,12 +949,12 @@
return this;
}
}
-
+
<T> R<T> findResult(ProxyLookup proxy, ImmutableInternalData[] newData, Template<T> template) {
assert Thread.holdsLock(proxy);
-
+
Map<Template,Reference<R>> map = getResults();
-
+
Reference<R> ref = map.get(template);
R r = (ref == null) ? null : ref.get();
@@ -878,7 +962,7 @@
newData[0] = this;
return convertResult(r);
}
-
+
HashMap<Template, Reference<R>> res = new HashMap<Template, Reference<R>>(map);
R<T> newR = new R<T>(proxy, template);
res.put(template, new java.lang.ref.SoftReference<R>(newR));
@@ -887,13 +971,13 @@
}
final ImmutableInternalData setLookupsNoFire(Lookup[] lookups, boolean skipCheck) {
Object l;
-
+
if (!skipCheck) {
Lookup[] previous = getLookups(false);
if (previous == lookups) {
return this;
}
-
+
if (previous.length == lookups.length) {
int same = 0;
for (int i = 0; i < previous.length; i++) {
@@ -907,7 +991,7 @@
}
}
}
-
+
if (lookups.length == 1) {
l = lookups[0];
assert l != null : "Cannot assign null delegate";
@@ -918,11 +1002,11 @@
l = lookups.clone();
}
}
-
+
if (isEmpty() && l == EMPTY_ARR) {
return this;
}
-
+
return create(l, getResults());
}
final Lookup[] getLookups(boolean clone) {
@@ -938,17 +1022,17 @@
}
}
final List<Lookup> getLookupsList() {
- return Arrays.asList(getLookups(false));
+ return Arrays.asList(getLookups(false));
}
} // end of ImmutableInternalData
-
+
private static final class SingleInternalData extends ImmutableInternalData {
/** lookups to delegate to (either Lookup or array of Lookups) */
private final Object lookups;
private final Template template;
private final Reference<ProxyLookup.R> result;
-
+
public SingleInternalData(Object lookups, Template<?> template, Reference<ProxyLookup.R> result) {
this.lookups = lookups;
this.template = template;
@@ -962,7 +1046,7 @@
protected Map<Template, Reference<R>> getResults() {
return Collections.singletonMap(template, result);
}
-
+
protected Object getRawLookups() {
return lookups;
}
@@ -990,7 +1074,7 @@
assert needsStrict = true;
return needsStrict && !isUnmodifiable(results) ? unmodifiableMap(results) : results;
}
-
+
@Override
protected Object getRawLookups() {
return lookups;
@@ -1008,7 +1092,7 @@
return res;
}
}
-
+
private static final class EmptyInternalData extends ImmutableInternalData {
EmptyInternalData() {
}
diff --git a/platform/openide.util.lookup/test/unit/src/org/openide/util/lookup/ProxyLookupFactoryMethodsTest.java b/platform/openide.util.lookup/test/unit/src/org/openide/util/lookup/ProxyLookupFactoryMethodsTest.java
new file mode 100644
index 0000000..715e9f1
--- /dev/null
+++ b/platform/openide.util.lookup/test/unit/src/org/openide/util/lookup/ProxyLookupFactoryMethodsTest.java
@@ -0,0 +1,196 @@
+/*
+ * 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.openide.util.lookup;
+
+import java.util.Arrays;
+import static java.util.Arrays.asList;
+import java.util.HashSet;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import org.openide.util.Lookup;
+import org.openide.util.LookupEvent;
+import org.openide.util.LookupListener;
+import org.openide.util.lookup.ProxyLookupFactoryMethodsTest.TThreadFactory.TThread;
+
+/**
+ *
+ * @author Tim Boudreau
+ */
+public class ProxyLookupFactoryMethodsTest {
+
+ private ProxyLookup.Controller controller1;
+ private ProxyLookup.Controller controller2;
+
+ private Lookup createWithSingleConsumer(Lookup... lookups) {
+ ProxyLookup.Controller controller = new ProxyLookup.Controller();
+ controller1 = controller;
+ ProxyLookup result = new ProxyLookup(controller);
+ result.setLookups(lookups);
+ return result;
+ }
+
+ private Lookup createWithBiConsumer(Lookup... lookups) {
+ ProxyLookup.Controller controller = new ProxyLookup.Controller();
+ controller2 = controller;
+ ProxyLookup result = new ProxyLookup(controller);
+ result.setLookups(lookups);
+ return result;
+ }
+
+ @Test
+ public void testCannotUseControllerOnMultipleLookups() {
+ ProxyLookup.Controller ctrllr = new ProxyLookup.Controller();
+ ProxyLookup first = new ProxyLookup(ctrllr);
+ assertTrue(first.lookupAll(String.class).isEmpty());
+ try {
+ ProxyLookup second = new ProxyLookup(ctrllr);
+ fail("Exception should have been thrown using controller more than "
+ + "once but was able to create " + second);
+ } catch (IllegalStateException ex) {
+ // ok
+ }
+ }
+
+ @Test
+ public void testStartWithEmptyController() {
+ ProxyLookup.Controller ctrllr = new ProxyLookup.Controller();
+ ProxyLookup lkp = new ProxyLookup(ctrllr);
+ assertTrue(lkp.lookupAll(String.class).isEmpty());
+ ctrllr.setLookups(Lookups.fixed("a"), Lookups.fixed("b"));
+ assertEquals(new HashSet<>(Arrays.asList("a", "b")),
+ new HashSet<>(lkp.lookupAll(String.class)));
+ }
+
+ @Test
+ public void testSimpleFactory() {
+ Lookup a = Lookups.fixed("a");
+ Lookup b = Lookups.fixed("b");
+ Lookup c = Lookups.fixed("c");
+
+ Lookup target = createWithSingleConsumer(a, b);
+ assertNotNull(controller1);
+ assertTrue(target.lookupAll(String.class).containsAll(asList("a", "b")));
+ assertFalse(target.lookupAll(String.class).contains("c"));
+
+ controller1.setLookups(new Lookup[]{a, b, c});
+ assertTrue(target.lookupAll(String.class).containsAll(asList("a", "b", "c")));
+
+ controller1.setLookups(new Lookup[0]);
+ assertTrue(target.lookupAll(String.class).isEmpty());
+ }
+
+ @Test
+ public void testThreadedFactory() throws Throwable {
+ ExecutorService svc = Executors.newSingleThreadExecutor(new TThreadFactory());
+ Lookup a = Lookups.fixed("a");
+ Lookup b = Lookups.fixed("b");
+ Lookup c = Lookups.fixed("c");
+
+ Lookup target = createWithBiConsumer(a, b);
+ Lookup.Result<String> result = target.lookupResult(String.class);
+
+ LL lis = new LL();
+ result.addLookupListener(lis);
+ // Ugh, ProxyLookup.LazyList does not implement the contract
+ // of Collection.equals().
+ assertEquals(new HashSet<>(result.allInstances()), new HashSet<>(Arrays.asList("a", "b")));
+
+ assertNotNull(controller2);
+ assertTrue(target.lookupAll(String.class).containsAll(asList("a", "b")));
+ assertFalse(target.lookupAll(String.class).contains("c"));
+
+ controller2.setLookups(svc, new Lookup[]{a, b, c});
+
+ lis.assertNotifiedInExecutor();
+
+ assertTrue(target.lookupAll(String.class).containsAll(asList("a", "b", "c")));
+ assertEquals(new HashSet<>(result.allInstances()), new HashSet<>(Arrays.asList("a", "b", "c")));
+
+ controller2.setLookups(svc, new Lookup[0]);
+ lis.assertNotifiedInExecutor();
+ assertTrue(target.lookupAll(String.class).isEmpty());
+ assertTrue(result.allInstances().isEmpty());
+
+ controller2.setLookups(null, new Lookup[]{b, c});
+ assertTrue(target.lookupAll(String.class).containsAll(asList("b", "c")));
+ assertEquals(new HashSet<>(result.allInstances()), new HashSet<>(Arrays.asList("b", "c")));
+ lis.assertNotifiedSynchronously();
+ }
+
+ static final class TThreadFactory implements ThreadFactory {
+
+ @Override
+ public Thread newThread(Runnable r) {
+ return new TThread(r);
+ }
+
+ static class TThread extends Thread {
+
+ TThread(Runnable r) {
+ super(r);
+ setName("test-thread");
+ setDaemon(true);
+ }
+ }
+ }
+
+ static class LL implements LookupListener {
+
+ private Thread notifyThread;
+ private CountDownLatch latch = new CountDownLatch(1);
+
+ void assertNotifiedInExecutor() throws InterruptedException {
+ CountDownLatch l;
+ synchronized (this) {
+ l = latch;
+ }
+ latch.await(10, TimeUnit.SECONDS);
+ Thread t;
+ synchronized (this) {
+ t = notifyThread;
+ notifyThread = null;
+ }
+ assertNotNull(t);
+ assertTrue(t instanceof TThread);
+ }
+
+ void assertNotifiedSynchronously() throws InterruptedException {
+ assertSame(Thread.currentThread(), notifyThread);
+ }
+
+ @Override
+ public void resultChanged(LookupEvent ev) {
+ CountDownLatch l;
+ synchronized (this) {
+ notifyThread = Thread.currentThread();
+ l = latch;
+ latch = new CountDownLatch(1);
+ assert l != null;
+ }
+ l.countDown();
+ }
+
+ }
+
+}