Skip to content

Commit

Permalink
Provide diagnostics and quickfix when open non-project java file
Browse files Browse the repository at this point in the history
Signed-off-by: Jinbo Wang <[email protected]>
  • Loading branch information
testforstephen committed Feb 27, 2020
1 parent 0585be7 commit 510aa04
Show file tree
Hide file tree
Showing 14 changed files with 620 additions and 38 deletions.
3 changes: 3 additions & 0 deletions org.eclipse.jdt.ls.core/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@
<command
id="java.project.isTestFile">
</command>
<command
id="java.project.refreshDiagnostics">
</command>
</delegateCommandHandler>
</extension>
<extension
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*******************************************************************************
* Copyright (c) 2020 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Microsoft Corporation - initial API and implementation
*******************************************************************************/

package org.eclipse.jdt.ls.core.internal;

import java.util.HashMap;
import java.util.Map;

public class DiagnosticsState {
private ErrorLevel globalErrorLevel = ErrorLevel.SYNTAX_ERROR;
private Map<String, ErrorLevel> customizedErrorLevels = new HashMap<>();

public boolean isOnlySyntaxReported(String uri) {
return customizedErrorLevels.getOrDefault(uri, globalErrorLevel) == ErrorLevel.SYNTAX_ERROR;
}

public void setErrorLevel(String uri, boolean syntaxOnly) {
customizedErrorLevels.put(uri, syntaxOnly ? ErrorLevel.SYNTAX_ERROR : ErrorLevel.COMPILATION_ERROR);
}

public void setGlobalErrorLevel(boolean syntaxOnly) {
globalErrorLevel = syntaxOnly ? ErrorLevel.SYNTAX_ERROR : ErrorLevel.COMPILATION_ERROR;
customizedErrorLevels.clear();
}

enum ErrorLevel {
SYNTAX_ERROR,
COMPILATION_ERROR
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.apache.commons.lang3.StringUtils;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.ls.core.internal.commands.BuildPathCommand;
import org.eclipse.jdt.ls.core.internal.commands.DiagnosticsCommand;
import org.eclipse.jdt.ls.core.internal.commands.OrganizeImportsCommand;
import org.eclipse.jdt.ls.core.internal.commands.ProjectCommand;
import org.eclipse.jdt.ls.core.internal.commands.SourceAttachmentCommand;
Expand Down Expand Up @@ -65,6 +66,8 @@ public Object executeCommand(String commandId, List<Object> arguments, IProgress
return ProjectCommand.getClasspaths((String) arguments.get(0), JSONUtility.toModel(arguments.get(1), ClasspathOptions.class));
case "java.project.isTestFile":
return ProjectCommand.isTestFile((String) arguments.get(0));
case "java.project.refreshDiagnostics":
return DiagnosticsCommand.refreshDiagnostics((String) arguments.get(0), (String) arguments.get(1), (boolean) arguments.get(2));
default:
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ public class JavaLanguageServerPlugin extends Plugin {
private ContextTypeRegistry fContextTypeRegistry;
private JavaLanguageServerTemplateStore fTemplateStore;

private DiagnosticsState nonProjectDiagnosticsState;

public static LanguageServer getLanguageServer() {
return pluginInstance == null ? null : pluginInstance.languageServer;
}
Expand Down Expand Up @@ -169,6 +171,7 @@ public void start(BundleContext bundleContext) throws Exception {
logException(e.getMessage(), e);
}
contentProviderManager = new ContentProviderManager(preferenceManager);
nonProjectDiagnosticsState = new DiagnosticsState();
logInfo(getClass() + " is started");
configureProxy();
}
Expand Down Expand Up @@ -357,6 +360,10 @@ public static JavaLanguageServerPlugin getInstance() {
return pluginInstance;
}

public static DiagnosticsState getNonProjectDiagnosticsState() {
return pluginInstance.nonProjectDiagnosticsState;
}

public static void log(IStatus status) {
if (context != null) {
Platform.getLog(JavaLanguageServerPlugin.context.getBundle()).log(status);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*******************************************************************************
* Copyright (c) 2020 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Microsoft Corporation - initial API and implementation
*******************************************************************************/

package org.eclipse.jdt.ls.core.internal.commands;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.IBuffer;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IProblemRequestor;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.WorkingCopyOwner;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.manipulation.CoreASTProvider;
import org.eclipse.jdt.ls.core.internal.DiagnosticsState;
import org.eclipse.jdt.ls.core.internal.DocumentAdapter;
import org.eclipse.jdt.ls.core.internal.JDTUtils;
import org.eclipse.jdt.ls.core.internal.JavaClientConnection;
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
import org.eclipse.jdt.ls.core.internal.handlers.DiagnosticsHandler;

public class DiagnosticsCommand {

public static Object refreshDiagnostics(String uri, String scope, boolean syntaxOnly) {
DiagnosticsState state = JavaLanguageServerPlugin.getNonProjectDiagnosticsState();
boolean refreshAll = false;
if (Objects.equals(scope, "thisFile")) {
state.setErrorLevel(uri, syntaxOnly);
} else if (Objects.equals(scope, "anyNonProjectFile")) {
state.setGlobalErrorLevel(syntaxOnly);
refreshAll = true;
}

ICompilationUnit target = refreshAll ? null : JDTUtils.resolveCompilationUnit(uri);
refreshDiagnostics(target);

return null;
}

private static void refreshDiagnostics(final ICompilationUnit target) {
final JavaClientConnection connection = JavaLanguageServerPlugin.getInstance().getClientConnection();
if (connection == null) {
JavaLanguageServerPlugin.logError("The client connection doesn't exist.");
return;
}

try {
ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() {
@Override
public void run(IProgressMonitor monitor) throws CoreException {
List<ICompilationUnit> units = getNonProjectCompilationUnits(target, monitor);
for (ICompilationUnit unit : units) {
publishDiagnostics(connection, unit, monitor);
}
}
}, new NullProgressMonitor());
} catch (CoreException e) {
JavaLanguageServerPlugin.logException("Refresh Diagnostics for non-project Java files", e);
}
}

private static List<ICompilationUnit> getNonProjectCompilationUnits(ICompilationUnit target, IProgressMonitor monitor) {
List<ICompilationUnit> candidates = new ArrayList<>();
CoreASTProvider sharedASTProvider = CoreASTProvider.getInstance();
if (target == null) {
List<ICompilationUnit> workingCopies = Arrays.asList(JavaCore.getWorkingCopies(null));
for (ICompilationUnit wc : workingCopies) {
if (JDTUtils.isDefaultProject(wc) || !JDTUtils.isOnClassPath(wc)) {
candidates.add(wc);
}
}
} else {
CompilationUnit unit = sharedASTProvider.getAST(target, CoreASTProvider.WAIT_YES, monitor);
candidates.add((ICompilationUnit) unit.getTypeRoot());
}

return candidates;
}

private static void publishDiagnostics(JavaClientConnection connection, ICompilationUnit unit, IProgressMonitor monitor) throws JavaModelException {
final DiagnosticsHandler handler = new DiagnosticsHandler(connection, unit);
WorkingCopyOwner wcOwner = new WorkingCopyOwner() {
@Override
public IBuffer createBuffer(ICompilationUnit workingCopy) {
ICompilationUnit original = workingCopy.getPrimary();
IResource resource = original.getResource();
if (resource instanceof IFile) {
return new DocumentAdapter(workingCopy, (IFile) resource);
}
return DocumentAdapter.Null;
}

@Override
public IProblemRequestor getProblemRequestor(ICompilationUnit workingCopy) {
return handler;
}

};
int flags = ICompilationUnit.FORCE_PROBLEM_DETECTION | ICompilationUnit.ENABLE_BINDINGS_RECOVERY | ICompilationUnit.ENABLE_STATEMENTS_RECOVERY;
unit.reconcile(ICompilationUnit.NO_AST, flags, wcOwner, monitor);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager;
import org.eclipse.jdt.ls.core.internal.text.correction.AssignToVariableAssistCommandProposal;
import org.eclipse.jdt.ls.core.internal.text.correction.CUCorrectionCommandProposal;
import org.eclipse.jdt.ls.core.internal.text.correction.NonProjectFixProcessor;
import org.eclipse.jdt.ls.core.internal.text.correction.QuickAssistProcessor;
import org.eclipse.jdt.ls.core.internal.text.correction.RefactoringCorrectionCommandProposal;
import org.eclipse.jdt.ls.core.internal.text.correction.SourceAssistProcessor;
Expand All @@ -62,6 +63,7 @@ public class CodeActionHandler {
private RefactorProcessor refactorProcessor;
private QuickAssistProcessor quickAssistProcessor;
private SourceAssistProcessor sourceAssistProcessor;
private NonProjectFixProcessor nonProjectFixProcessor;

private PreferenceManager preferenceManager;

Expand All @@ -71,6 +73,7 @@ public CodeActionHandler(PreferenceManager preferenceManager) {
this.sourceAssistProcessor = new SourceAssistProcessor(preferenceManager);
this.quickAssistProcessor = new QuickAssistProcessor(preferenceManager);
this.refactorProcessor = new RefactorProcessor(preferenceManager);
this.nonProjectFixProcessor = new NonProjectFixProcessor(preferenceManager);
}

public List<Either<Command, CodeAction>> getCodeActionCommands(CodeActionParams params, IProgressMonitor monitor) {
Expand Down Expand Up @@ -99,10 +102,12 @@ public List<Either<Command, CodeAction>> getCodeActionCommands(CodeActionParams
codeActionKinds.addAll(defaultCodeActionKinds);
}

List<Either<Command, CodeAction>> codeActions = new ArrayList<>();
List<ChangeCorrectionProposal> proposals = new ArrayList<>();
ChangeCorrectionProposalComparator comparator = new ChangeCorrectionProposalComparator();
if (containsKind(codeActionKinds, CodeActionKind.QuickFix)) {
try {
codeActions.addAll(nonProjectFixProcessor.getCorrections(params, context, locations));
List<ChangeCorrectionProposal> quickfixProposals = this.quickFixProcessor.getCorrections(context, locations);
quickfixProposals.sort(comparator);
proposals.addAll(quickfixProposals);
Expand Down Expand Up @@ -131,7 +136,6 @@ public List<Either<Command, CodeAction>> getCodeActionCommands(CodeActionParams
}
}

List<Either<Command, CodeAction>> codeActions = new ArrayList<>();
try {
for (ChangeCorrectionProposal proposal : proposals) {
Optional<Either<Command, CodeAction>> codeActionFromProposal = getCodeActionFromProposal(proposal, params.getContext());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
*******************************************************************************/
package org.eclipse.jdt.ls.core.internal.handlers;


import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
Expand All @@ -25,6 +24,7 @@
import org.eclipse.jdt.core.IProblemRequestor;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.compiler.problem.DefaultProblem;
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
import org.eclipse.jdt.ls.core.internal.JDTUtils;
import org.eclipse.jdt.ls.core.internal.JavaClientConnection;
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
Expand All @@ -40,13 +40,18 @@ public class DiagnosticsHandler implements IProblemRequestor {

private final ICompilationUnit cu;
private final List<IProblem> problems;
private final List<IProblem> unreportedProblems;
private final String uri;
private final JavaClientConnection connection;
private boolean reportAllErrors = true;
private boolean isDefaultProject;

public static final int NON_PROJECT_JAVA_FILE = 0x10;
public static final int NOT_ON_CLASSPATH = 0x20;

public DiagnosticsHandler(JavaClientConnection conn, ICompilationUnit cu) {
problems = new ArrayList<>();
unreportedProblems = new ArrayList<>();
this.cu = cu;
this.uri = JDTUtils.toURI(cu);
this.connection = conn;
Expand All @@ -56,11 +61,17 @@ public DiagnosticsHandler(JavaClientConnection conn, ICompilationUnit cu) {

@Override
public void acceptProblem(IProblem problem) {
if (reportAllErrors || isSyntaxLikeError(problem)) {
if (reportAllErrors() || isSyntaxLikeError(problem)) {
problems.add(problem);
} else {
unreportedProblems.add(problem);
}
}

private boolean reportAllErrors() {
return reportAllErrors || !JavaLanguageServerPlugin.getNonProjectDiagnosticsState().isOnlySyntaxReported(uri);
}

public boolean isSyntaxLikeError(IProblem problem) {
//Syntax issues are always reported
if ((problem.getID() & IProblem.Syntax) != 0) {
Expand Down Expand Up @@ -119,12 +130,39 @@ public void beginReporting() {

@Override
public void endReporting() {
JavaLanguageServerPlugin.logInfo(problems.size() + " problems reported for " + this.uri.substring(this.uri.lastIndexOf('/')));
List<IProblem> allProblems = problems;
if (!unreportedProblems.isEmpty() && JavaLanguageServerPlugin.getNonProjectDiagnosticsState().isOnlySyntaxReported(uri)) {
allProblems = new ArrayList<>();
allProblems.add(createNonProjectProblem());
allProblems.addAll(problems);
}
JavaLanguageServerPlugin.logInfo(allProblems.size() + " problems reported for " + this.uri.substring(this.uri.lastIndexOf('/')));
boolean isDiagnosticTagSupported = JavaLanguageServerPlugin.getPreferencesManager().getClientPreferences().isDiagnosticTagSupported();
PublishDiagnosticsParams $ = new PublishDiagnosticsParams(ResourceUtils.toClientUri(uri), toDiagnosticsArray(this.cu, problems, isDiagnosticTagSupported));
PublishDiagnosticsParams $ = new PublishDiagnosticsParams(ResourceUtils.toClientUri(uri), toDiagnosticsArray(this.cu, allProblems, isDiagnosticTagSupported));
this.connection.publishDiagnostics($);
}

private IProblem createNonProjectProblem() {
String fileName = cu.getElementName();
String projectName = cu.getJavaProject().getProject().getName();
String message = null;
int problemId = NON_PROJECT_JAVA_FILE;
if (isDefaultProject) {
message = "File " + fileName + " is non-project file, only syntax errors are reported";
problemId = NON_PROJECT_JAVA_FILE;
} else {
message = "File " + fileName + " isn't on the classpath of project " + projectName + ", only syntax errors are reported";
problemId = NOT_ON_CLASSPATH;
}

return new DefaultProblem(
fileName.toCharArray(),
message,
problemId,
null,
ProblemSeverities.Error, 0, 0, 1, 1);
}

@Override
public boolean isActive() {
return true;
Expand Down Expand Up @@ -216,7 +254,7 @@ public static List<DiagnosticTag> getDiagnosticTag(int id) {
}

private static DiagnosticSeverity convertSeverity(IProblem problem) {
if(problem.isError()) {
if (problem.isError()) {
return DiagnosticSeverity.Error;
}
if (problem.isWarning() && (problem.getID() != IProblem.Task)) {
Expand Down Expand Up @@ -260,6 +298,13 @@ public void clearDiagnostics() {
* @noreference public for test purposes only
*/
public List<IProblem> getProblems() {
return problems;
List<IProblem> allProblems = problems;
if (!unreportedProblems.isEmpty() && JavaLanguageServerPlugin.getNonProjectDiagnosticsState().isOnlySyntaxReported(uri)) {
allProblems = new ArrayList<>();
allProblems.add(createNonProjectProblem());
allProblems.addAll(problems);
}

return allProblems;
}
}
Loading

0 comments on commit 510aa04

Please sign in to comment.