This is an automated email from the ASF dual-hosted git repository.
jlahoda pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new 42caa8adc5 Supporting source file launcher in VS Code extension
new f06522f08b Merge pull request #6262 from lahodaj/source-launcher-in-lsp
42caa8adc5 is described below
commit 42caa8adc5faf020865c772fa93b2b385804bddf
Author: Jan Lahoda <[email protected]>
AuthorDate: Mon Jul 31 13:07:04 2023 +0200
Supporting source file launcher in VS Code extension
---
java/java.api.common/nbproject/project.xml | 9 +
.../api/common/singlesourcefile/LaunchProcess.java | 46 ++-
.../SingleJavaSourceRunActionProvider.java | 8 +-
.../api/common/singlesourcefile/JavaFileTest.java | 3 +-
.../errors/EnablePreviewSingleSourceFile.java | 2 +-
.../hints/infrastructure/JavaErrorProvider.java | 4 +-
.../server/protocol/TextDocumentServiceImpl.java | 2 +-
.../lsp/server/protocol/WorkspaceServiceImpl.java | 13 +
.../singlesourcefile/CompilerOptionsQueryImpl.java | 307 +++++++++++++++++++++
.../EnablePreviewSingleSourceFile.java | 125 +++++++++
.../CompilerOptionsQueryImplTest.java | 109 ++++++++
java/java.lsp.server/vscode/src/extension.ts | 3 +-
.../java.lsp.server/vscode/src/runConfiguration.ts | 9 +-
.../modules/java/source/parsing/Hacks.java | 13 +
14 files changed, 623 insertions(+), 30 deletions(-)
diff --git a/java/java.api.common/nbproject/project.xml
b/java/java.api.common/nbproject/project.xml
index 1556766ea3..46ddf51403 100644
--- a/java/java.api.common/nbproject/project.xml
+++ b/java/java.api.common/nbproject/project.xml
@@ -137,6 +137,15 @@
<specification-version>1.61</specification-version>
</run-dependency>
</dependency>
+ <dependency>
+
<code-name-base>org.netbeans.modules.extexecution.base</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <release-version>2</release-version>
+ <specification-version>1.27</specification-version>
+ </run-dependency>
+ </dependency>
<dependency>
<code-name-base>org.netbeans.modules.java.platform</code-name-base>
<build-prerequisite/>
diff --git
a/java/java.api.common/src/org/netbeans/modules/java/api/common/singlesourcefile/LaunchProcess.java
b/java/java.api.common/src/org/netbeans/modules/java/api/common/singlesourcefile/LaunchProcess.java
index 2d080b64a9..4a574834c7 100644
---
a/java/java.api.common/src/org/netbeans/modules/java/api/common/singlesourcefile/LaunchProcess.java
+++
b/java/java.api.common/src/org/netbeans/modules/java/api/common/singlesourcefile/LaunchProcess.java
@@ -25,20 +25,22 @@ import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.logging.Level;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import org.netbeans.api.extexecution.base.ExplicitProcessParameters;
import org.netbeans.api.java.platform.JavaPlatformManager;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
+import org.openide.util.BaseUtilities;
final class LaunchProcess implements Callable<Process> {
private final FileObject fileObject;
private final JPDAStart start;
+ private final ExplicitProcessParameters params;
- LaunchProcess(FileObject fileObject, JPDAStart start) {
+ LaunchProcess(FileObject fileObject, JPDAStart start,
ExplicitProcessParameters params) {
this.fileObject = fileObject;
this.start = start;
+ this.params = params;
}
@Override
@@ -67,19 +69,28 @@ final class LaunchProcess implements Callable<Process> {
File javaFile = FileUtil.toFile(java);
String javaPath = javaFile.getAbsolutePath();
- Object argumentsObject =
fileObject.getAttribute(SingleSourceFileUtil.FILE_ARGUMENTS);
- String arguments = argumentsObject != null ? ((String)
argumentsObject).trim() : ""; // NOI18N
-
- Object vmOptionsObj =
fileObject.getAttribute(SingleSourceFileUtil.FILE_VM_OPTIONS);
- String vmOptions = vmOptionsObj != null ? ((String) vmOptionsObj)
: ""; // NOI18N
+ ExplicitProcessParameters paramsFromAttributes =
+ ExplicitProcessParameters.builder()
+
.args(readArgumentsFromAttribute(fileObject,
SingleSourceFileUtil.FILE_ARGUMENTS))
+
.launcherArgs(readArgumentsFromAttribute(fileObject,
SingleSourceFileUtil.FILE_VM_OPTIONS))
+
.workingDirectory(FileUtil.toFile(fileObject.getParent()))
+ .build();
+ ExplicitProcessParameters realParameters =
+ ExplicitProcessParameters.builder()
+ .combine(params)
+ .combine(paramsFromAttributes)
+ .build();
commandsList.add(javaPath);
- if (!vmOptions.isEmpty()) {
- commandsList.addAll(Arrays.asList(vmOptions.split(" ")));
//NOI18N
+
+ if (realParameters.getLauncherArguments()!= null) {
+ commandsList.addAll(realParameters.getLauncherArguments());
}
+
if (port != null) {
commandsList.add("-agentlib:jdwp=transport=dt_socket,address="
+ port + ",server=n"); //NOI18N
}
+
if (compile) {
commandsList.add("-cp");
commandsList.add(FileUtil.toFile(fileObject.getParent()).toString());
@@ -88,12 +99,13 @@ final class LaunchProcess implements Callable<Process> {
commandsList.add(fileObject.getNameExt());
}
- if (!arguments.isEmpty()) {
- commandsList.addAll(Arrays.asList(arguments.split(" ")));
//NOI18N
+ if (realParameters.getArguments() != null) {
+ commandsList.addAll(realParameters.getArguments());
}
ProcessBuilder runFileProcessBuilder = new
ProcessBuilder(commandsList);
-
runFileProcessBuilder.directory(FileUtil.toFile(fileObject.getParent()));
//NOI18N
+
runFileProcessBuilder.environment().putAll(realParameters.getEnvironmentVariables());
+
runFileProcessBuilder.directory(realParameters.getWorkingDirectory());
runFileProcessBuilder.redirectErrorStream(true);
runFileProcessBuilder.redirectOutput();
@@ -105,4 +117,12 @@ final class LaunchProcess implements Callable<Process> {
}
return null;
}
+
+ private static List<String> readArgumentsFromAttribute(FileObject
fileObject, String attributeName) {
+ Object argumentsObject = fileObject.getAttribute(attributeName);
+ if (!(argumentsObject instanceof String)) {
+ return null;
+ }
+ return Arrays.asList(BaseUtilities.parseParameters(((String)
argumentsObject).trim()));
+ }
}
diff --git
a/java/java.api.common/src/org/netbeans/modules/java/api/common/singlesourcefile/SingleJavaSourceRunActionProvider.java
b/java/java.api.common/src/org/netbeans/modules/java/api/common/singlesourcefile/SingleJavaSourceRunActionProvider.java
index e245e8e13a..ccbddcecd6 100644
---
a/java/java.api.common/src/org/netbeans/modules/java/api/common/singlesourcefile/SingleJavaSourceRunActionProvider.java
+++
b/java/java.api.common/src/org/netbeans/modules/java/api/common/singlesourcefile/SingleJavaSourceRunActionProvider.java
@@ -21,6 +21,7 @@ package org.netbeans.modules.java.api.common.singlesourcefile;
import java.util.concurrent.Future;
import org.netbeans.api.extexecution.ExecutionDescriptor;
import org.netbeans.api.extexecution.ExecutionService;
+import org.netbeans.api.extexecution.base.ExplicitProcessParameters;
import org.netbeans.spi.project.ActionProgress;
import org.netbeans.spi.project.ActionProvider;
import org.openide.filesystems.FileObject;
@@ -55,6 +56,7 @@ public final class SingleJavaSourceRunActionProvider
implements ActionProvider {
if (fileObject == null)
return;
+ ExplicitProcessParameters params =
ExplicitProcessParameters.buildExplicitParameters(context);
InputOutput io =
IOProvider.getDefault().getIO(Bundle.CTL_SingleJavaFile(), false);
ActionProgress progress = ActionProgress.start(context);
ExecutionDescriptor descriptor = new ExecutionDescriptor().
@@ -65,7 +67,7 @@ public final class SingleJavaSourceRunActionProvider
implements ActionProvider {
postExecution((exitCode) -> {
progress.finished(exitCode == 0);
});
- LaunchProcess process = invokeActionHelper(io, command, fileObject);
+ LaunchProcess process = invokeActionHelper(io, command, fileObject,
params);
ExecutionService exeService = ExecutionService.newService(
process,
descriptor, "Running Single Java File");
@@ -78,10 +80,10 @@ public final class SingleJavaSourceRunActionProvider
implements ActionProvider {
return fileObject != null;
}
- final LaunchProcess invokeActionHelper (InputOutput io, String command,
FileObject fo) {
+ final LaunchProcess invokeActionHelper (InputOutput io, String command,
FileObject fo, ExplicitProcessParameters params) {
JPDAStart start = ActionProvider.COMMAND_DEBUG_SINGLE.equals(command) ?
new JPDAStart(io, fo) : null;
- return new LaunchProcess(fo, start);
+ return new LaunchProcess(fo, start, params);
}
}
diff --git
a/java/java.api.common/test/unit/src/org/netbeans/modules/java/api/common/singlesourcefile/JavaFileTest.java
b/java/java.api.common/test/unit/src/org/netbeans/modules/java/api/common/singlesourcefile/JavaFileTest.java
index 1c92ace3f2..2c616b2b95 100644
---
a/java/java.api.common/test/unit/src/org/netbeans/modules/java/api/common/singlesourcefile/JavaFileTest.java
+++
b/java/java.api.common/test/unit/src/org/netbeans/modules/java/api/common/singlesourcefile/JavaFileTest.java
@@ -24,6 +24,7 @@ import java.io.FileWriter;
import java.io.InputStreamReader;
import java.util.logging.Logger;
import static junit.framework.TestCase.assertEquals;
+import org.netbeans.api.extexecution.base.ExplicitProcessParameters;
import org.netbeans.junit.NbTestCase;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
@@ -55,7 +56,7 @@ public class JavaFileTest extends NbTestCase {
FileObject javaFO = FileUtil.toFileObject(f1);
assertNotNull("FileObject found: " + f1, javaFO);
SingleJavaSourceRunActionProvider runActionProvider = new
SingleJavaSourceRunActionProvider();
- LaunchProcess process = runActionProvider.invokeActionHelper(null,
"run.single", javaFO);
+ LaunchProcess process = runActionProvider.invokeActionHelper(null,
"run.single", javaFO, ExplicitProcessParameters.empty());
BufferedReader reader
= new BufferedReader(new
InputStreamReader(process.call().getInputStream()));
StringBuilder builder = new StringBuilder();
diff --git
a/java/java.hints/src/org/netbeans/modules/java/hints/errors/EnablePreviewSingleSourceFile.java
b/java/java.hints/src/org/netbeans/modules/java/hints/errors/EnablePreviewSingleSourceFile.java
index 0ad7fe7c2f..b4f921e9df 100644
---
a/java/java.hints/src/org/netbeans/modules/java/hints/errors/EnablePreviewSingleSourceFile.java
+++
b/java/java.hints/src/org/netbeans/modules/java/hints/errors/EnablePreviewSingleSourceFile.java
@@ -74,7 +74,7 @@ public class EnablePreviewSingleSourceFile implements
PreviewEnabler {
if (compilerArgs.contains(SOURCE_FLAG)) {
compilerArgs = m.replaceAll("--enable-preview " + SOURCE_FLAG + "
" + newSourceLevel);
} else {
- compilerArgs = (compilerArgs.isEmpty() ? "" : " ") +
ENABLE_PREVIEW_FLAG + " " + SOURCE_FLAG + " " + newSourceLevel;
+ compilerArgs += (compilerArgs.isEmpty() ? "" : " ") +
ENABLE_PREVIEW_FLAG + " " + SOURCE_FLAG + " " + newSourceLevel;
}
file.setAttribute(FILE_VM_OPTIONS, compilerArgs);
storeEditableProperties(ep, file);
diff --git
a/java/java.hints/src/org/netbeans/modules/java/hints/infrastructure/JavaErrorProvider.java
b/java/java.hints/src/org/netbeans/modules/java/hints/infrastructure/JavaErrorProvider.java
index a756a2e9bd..9e5c5e69c4 100644
---
a/java/java.hints/src/org/netbeans/modules/java/hints/infrastructure/JavaErrorProvider.java
+++
b/java/java.hints/src/org/netbeans/modules/java/hints/infrastructure/JavaErrorProvider.java
@@ -194,14 +194,14 @@ public class JavaErrorProvider implements ErrorProvider {
for (Fix f : fixes) {
if (f instanceof IncompleteClassPath.ResolveFix) {
// We know that this is a project problem and that the
problems reported by ProjectProblemsProvider should be resolved
- CodeAction action = new CodeAction(f.getText(), new
Command(f.getText(), "nbls.java.project.resolveProjectProblems"));
+ CodeAction action = new CodeAction(f.getText(), new
Command(f.getText(), "java.project.resolveProjectProblems"));
result.add(action);
}
if (f instanceof
org.netbeans.modules.java.hints.errors.EnablePreview.ResolveFix) {
org.netbeans.modules.java.hints.errors.EnablePreview.ResolveFix rf =
(org.netbeans.modules.java.hints.errors.EnablePreview.ResolveFix) f;
List<Object> params = rf.getNewSourceLevel() != null ?
Arrays.asList(rf.getNewSourceLevel())
:
Collections.emptyList();
- CodeAction action = new CodeAction(f.getText(), new
Command(f.getText(), "nbls.java.project.enable.preview", params));
+ CodeAction action = new CodeAction(f.getText(), new
Command(f.getText(), "java.project.enable.preview", params));
result.add(action);
}
if (f instanceof ImportClass.FixImport) {
diff --git
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java
index bcdfc93221..dd6d9d723a 100644
---
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java
+++
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java
@@ -289,7 +289,7 @@ public class TextDocumentServiceImpl implements
TextDocumentService, LanguageCli
Lookup.getDefault().lookup(RefreshDocument.class).register(this);
}
- private void reRunDiagnostics() {
+ void reRunDiagnostics() {
for (String doc : server.getOpenedDocuments().getUris()) {
runDiagnosticTasks(doc, true);
}
diff --git
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java
index dc9b090e59..628d3d86d2 100644
---
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java
+++
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java
@@ -120,6 +120,7 @@ import org.netbeans.modules.java.lsp.server.Utils;
import
org.netbeans.modules.java.lsp.server.debugging.attach.AttachConfigurations;
import
org.netbeans.modules.java.lsp.server.debugging.attach.AttachNativeConfigurations;
import org.netbeans.modules.java.lsp.server.project.LspProjectInfo;
+import
org.netbeans.modules.java.lsp.server.singlesourcefile.CompilerOptionsQueryImpl;
import org.netbeans.modules.java.source.ElementHandleAccessor;
import org.netbeans.modules.java.source.ui.JavaSymbolProvider;
import org.netbeans.modules.java.source.ui.JavaTypeProvider;
@@ -1216,6 +1217,18 @@ public final class WorkspaceServiceImpl implements
WorkspaceService, LanguageCli
updateJavaImportPreferences(projects[0].getProjectDirectory(),
((JsonObject)
params.getSettings()).getAsJsonObject("netbeans").getAsJsonObject("java").getAsJsonObject("imports"));
}
});
+ boolean modified = false;
+ String newVMOptions = "";
+ JsonObject javaPlus = ((JsonObject)
params.getSettings()).getAsJsonObject("java+");
+ if (javaPlus != null) {
+ newVMOptions =
javaPlus.getAsJsonObject("runConfig").getAsJsonPrimitive("vmOptions").getAsString();
+ }
+ for (CompilerOptionsQueryImpl query :
Lookup.getDefault().lookupAll(CompilerOptionsQueryImpl.class)) {
+ modified |= query.setConfiguration(client, newVMOptions);
+ }
+ if (modified) {
+
((TextDocumentServiceImpl)server.getTextDocumentService()).reRunDiagnostics();
+ }
}
void updateJavaFormatPreferences(FileObject fo, JsonObject configuration) {
diff --git
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/singlesourcefile/CompilerOptionsQueryImpl.java
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/singlesourcefile/CompilerOptionsQueryImpl.java
new file mode 100644
index 0000000000..487b8557da
--- /dev/null
+++
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/singlesourcefile/CompilerOptionsQueryImpl.java
@@ -0,0 +1,307 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.lsp.server.singlesourcefile;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.stream.Collectors;
+import javax.lang.model.SourceVersion;
+import javax.swing.event.ChangeListener;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.classpath.JavaClassPathConstants;
+import org.netbeans.api.project.FileOwnerQuery;
+import org.netbeans.api.project.Project;
+import org.netbeans.modules.java.lsp.server.protocol.NbCodeLanguageClient;
+import org.netbeans.spi.java.classpath.ClassPathFactory;
+import org.netbeans.spi.java.classpath.ClassPathImplementation;
+import org.netbeans.spi.java.classpath.ClassPathProvider;
+import org.netbeans.spi.java.classpath.PathResourceImplementation;
+import org.netbeans.spi.java.classpath.support.ClassPathSupport;
+import org.netbeans.spi.java.queries.CompilerOptionsQueryImplementation;
+import org.netbeans.spi.java.queries.SourceLevelQueryImplementation2;
+import org.openide.filesystems.FileObject;
+import org.openide.util.ChangeSupport;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.ServiceProvider;
+import org.openide.util.lookup.ServiceProviders;
+
+@ServiceProviders({
+ @ServiceProvider(service=CompilerOptionsQueryImplementation.class,
position=99),
+ @ServiceProvider(service=ClassPathProvider.class, position=9999),
//DefaultClassPathProvider has 10000
+ @ServiceProvider(service=CompilerOptionsQueryImpl.class)
+})
+public class CompilerOptionsQueryImpl implements
CompilerOptionsQueryImplementation, ClassPathProvider,
SourceLevelQueryImplementation2 {
+
+ private final Map<NbCodeLanguageClient, Configuration> file2Configuration
= new WeakHashMap<>();
+
+ @Override
+ public CompilerOptionsQueryImplementation.Result getOptions(FileObject
file) {
+ if (isSingleSourceFile(file)) {
+ NbCodeLanguageClient client =
Lookup.getDefault().lookup(NbCodeLanguageClient.class);
+ if (client != null) {
+ return getConfiguration(client).compilerOptions;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public ClassPath findClassPath(FileObject file, String type) {
+ if (isSingleSourceFile(file)) {
+ NbCodeLanguageClient client =
Lookup.getDefault().lookup(NbCodeLanguageClient.class);
+ if (client != null) {
+ switch (type) {
+ case ClassPath.COMPILE: case
JavaClassPathConstants.MODULE_CLASS_PATH:
+ return getConfiguration(client).compileClassPath;
+ case JavaClassPathConstants.MODULE_COMPILE_PATH:
+ return getConfiguration(client).moduleCompileClassPath;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public SourceLevelQueryImplementation2.Result getSourceLevel(FileObject
file) {
+ if (isSingleSourceFile(file)) {
+ NbCodeLanguageClient client =
Lookup.getDefault().lookup(NbCodeLanguageClient.class);
+ if (client != null) {
+ return getConfiguration(client).sourceLevelResult;
+ }
+ }
+ return null;
+ }
+
+ public boolean setConfiguration(NbCodeLanguageClient client, String
vmOptions) {
+ return getConfiguration(client).setConfiguration(vmOptions);
+ }
+
+ private synchronized Configuration getConfiguration(NbCodeLanguageClient
client) {
+ return file2Configuration.computeIfAbsent(client, cl -> {
+ return new Configuration();
+ });
+ }
+
+ //copied from SingleSourceFileUtil:
+ static boolean isSingleSourceFile(FileObject fObj) {
+ Project p = FileOwnerQuery.getOwner(fObj);
+ if (p != null || !fObj.getExt().equalsIgnoreCase("java")) { //NOI18N
+ return false;
+ }
+ return true;
+ }
+
+ private static final class OptionsResultImpl extends
CompilerOptionsQueryImplementation.Result {
+
+ private final ChangeSupport cs = new ChangeSupport(this);
+ private List<String> args = Collections.emptyList();
+
+ public List<String> doParse(String line) {
+ return parseLine(line);
+ }
+
+ private void setArguments(List<String> newArguments) {
+ synchronized (this) {
+ args = newArguments;
+ }
+ cs.fireChange();
+ }
+
+ @Override
+ public synchronized List<? extends String> getArguments() {
+ return args;
+ }
+
+ @Override
+ public void addChangeListener(ChangeListener listener) {
+ cs.addChangeListener(listener);
+ }
+
+ @Override
+ public void removeChangeListener(ChangeListener listener) {
+ cs.addChangeListener(listener);
+ }
+
+ }
+
+ private static final class SourceLevelResultImpl implements
SourceLevelQueryImplementation2.Result {
+
+ private static final String DEFAULT_SL =
String.valueOf(SourceVersion.latest().ordinal() -
SourceVersion.RELEASE_0.ordinal());
+ private final ChangeSupport cs = new ChangeSupport(this);
+ private String sourceLevel = DEFAULT_SL;
+
+ @Override
+ public synchronized String getSourceLevel() {
+ return sourceLevel;
+ }
+
+ private void setSourceLevel(String sourceLevel) {
+ synchronized (this) {
+ this.sourceLevel = sourceLevel;
+ }
+ cs.fireChange();
+ }
+
+ @Override
+ public void addChangeListener(ChangeListener listener) {
+ cs.addChangeListener(listener);
+ }
+
+ @Override
+ public void removeChangeListener(ChangeListener listener) {
+ cs.addChangeListener(listener);
+ }
+
+ }
+
+ private static final class Configuration {
+ private List<String> currentOptions = Collections.emptyList();
+ public final OptionsResultImpl compilerOptions;
+ public final SourceLevelResultImpl sourceLevelResult;
+ public final ProxyClassPathImplementation
compileClassPathImplementation;
+ public final ProxyClassPathImplementation
compileModulePathImplementation;
+ public final ClassPath compileClassPath;
+ public final ClassPath moduleCompileClassPath;
+
+ public Configuration() {
+ compilerOptions = new OptionsResultImpl();
+ sourceLevelResult = new SourceLevelResultImpl();
+ compileClassPathImplementation = new
ProxyClassPathImplementation();
+ compileModulePathImplementation = new
ProxyClassPathImplementation();
+ compileClassPath =
ClassPathFactory.createClassPath(compileClassPathImplementation);
+ moduleCompileClassPath =
ClassPathFactory.createClassPath(compileModulePathImplementation);
+ }
+
+ private boolean setConfiguration(String vmOptions) {
+ List<String> newOptions = compilerOptions.doParse(vmOptions);
+
+ synchronized (this) {
+ if (currentOptions.equals(newOptions)) {
+ return false;
+ }
+
+ currentOptions = newOptions;
+ }
+
+ compilerOptions.setArguments(newOptions);
+
+ String classpath = "";
+ String modulepath = "";
+ String sourceLevel = SourceLevelResultImpl.DEFAULT_SL;
+
+ for (int i = 0; i < newOptions.size() - 1; i++) {
+ String parameter = newOptions.get(i + 1);
+
+ switch (newOptions.get(i)) {
+ case "-classpath": case "-cp": case "--class-path":
+ classpath = parameter;
+ break;
+ case "--module-path": case "-p":
+ modulepath = parameter;
+ break;
+ case "--source":
+ sourceLevel = parameter;
+ break;
+ }
+ }
+
+ compileClassPathImplementation.setDelegates(spec2CP(classpath));
+ compileModulePathImplementation.setDelegates(spec2CP(modulepath));
+
+ sourceLevelResult.setSourceLevel(sourceLevel);
+
+ return true;
+ }
+
+ private List<ClassPathImplementation> spec2CP(String spec) {
+ List<PathResourceImplementation> entries;
+
+ if (spec.isEmpty()) {
+ entries = Collections.emptyList();
+ } else {
+ entries = ClassPathSupport.createClassPath(spec)
+ .entries()
+ .stream()
+ .map(e -> e.getURL())
+
.map(ClassPathSupport::createResource)
+ .collect(Collectors.toList());
+ }
+ return
Arrays.asList(ClassPathSupport.createClassPathImplementation(entries));
+ }
+ }
+
+ private static final class ProxyClassPathImplementation implements
ClassPathImplementation {
+ private final PropertyChangeSupport pcs = new
PropertyChangeSupport(this);
+ private List<ClassPathImplementation> delegates =
Collections.emptyList();
+ private List<? extends PathResourceImplementation> cachedResources =
null;
+
+ public void setDelegates(List<ClassPathImplementation> delegates) {
+ synchronized (delegates) {
+ this.delegates = new ArrayList<>(delegates);
+ this.cachedResources = null;
+ }
+ pcs.firePropertyChange(PROP_RESOURCES, null, null);
+ }
+
+ @Override
+ public List<? extends PathResourceImplementation> getResources() {
+ List<ClassPathImplementation> delegates;
+
+ synchronized (this) {
+ if (cachedResources != null) {
+ return cachedResources;
+ }
+
+ delegates = this.delegates;
+ }
+
+ List<PathResourceImplementation> allResources = new ArrayList<>();
+
+ delegates.stream().map(d ->
d.getResources()).forEach(allResources::addAll);
+
+ allResources = Collections.unmodifiableList(allResources);
+
+ synchronized (this) {
+ if (cachedResources == null && this.delegates == delegates) {
+ cachedResources = allResources;
+ }
+ }
+
+ return allResources;
+ }
+
+ @Override
+ public void addPropertyChangeListener(PropertyChangeListener listener)
{
+ pcs.addPropertyChangeListener(listener);
+ }
+
+ @Override
+ public void removePropertyChangeListener(PropertyChangeListener
listener) {
+ pcs.removePropertyChangeListener(listener);
+ }
+
+ }
+
+}
diff --git
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/singlesourcefile/EnablePreviewSingleSourceFile.java
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/singlesourcefile/EnablePreviewSingleSourceFile.java
new file mode 100644
index 0000000000..562f9fc328
--- /dev/null
+++
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/singlesourcefile/EnablePreviewSingleSourceFile.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.lsp.server.singlesourcefile;
+
+import com.google.gson.JsonPrimitive;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.eclipse.lsp4j.ConfigurationItem;
+import org.eclipse.lsp4j.ConfigurationParams;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.project.FileOwnerQuery;
+import org.openide.filesystems.FileObject;
+import org.openide.util.Parameters;
+import org.netbeans.api.project.Project;
+import org.netbeans.modules.java.hints.spi.preview.PreviewEnabler;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.netbeans.modules.java.lsp.server.protocol.NbCodeLanguageClient;
+import org.netbeans.modules.java.lsp.server.protocol.UpdateConfigParams;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ * Handle error rule "compiler.err.preview.feature.disabled.plural" and provide
+ * the fix for Single Source Java File.
+ *
+ * @author Arunava Sinha
+ */
+public class EnablePreviewSingleSourceFile implements PreviewEnabler {
+
+ private static final String ENABLE_PREVIEW_FLAG = "--enable-preview"; //
NOI18N
+ private static final String SOURCE_FLAG = "--source"; // NOI18N
+ private static final Pattern SOURCE_FLAG_PATTERN =
Pattern.compile(SOURCE_FLAG + "[ \t]+[0-9]+");
+
+ private FileObject file;
+
+ private EnablePreviewSingleSourceFile(@NonNull FileObject file) {
+ Parameters.notNull("file", file); //NOI18N
+ this.file = file;
+ }
+
+ @Override
+ public void enablePreview(String newSourceLevel) throws Exception {
+ NbCodeLanguageClient client =
Lookup.getDefault().lookup(NbCodeLanguageClient.class);
+ if (client == null) {
+ return ;
+ }
+
+ ConfigurationItem conf = new ConfigurationItem();
+ conf.setScopeUri(Utils.toUri(file));
+ conf.setSection("java+.runConfig.vmOptions"); //XXX
+ client.configuration(new
ConfigurationParams(Collections.singletonList(conf))).thenApply(c -> {
+ String compilerArgs = ((JsonPrimitive) ((List<Object>)
c).get(0)).getAsString();
+ if (compilerArgs == null) {
+ compilerArgs = "";
+ }
+
+ Matcher m = SOURCE_FLAG_PATTERN.matcher(compilerArgs);
+ String realNewSourceLevel = newSourceLevel;
+
+ if (realNewSourceLevel == null) {
+ realNewSourceLevel = getJdkRunVersion();
+ }
+
+ if (compilerArgs.contains(SOURCE_FLAG)) {
+ compilerArgs = m.replaceAll(ENABLE_PREVIEW_FLAG + " " +
SOURCE_FLAG + " " + realNewSourceLevel);
+ } else {
+ compilerArgs += (compilerArgs.isEmpty() ? "" : " ") +
ENABLE_PREVIEW_FLAG + " " + SOURCE_FLAG + " " + realNewSourceLevel;
+ }
+ client.configurationUpdate(new
UpdateConfigParams("java+.runConfig", "vmOptions", compilerArgs));
+ return null;
+ });
+ }
+
+ private static String getJdkRunVersion() {
+ String javaVersion = System.getProperty("java.specification.version");
//NOI18N
+ if (javaVersion.startsWith("1.")) { //NOI18N
+ javaVersion = javaVersion.substring(2);
+ }
+
+ return javaVersion;
+ }
+
+ @ServiceProvider(service=Factory.class, position=10_000_000)
+ public static final class FactoryImpl implements Factory {
+
+ @Override
+ public PreviewEnabler enablerFor(FileObject file) {
+ if (file != null) {
+ NbCodeLanguageClient client =
Lookup.getDefault().lookup(NbCodeLanguageClient.class);
+
+ if (client == null) {
+ return null;
+ }
+
+ Project prj = FileOwnerQuery.getOwner(file);
+
+ if (prj == null) {
+ return new EnablePreviewSingleSourceFile(file);
+ }
+ }
+
+ return null;
+ }
+
+ }
+
+}
diff --git
a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/singlesourcefile/CompilerOptionsQueryImplTest.java
b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/singlesourcefile/CompilerOptionsQueryImplTest.java
new file mode 100644
index 0000000000..548aac166a
--- /dev/null
+++
b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/singlesourcefile/CompilerOptionsQueryImplTest.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.lsp.server.singlesourcefile;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import static junit.framework.TestCase.assertEquals;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.classpath.JavaClassPathConstants;
+import org.netbeans.junit.NbTestCase;
+import org.netbeans.modules.java.lsp.server.TestCodeLanguageClient;
+import org.netbeans.modules.java.lsp.server.protocol.NbCodeLanguageClient;
+import org.netbeans.spi.java.queries.CompilerOptionsQueryImplementation;
+import org.netbeans.spi.java.queries.SourceLevelQueryImplementation2;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.util.lookup.Lookups;
+
+public class CompilerOptionsQueryImplTest extends NbTestCase {
+
+ public CompilerOptionsQueryImplTest(String name) {
+ super(name);
+ }
+
+ public void testParseCommandLine1() throws Exception {
+ File wd = getWorkDir();
+ FileObject wdFO = FileUtil.toFileObject(wd);
+ FileObject testFO = FileUtil.createData(wdFO, "Test.java");
+ File testJar = new File(wd, "test.jar");
+ CompilerOptionsQueryImpl query = new CompilerOptionsQueryImpl();
+ NbCodeLanguageClient client = new TestCodeLanguageClient() {
+ };
+
+ query.setConfiguration(client, "-classpath " +
testJar.getAbsolutePath() + " --module-path " + testJar.getAbsolutePath() + "
--source 21 --enable-preview");
+
+ AtomicReference<CompilerOptionsQueryImplementation.Result>
optionsResult = new AtomicReference<>();
+ AtomicInteger optionsResultModificationCount = new AtomicInteger();
+ AtomicReference<ClassPath> compileCP = new AtomicReference<>();
+ AtomicInteger compileCPModificationCount = new AtomicInteger();
+ AtomicReference<ClassPath> compileModuleCP = new AtomicReference<>();
+ AtomicInteger compileModuleCPModificationCount = new AtomicInteger();
+ AtomicReference<SourceLevelQueryImplementation2.Result>
sourceLevelResult = new AtomicReference<>();
+ AtomicInteger sourceLevelResultModificationCount = new AtomicInteger();
+
+ Lookups.executeWith(Lookups.fixed(client), () -> {
+ optionsResult.set(query.getOptions(testFO));
+ compileCP.set(query.findClassPath(testFO, ClassPath.COMPILE));
+ compileModuleCP.set(query.findClassPath(testFO,
JavaClassPathConstants.MODULE_COMPILE_PATH));
+ sourceLevelResult.set(query.getSourceLevel(testFO));
+ });
+
+ optionsResult.get().addChangeListener(evt ->
optionsResultModificationCount.incrementAndGet());
+ assertEquals(Arrays.asList("-classpath", testJar.getAbsolutePath(),
"--module-path", testJar.getAbsolutePath(), "--source", "21",
"--enable-preview"),
+ optionsResult.get().getArguments());
+ assertEquals(0, optionsResultModificationCount.get());
+
+ compileCP.get().addPropertyChangeListener(evt ->
compileCPModificationCount.incrementAndGet());
+ assertEquals(testJar.getAbsolutePath(),
+
compileCP.get().toString(ClassPath.PathConversionMode.PRINT));
+ assertEquals(0, compileCPModificationCount.get());
+
+ compileModuleCP.get().addPropertyChangeListener(evt ->
compileModuleCPModificationCount.incrementAndGet());
+ assertEquals(testJar.getAbsolutePath(),
+
compileModuleCP.get().toString(ClassPath.PathConversionMode.PRINT));
+ assertEquals(0, compileModuleCPModificationCount.get());
+
+ sourceLevelResult.get().addChangeListener(evt ->
sourceLevelResultModificationCount.incrementAndGet());
+ assertEquals("21",
+ sourceLevelResult.get().getSourceLevel());
+ assertEquals(0, sourceLevelResultModificationCount.get());
+
+ query.setConfiguration(client, "-cp " + testJar.getAbsolutePath() + "
-p " + testJar.getAbsolutePath() + " --source 17");
+
+ assertEquals(Arrays.asList("-cp", testJar.getAbsolutePath(), "-p",
testJar.getAbsolutePath(), "--source", "17"),
+ optionsResult.get().getArguments());
+ assertEquals(1, optionsResultModificationCount.get());
+
+ assertEquals(testJar.getAbsolutePath(),
+
compileCP.get().toString(ClassPath.PathConversionMode.PRINT));
+ assertEquals(2, compileCPModificationCount.get());
+
+ assertEquals(testJar.getAbsolutePath(),
+
compileModuleCP.get().toString(ClassPath.PathConversionMode.PRINT));
+ assertEquals(2, compileModuleCPModificationCount.get());
+
+ assertEquals("17",
+ sourceLevelResult.get().getSourceLevel());
+ assertEquals(1, sourceLevelResultModificationCount.get());
+ }
+
+}
diff --git a/java/java.lsp.server/vscode/src/extension.ts
b/java/java.lsp.server/vscode/src/extension.ts
index df9803109d..8ce5fe55b4 100644
--- a/java/java.lsp.server/vscode/src/extension.ts
+++ b/java/java.lsp.server/vscode/src/extension.ts
@@ -942,7 +942,8 @@ function doActivateWithJDK(specifiedJDK: string | null,
context: ExtensionContex
synchronize: {
configurationSection: [
'netbeans.format',
- 'netbeans.java.imports'
+ 'netbeans.java.imports',
+ 'java+.runConfig.vmOptions'
],
fileEvents: [
workspace.createFileSystemWatcher('**/*.java')
diff --git a/java/java.lsp.server/vscode/src/runConfiguration.ts
b/java/java.lsp.server/vscode/src/runConfiguration.ts
index e6b148ae93..e8f1e44f37 100644
--- a/java/java.lsp.server/vscode/src/runConfiguration.ts
+++ b/java/java.lsp.server/vscode/src/runConfiguration.ts
@@ -23,14 +23,7 @@ import { homedir } from 'os';
export async function initializeRunConfiguration(): Promise<boolean> {
const java = await vscode.workspace.findFiles('**/*.java',
'**/node_modules/**', 1);
if (java?.length > 0) {
- const maven = await vscode.workspace.findFiles('pom.xml',
'**/node_modules/**', 1);
- if (maven?.length > 0) {
- return true;
- }
- const gradle = await vscode.workspace.findFiles('build.gradle',
'**/node_modules/**', 1);
- if (gradle?.length > 0) {
- return true;
- }
+ return true;
}
return false;
}
diff --git
a/java/java.source.base/src/org/netbeans/modules/java/source/parsing/Hacks.java
b/java/java.source.base/src/org/netbeans/modules/java/source/parsing/Hacks.java
index 0b69a1d310..f187aa339a 100644
---
a/java/java.source.base/src/org/netbeans/modules/java/source/parsing/Hacks.java
+++
b/java/java.source.base/src/org/netbeans/modules/java/source/parsing/Hacks.java
@@ -20,8 +20,11 @@ package org.netbeans.modules.java.source.parsing;
import com.sun.tools.javac.util.JCDiagnostic;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag;
+import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import javax.tools.Diagnostic;
import
org.netbeans.modules.java.source.parsing.CompilationInfoImpl.RichDiagnostic;
@@ -30,6 +33,8 @@ import
org.netbeans.modules.java.source.parsing.CompilationInfoImpl.RichDiagnost
* @author lahvac
*/
public class Hacks {
+ private static final Logger LOG = Logger.getLogger(Hacks.class.getName());
+
public static boolean isSyntaxError(Diagnostic<?> d) {
JCDiagnostic jcd = getJCDiagnostic(d);
if (jcd == null) {
@@ -70,6 +75,14 @@ public class Hacks {
return ((JCDiagnostic)d);
} else if (d instanceof RichDiagnostic && ((RichDiagnostic)
d).getDelegate() instanceof JCDiagnostic) {
return (JCDiagnostic)((RichDiagnostic)d).getDelegate();
+ } else if
("org.netbeans.modules.java.source.parsing.CompilationInfoImpl$DiagnosticListenerImpl$D".equals(d.getClass().getName()))
{
+ try {
+ Field delegate = d.getClass().getDeclaredField("delegate");
+ delegate.setAccessible(true);
+ return getJCDiagnostic((Diagnostic<?>) delegate.get(d));
+ } catch (Exception ex) {
+ LOG.log(Level.FINE, null, ex);
+ }
}
return null;
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists