From a3575c86c4a20b72efe4f7a438e3d651b512da1e Mon Sep 17 00:00:00 2001 From: Yaohai Zheng Date: Thu, 22 Nov 2018 22:52:04 +0800 Subject: [PATCH 1/6] Add rename compilation unit support. --- .../jdt/ls/core/internal/ChangeUtil.java | 109 ++++++--- .../refactoring/rename/RenameProcessor.java | 222 ------------------ .../corrections/CorrectionMessages.properties | 2 +- .../ReorgCorrectionsSubProcessor.java | 29 +++ .../internal/handlers/CodeActionHandler.java | 15 +- .../handlers/DocumentLifeCycleHandler.java | 3 + .../core/internal/handlers/InitHandler.java | 2 +- .../internal/handlers/JDTLanguageServer.java | 17 +- .../handlers/PrepareRenameHandler.java | 78 ++++++ .../core/internal/handlers/RenameHandler.java | 9 +- .../handlers/WorkspaceDiagnosticsHandler.java | 14 +- .../handlers/WorkspaceEventsHandler.java | 7 +- .../preferences/ClientPreferences.java | 11 + .../correction/ReorgQuickFixTest.java | 19 ++ .../internal/handlers/RenameHandlerTest.java | 42 ++-- 15 files changed, 279 insertions(+), 300 deletions(-) delete mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/rename/RenameProcessor.java create mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandler.java diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/ChangeUtil.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/ChangeUtil.java index 659d0666cb..8eb32d0023 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/ChangeUtil.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/ChangeUtil.java @@ -39,6 +39,12 @@ import org.eclipse.jdt.ls.core.internal.corext.refactoring.changes.RenameCompilationUnitChange; import org.eclipse.jdt.ls.core.internal.corext.refactoring.changes.RenamePackageChange; import org.eclipse.jdt.ls.core.internal.corext.util.JavaElementUtil; +import org.eclipse.lsp4j.CreateFile; +import org.eclipse.lsp4j.CreateFileOptions; +import org.eclipse.lsp4j.DeleteFile; +import org.eclipse.lsp4j.DeleteFileOptions; +import org.eclipse.lsp4j.RenameFile; +import org.eclipse.lsp4j.ResourceOperation; import org.eclipse.lsp4j.TextDocumentEdit; import org.eclipse.lsp4j.WorkspaceEdit; import org.eclipse.lsp4j.jsonrpc.messages.Either; @@ -56,19 +62,19 @@ */ public class ChangeUtil { + private static final String TEMP_FILE_NAME = ".temp"; + /** - * Converts changes to resource changes if resource changes are supported by the - * client otherwise converts to TextEdit changes. + * Converts changes to resource operations if resource operations are supported + * by the client otherwise converts to TextEdit changes. * * @param change * changes after Refactoring operation * @param edit * instance of workspace edit changes - * @param manager - * preference manager * @throws CoreException */ - public static void convertChanges(Change change, WorkspaceEdit edit) throws CoreException { + public static void convertCompositeChange(Change change, WorkspaceEdit edit) throws CoreException { if (!(change instanceof CompositeChange)) { return; } @@ -78,31 +84,34 @@ public static void convertChanges(Change change, WorkspaceEdit edit) throws Core if (ch instanceof DynamicValidationRefactoringChange) { CompositeChange compositeChange = (CompositeChange) ch; for (Change child : compositeChange.getChildren()) { - convertCompositeChange(child, edit); + doConvertCompositeChange(child, edit); } } else { - convertCompositeChange(ch, edit); + doConvertCompositeChange(ch, edit); } } } - private static void convertCompositeChange(Change change, WorkspaceEdit edit) throws CoreException { - Object modifiedElement = change.getModifiedElement(); - if (!(modifiedElement instanceof IJavaElement)) { - return; - } - if (change instanceof TextChange) { - convertTextChange(edit, (IJavaElement) modifiedElement, (TextChange) change); - } else if (change instanceof ResourceChange) { - ResourceChange resourceChange = (ResourceChange) change; - convertResourceChange(edit, resourceChange); + /** + * Converts changes to resource operations if resource operations are supported + * by the client otherwise converts to TextEdit changes. + * + * @param resourceChange + * resource changes after Refactoring operation + * @param edit + * instance of workspace edit changes + * @throws CoreException + */ + public static void convertResourceChange(ResourceChange resourceChange, WorkspaceEdit edit) throws CoreException { + if (!JavaLanguageServerPlugin.getPreferencesManager().getClientPreferences().isResourceOperationSupported()) { + return; } - } - private static void convertResourceChange(WorkspaceEdit edit, ResourceChange resourceChange) throws CoreException { - if (!JavaLanguageServerPlugin.getPreferencesManager().getClientPreferences().isWorkspaceEditResourceChangesSupported()) { - return; + List> changes = edit.getDocumentChanges(); + if (changes == null) { + changes = new LinkedList<>(); + edit.setDocumentChanges(changes); } // Resource change is needed and supported by client @@ -113,6 +122,20 @@ private static void convertResourceChange(WorkspaceEdit edit, ResourceChange res } } + private static void doConvertCompositeChange(Change change, WorkspaceEdit edit) throws CoreException { + Object modifiedElement = change.getModifiedElement(); + if (!(modifiedElement instanceof IJavaElement)) { + return; + } + + if (change instanceof TextChange) { + convertTextChange(edit, (IJavaElement) modifiedElement, (TextChange) change); + } else if (change instanceof ResourceChange) { + ResourceChange resourceChange = (ResourceChange) change; + convertResourceChange(resourceChange, edit); + } + } + private static void convertRenamePackcageChange(WorkspaceEdit edit, RenamePackageChange packageChange) throws CoreException { IPackageFragment pack = (IPackageFragment) packageChange.getModifiedElement(); List units = new ArrayList<>(); @@ -134,35 +157,47 @@ private static void convertRenamePackcageChange(WorkspaceEdit edit, RenamePackag convertTextEdit(edit, cu, textEdit); } - org.eclipse.lsp4j.ResourceChange rc = new org.eclipse.lsp4j.ResourceChange(); IPath newPackageFragment = new Path(packageChange.getNewName().replace('.', IPath.SEPARATOR)); IPath oldPackageFragment = new Path(packageChange.getOldName().replace('.', IPath.SEPARATOR)); IPath newPackagePath = pack.getResource().getLocation().removeLastSegments(oldPackageFragment.segmentCount()).append(newPackageFragment); - rc.setNewUri(ResourceUtils.fixURI(newPackagePath.toFile().toURI())); + if (packageChange.getRenameSubpackages()) { - rc.setCurrent(ResourceUtils.fixURI(pack.getResource().getRawLocationURI())); - edit.getResourceChanges().add(Either.forLeft(rc)); + RenameFile renameFile = new RenameFile(); + renameFile.setNewUri(ResourceUtils.fixURI(newPackagePath.toFile().toURI())); + renameFile.setOldUri(ResourceUtils.fixURI(pack.getResource().getRawLocationURI())); + edit.getDocumentChanges().add(Either.forRight(renameFile)); } else { - edit.getResourceChanges().add(Either.forLeft(rc)); + CreateFile createFile = new CreateFile(); + createFile.setUri(ResourceUtils.fixURI(newPackagePath.append(TEMP_FILE_NAME).toFile().toURI())); + createFile.setOptions(new CreateFileOptions(false, true)); + edit.getDocumentChanges().add(Either.forRight(createFile)); + for (ICompilationUnit unit : units) { - org.eclipse.lsp4j.ResourceChange cuResourceChange = new org.eclipse.lsp4j.ResourceChange(); - cuResourceChange.setCurrent(ResourceUtils.fixURI(unit.getResource().getLocationURI())); + RenameFile cuResourceChange = new RenameFile(); + cuResourceChange.setOldUri(ResourceUtils.fixURI(unit.getResource().getLocationURI())); IPath newCUPath = newPackagePath.append(unit.getPath().lastSegment()); cuResourceChange.setNewUri(ResourceUtils.fixURI(newCUPath.toFile().toURI())); - edit.getResourceChanges().add(Either.forLeft(cuResourceChange)); + edit.getDocumentChanges().add(Either.forRight(cuResourceChange)); } + + // Workaround: https://github.com/Microsoft/language-server-protocol/issues/272 + DeleteFile deleteFile = new DeleteFile(); + deleteFile.setUri(ResourceUtils.fixURI(newPackagePath.append(TEMP_FILE_NAME).toFile().toURI())); + deleteFile.setOptions(new DeleteFileOptions(false, true)); + edit.getDocumentChanges().add(Either.forRight(deleteFile)); + } } private static void convertCUResourceChange(WorkspaceEdit edit, RenameCompilationUnitChange cuChange) { ICompilationUnit modifiedCU = (ICompilationUnit) cuChange.getModifiedElement(); - org.eclipse.lsp4j.ResourceChange rc = new org.eclipse.lsp4j.ResourceChange(); + RenameFile rf = new RenameFile(); String newCUName = cuChange.getNewName(); IPath currentPath = modifiedCU.getResource().getLocation(); - rc.setCurrent(ResourceUtils.fixURI(modifiedCU.getResource().getRawLocationURI())); + rf.setOldUri(ResourceUtils.fixURI(modifiedCU.getResource().getRawLocationURI())); IPath newPath = currentPath.removeLastSegments(1).append(newCUName); - rc.setNewUri(ResourceUtils.fixURI(newPath.toFile().toURI())); - edit.getResourceChanges().add(Either.forLeft(rc)); + rf.setNewUri(ResourceUtils.fixURI(newPath.toFile().toURI())); + edit.getDocumentChanges().add(Either.forRight(rf)); } private static void convertTextChange(WorkspaceEdit root, IJavaElement element, TextChange textChange) { @@ -182,13 +217,13 @@ private static void convertTextEdit(WorkspaceEdit root, ICompilationUnit unit, T for (TextEdit textEdit : children) { TextEditConverter converter = new TextEditConverter(unit, textEdit); String uri = JDTUtils.toURI(unit); - if (JavaLanguageServerPlugin.getPreferencesManager().getClientPreferences().isWorkspaceEditResourceChangesSupported()) { - List> changes = root.getResourceChanges(); + if (JavaLanguageServerPlugin.getPreferencesManager().getClientPreferences().isResourceOperationSupported()) { + List> changes = root.getDocumentChanges(); if (changes == null) { changes = new LinkedList<>(); - root.setResourceChanges(changes); + root.setDocumentChanges(changes); } - changes.add(Either.forRight(converter.convertToTextDocumentEdit(0))); + changes.add(Either.forLeft(converter.convertToTextDocumentEdit(0))); } else { Map> changes = root.getChanges(); if (changes.containsKey(uri)) { diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/rename/RenameProcessor.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/rename/RenameProcessor.java deleted file mode 100644 index a142b5b9fb..0000000000 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/rename/RenameProcessor.java +++ /dev/null @@ -1,222 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. -* All rights reserved. This program and the accompanying materials -* are made available under the terms of the Eclipse Public License v1.0 -* which accompanies this distribution, and is available at -* http://www.eclipse.org/legal/epl-v10.html -* -* Contributors: -* Microsoft Corporation - initial API and implementation -*******************************************************************************/ - -package org.eclipse.jdt.ls.core.internal.corext.refactoring.rename; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.Status; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IJavaElement; -import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.IMethod; -import org.eclipse.jdt.core.IPackageFragment; -import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.jdt.core.ToolFactory; -import org.eclipse.jdt.core.compiler.IScanner; -import org.eclipse.jdt.core.compiler.ITerminalSymbols; -import org.eclipse.jdt.core.compiler.InvalidInputException; -import org.eclipse.jdt.core.search.IJavaSearchConstants; -import org.eclipse.jdt.core.search.IJavaSearchScope; -import org.eclipse.jdt.core.search.MethodDeclarationMatch; -import org.eclipse.jdt.core.search.MethodReferenceMatch; -import org.eclipse.jdt.core.search.SearchEngine; -import org.eclipse.jdt.core.search.SearchMatch; -import org.eclipse.jdt.core.search.SearchParticipant; -import org.eclipse.jdt.core.search.SearchPattern; -import org.eclipse.jdt.core.search.SearchRequestor; -import org.eclipse.jdt.ls.core.internal.JDTUtils; -import org.eclipse.jdt.ls.core.internal.TextEditConverter; -import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager; -import org.eclipse.lsp4j.ResourceChange; -import org.eclipse.lsp4j.TextDocumentEdit; -import org.eclipse.lsp4j.WorkspaceEdit; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.eclipse.text.edits.ReplaceEdit; -import org.eclipse.text.edits.TextEdit; - -public class RenameProcessor { - - protected IJavaElement fElement; - - private IJavaProject fProjectCache; - private IScanner fScannerCache; - - protected PreferenceManager preferenceManager; - - public RenameProcessor(IJavaElement selectedElement, PreferenceManager preferenceManager) { - fElement = selectedElement; - this.preferenceManager = preferenceManager; - } - - public void renameOccurrences(WorkspaceEdit edit, String newName, IProgressMonitor monitor) throws CoreException { - if (fElement == null || !canRename()) { - return; - } - - IJavaElement[] elementsToSearch = null; - - if (fElement instanceof IMethod) { - elementsToSearch = RippleMethodFinder.getRelatedMethods((IMethod) fElement, monitor, null); - } else { - elementsToSearch = new IJavaElement[] { fElement }; - } - - SearchPattern pattern = createOccurrenceSearchPattern(elementsToSearch); - if (pattern == null) { - return; - } - SearchEngine engine = new SearchEngine(); - engine.search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, createSearchScope(), new SearchRequestor() { - - @Override - public void acceptSearchMatch(SearchMatch match) throws CoreException { - Object o = match.getElement(); - if (o instanceof IJavaElement) { - IJavaElement element = (IJavaElement) o; - ICompilationUnit compilationUnit = (ICompilationUnit) element.getAncestor(IJavaElement.COMPILATION_UNIT); - if (compilationUnit == null) { - return; - } - TextEdit replaceEdit = collectMatch(match, element, compilationUnit, newName); - if (replaceEdit != null) { - convert(edit, compilationUnit, replaceEdit); - } - } - } - }, monitor); - - } - - protected SearchPattern createOccurrenceSearchPattern(IJavaElement[] elements) throws CoreException { - if (elements == null || elements.length == 0) { - return null; - } - Set set = new HashSet<>(Arrays.asList(elements)); - Iterator iter = set.iterator(); - IJavaElement first = iter.next(); - SearchPattern pattern = SearchPattern.createPattern(first, IJavaSearchConstants.ALL_OCCURRENCES); - if (pattern == null) { - throw new CoreException(Status.CANCEL_STATUS); - } - while (iter.hasNext()) { - IJavaElement each = iter.next(); - SearchPattern nextPattern = SearchPattern.createPattern(each, IJavaSearchConstants.ALL_OCCURRENCES); - if (nextPattern == null) { - throw new CoreException(Status.CANCEL_STATUS); - } - pattern = SearchPattern.createOrPattern(pattern, nextPattern); - } - return pattern; - } - - protected void convert(WorkspaceEdit root, ICompilationUnit unit, TextEdit edits) { - TextEditConverter converter = new TextEditConverter(unit, edits); - String uri = JDTUtils.toURI(unit); - if (preferenceManager.getClientPreferences().isWorkspaceEditResourceChangesSupported()) { - List> changes = root.getResourceChanges(); - if (changes == null) { - changes = new LinkedList<>(); - root.setResourceChanges(changes); - } - changes.add(Either.forRight(converter.convertToTextDocumentEdit(0))); - } else { - Map> changes = root.getChanges(); - if (changes.containsKey(uri)) { - changes.get(uri).addAll(converter.convert()); - } else { - changes.put(uri, converter.convert()); - } - } - } - - protected IJavaSearchScope createSearchScope() throws JavaModelException { - return SearchEngine.createWorkspaceScope(); - } - - protected boolean canRename() throws CoreException { - if (fElement instanceof IPackageFragment) { - return false; - } - ICompilationUnit compilationUnit = (ICompilationUnit) fElement.getAncestor(IJavaElement.COMPILATION_UNIT); - return compilationUnit != null; - } - - private TextEdit collectMatch(SearchMatch match, IJavaElement element, ICompilationUnit unit, String newName) throws IndexOutOfBoundsException, JavaModelException { - if (match instanceof MethodReferenceMatch && ((MethodReferenceMatch) match).isSuperInvocation() && match.getAccuracy() == SearchMatch.A_INACCURATE) { - return null; - } - - if (!(element instanceof IMethod) || match.isImplicit()) { - return new ReplaceEdit(match.getOffset(), match.getLength(), newName); - } - - int start = match.getOffset(); - int length = match.getLength(); - String matchText = unit.getBuffer().getText(start, length); - - //direct match: - if (newName.equals(matchText)) { - return new ReplaceEdit(match.getOffset(), match.getLength(), newName); - } - - // lambda expression - if (match instanceof MethodDeclarationMatch && match.getElement() instanceof IMethod && ((IMethod) match.getElement()).isLambdaMethod()) { - // don't touch the lambda - return null; - } - - //Not a standard reference -- use scanner to find last identifier token before left parenthesis: - IScanner scanner = getScanner(unit); - scanner.setSource(matchText.toCharArray()); - int simpleNameStart = -1; - int simpleNameEnd = -1; - try { - int token = scanner.getNextToken(); - while (token != ITerminalSymbols.TokenNameEOF && token != ITerminalSymbols.TokenNameLPAREN) { // reference in code includes arguments in parentheses - if (token == ITerminalSymbols.TokenNameIdentifier) { - simpleNameStart = scanner.getCurrentTokenStartPosition(); - simpleNameEnd = scanner.getCurrentTokenEndPosition(); - } - token = scanner.getNextToken(); - } - } catch (InvalidInputException e) { - //ignore - } - if (simpleNameStart != -1) { - match.setOffset(start + simpleNameStart); - match.setLength(simpleNameEnd + 1 - simpleNameStart); - } - return new ReplaceEdit(match.getOffset(), match.getLength(), newName); - } - - protected IScanner getScanner(ICompilationUnit unit) { - IJavaProject project = unit.getJavaProject(); - if (project.equals(fProjectCache)) { - return fScannerCache; - } - - fProjectCache = project; - String sourceLevel = project.getOption(JavaCore.COMPILER_SOURCE, true); - String complianceLevel = project.getOption(JavaCore.COMPILER_COMPLIANCE, true); - fScannerCache = ToolFactory.createScanner(false, false, false, sourceLevel, complianceLevel); - return fScannerCache; - } -} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/CorrectionMessages.properties b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/CorrectionMessages.properties index 0da595ee4a..a9e717ac4d 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/CorrectionMessages.properties +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/CorrectionMessages.properties @@ -48,7 +48,7 @@ CUCorrectionProposal_error_title=Quick Fix CUCorrectionProposal_error_message=An exception occurred while applying the quick fix. ReorgCorrectionsSubProcessor_renametype_description=Rename type to ''{0}'' -ReorgCorrectionsSubProcessor_renamecu_description=Rename compilation unit to ''{0}'' +ReorgCorrectionsSubProcessor_renamecu_description=Rename file to ''{0}'' ReorgCorrectionsSubProcessor_movecu_default_description=Move ''{0}'' to the default package ReorgCorrectionsSubProcessor_movecu_description=Move ''{0}'' to package ''{1}'' ReorgCorrectionsSubProcessor_no_required_jre_title=Change To {0} Quick Fix diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/ReorgCorrectionsSubProcessor.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/ReorgCorrectionsSubProcessor.java index d407d9201f..a206666af0 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/ReorgCorrectionsSubProcessor.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/ReorgCorrectionsSubProcessor.java @@ -20,18 +20,24 @@ import org.eclipse.core.runtime.IStatus; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.JavaConventions; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.Modifier; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.manipulation.OrganizeImportsOperation; import org.eclipse.jdt.core.refactoring.CompilationUnitChange; +import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels; import org.eclipse.jdt.internal.corext.fix.IProposableFix; +import org.eclipse.jdt.internal.corext.util.JavaModelUtil; +import org.eclipse.jdt.internal.corext.util.Messages; import org.eclipse.jdt.internal.ui.text.correction.IProblemLocationCore; import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; import org.eclipse.jdt.ls.core.internal.corext.fix.UnusedCodeFix; +import org.eclipse.jdt.ls.core.internal.corext.refactoring.changes.RenameCompilationUnitChange; import org.eclipse.jdt.ls.core.internal.corrections.CorrectionMessages; import org.eclipse.jdt.ls.core.internal.corrections.IInvocationContext; import org.eclipse.jface.text.IDocument; @@ -44,6 +50,7 @@ public class ReorgCorrectionsSubProcessor { public static void getWrongTypeNameProposals(IInvocationContext context, IProblemLocationCore problem, Collection proposals) { ICompilationUnit cu= context.getCompilationUnit(); + boolean isLinked = cu.getResource().isLinked(); IJavaProject javaProject= cu.getJavaProject(); String sourceLevel= javaProject.getOption(JavaCore.COMPILER_SOURCE, true); @@ -65,6 +72,9 @@ public static void getWrongTypeNameProposals(IInvocationContext context, IProble String newTypeName= JavaCore.removeJavaLikeExtension(cu.getElementName()); + boolean hasOtherPublicTypeBefore = false; + + boolean found = false; List types= root.types(); for (int i= 0; i < types.size(); i++) { AbstractTypeDeclaration curr= types.get(i); @@ -72,11 +82,30 @@ public static void getWrongTypeNameProposals(IInvocationContext context, IProble if (newTypeName.equals(curr.getName().getIdentifier())) { return; } + if (!found && Modifier.isPublic(curr.getModifiers())) { + hasOtherPublicTypeBefore = true; + } + } else { + found = true; } } + if (!JavaConventions.validateJavaTypeName(newTypeName, sourceLevel, compliance).matches(IStatus.ERROR)) { proposals.add(new CorrectMainTypeNameProposal(cu, context, currTypeName, newTypeName, IProposalRelevance.RENAME_TYPE)); } + + if (!hasOtherPublicTypeBefore && JavaLanguageServerPlugin.getPreferencesManager().getClientPreferences().isResourceOperationSupported()) { + String newCUName = JavaModelUtil.getRenamedCUName(cu, currTypeName); + ICompilationUnit newCU = ((IPackageFragment) (cu.getParent())).getCompilationUnit(newCUName); + if (!newCU.exists() && !isLinked && !JavaConventions.validateCompilationUnitName(newCUName, sourceLevel, compliance).matches(IStatus.ERROR)) { + RenameCompilationUnitChange change = new RenameCompilationUnitChange(cu, newCUName); + + // rename CU + String label = Messages.format(CorrectionMessages.ReorgCorrectionsSubProcessor_renamecu_description, BasicElementLabels.getResourceName(newCUName)); + proposals.add(new CUCorrectionProposal(label, CodeActionKind.QuickFix, cu, change, IProposalRelevance.RENAME_CU)); + } + } + } public static void getWrongPackageDeclNameProposals(IInvocationContext context, IProblemLocationCore problem, diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/CodeActionHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/CodeActionHandler.java index d572e9ea51..06b2a6d54a 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/CodeActionHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/CodeActionHandler.java @@ -24,9 +24,9 @@ import org.eclipse.jdt.core.IJavaModelMarker; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.manipulation.CoreASTProvider; -import org.eclipse.jdt.core.refactoring.CompilationUnitChange; import org.eclipse.jdt.internal.ui.text.correction.IProblemLocationCore; import org.eclipse.jdt.internal.ui.text.correction.ProblemLocationCore; +import org.eclipse.jdt.ls.core.internal.ChangeUtil; import org.eclipse.jdt.ls.core.internal.JDTUtils; import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; import org.eclipse.jdt.ls.core.internal.TextEditConverter; @@ -48,6 +48,7 @@ import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.CompositeChange; import org.eclipse.ltk.core.refactoring.TextChange; +import org.eclipse.ltk.core.refactoring.resource.ResourceChange; public class CodeActionHandler { @@ -161,21 +162,17 @@ private int getProblemId(Diagnostic diagnostic) { return $; } - private static WorkspaceEdit convertChangeToWorkspaceEdit(ICompilationUnit unit, Change change) { + private static WorkspaceEdit convertChangeToWorkspaceEdit(ICompilationUnit unit, Change change) throws CoreException { WorkspaceEdit $ = new WorkspaceEdit(); if (change instanceof TextChange) { TextEditConverter converter = new TextEditConverter(unit, ((TextChange) change).getEdit()); String uri = JDTUtils.toURI(unit); $.getChanges().put(uri, converter.convert()); + } else if (change instanceof ResourceChange) { + ChangeUtil.convertResourceChange((ResourceChange) change, $); } else if (change instanceof CompositeChange) { - for (Change c : ((CompositeChange) change).getChildren()) { - if (c instanceof CompilationUnitChange) { - TextEditConverter converter = new TextEditConverter(((CompilationUnitChange) c).getCompilationUnit(), ((TextChange) c).getEdit()); - String uri = JDTUtils.toURI(((CompilationUnitChange) c).getCompilationUnit()); - $.getChanges().put(uri, converter.convert()); - } - } + ChangeUtil.convertCompositeChange(change, $); } return $; diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/DocumentLifeCycleHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/DocumentLifeCycleHandler.java index 434fe1450d..a5a65cbb90 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/DocumentLifeCycleHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/DocumentLifeCycleHandler.java @@ -378,6 +378,9 @@ public void handleClosed(DidCloseTextDocumentParams params) { return; } try { + synchronized (toReconcile) { + toReconcile.remove(unit); + } if (JDTUtils.isDefaultProject(unit) || !JDTUtils.isOnClassPath(unit) || unit.getResource().isDerived()) { new DiagnosticsHandler(connection, unit).clearDiagnostics(); } diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/InitHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/InitHandler.java index 14ed72bbdb..f33746b8a9 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/InitHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/InitHandler.java @@ -154,7 +154,7 @@ InitializeResult initialize(InitializeParams param) { capabilities.setSignatureHelpProvider(SignatureHelpHandler.createOptions()); } if (!preferenceManager.getClientPreferences().isRenameDynamicRegistrationSupported()) { - capabilities.setRenameProvider(Boolean.TRUE); + capabilities.setRenameProvider(RenameHandler.createOptions()); } if (!preferenceManager.getClientPreferences().isCodeActionDynamicRegistered()) { capabilities.setCodeActionProvider(Boolean.TRUE); diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/JDTLanguageServer.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/JDTLanguageServer.java index 170a708e4e..7816f85513 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/JDTLanguageServer.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/JDTLanguageServer.java @@ -90,6 +90,8 @@ import org.eclipse.lsp4j.InitializeResult; import org.eclipse.lsp4j.InitializedParams; import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.PrepareRenameResult; +import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.ReferenceParams; import org.eclipse.lsp4j.Registration; import org.eclipse.lsp4j.RegistrationParams; @@ -251,7 +253,7 @@ private void syncCapabilitiesToSettings() { toggleCapability(preferenceManager.getPreferences().isSignatureHelpEnabled(), Preferences.SIGNATURE_HELP_ID, Preferences.TEXT_DOCUMENT_SIGNATURE_HELP, SignatureHelpHandler.createOptions()); } if (preferenceManager.getClientPreferences().isRenameDynamicRegistrationSupported()) { - toggleCapability(preferenceManager.getPreferences().isRenameEnabled(), Preferences.RENAME_ID, Preferences.TEXT_DOCUMENT_RENAME, null); + toggleCapability(preferenceManager.getPreferences().isRenameEnabled(), Preferences.RENAME_ID, Preferences.TEXT_DOCUMENT_RENAME, RenameHandler.createOptions()); } if (preferenceManager.getClientPreferences().isExecuteCommandDynamicRegistrationSupported()) { toggleCapability(preferenceManager.getPreferences().isExecuteCommandEnabled(), Preferences.EXECUTE_COMMAND_ID, Preferences.WORKSPACE_EXECUTE_COMMAND, @@ -434,7 +436,7 @@ private void toggleCapability(boolean enabled, String id, String capability, Obj @Override public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) { logInfo(">> workspace/didChangeWatchedFiles "); - WorkspaceEventsHandler handler = new WorkspaceEventsHandler(pm, client); + WorkspaceEventsHandler handler = new WorkspaceEventsHandler(pm, client, this.documentLifeCycleHandler); handler.didChangeWatchedFiles(params); } @@ -643,6 +645,17 @@ public CompletableFuture> onTypeFormatting(DocumentOnTy return computeAsync((monitor) -> handler.onTypeFormatting(params, monitor)); } + /* (non-Javadoc) + * @see org.eclipse.lsp4j.services.TextDocumentService#prepareRename(org.eclipse.lsp4j.TextDocumentPositionParams) + */ + @Override + public CompletableFuture> prepareRename(TextDocumentPositionParams params) { + logInfo(">> document/prepareRename"); + + PrepareRenameHandler handler = new PrepareRenameHandler(); + return computeAsync((monitor) -> handler.prepareRename(params, monitor)); + } + /* (non-Javadoc) * @see org.eclipse.lsp4j.services.TextDocumentService#rename(org.eclipse.lsp4j.RenameParams) */ diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandler.java new file mode 100644 index 0000000000..20b6bff245 --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandler.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Microsoft Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.jdt.ls.core.internal.handlers; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.PackageDeclaration; +import org.eclipse.jdt.core.dom.QualifiedName; +import org.eclipse.jdt.core.manipulation.CoreASTProvider; +import org.eclipse.jdt.internal.core.manipulation.search.IOccurrencesFinder.OccurrenceLocation; +import org.eclipse.jdt.internal.core.manipulation.search.OccurrencesFinder; +import org.eclipse.jdt.ls.core.internal.JDTUtils; +import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; +import org.eclipse.jdt.ls.core.internal.corrections.InnovationContext; +import org.eclipse.lsp4j.PrepareRenameResult; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.TextDocumentPositionParams; +import org.eclipse.lsp4j.jsonrpc.ResponseErrorException; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.eclipse.lsp4j.jsonrpc.messages.ResponseError; +import org.eclipse.lsp4j.jsonrpc.messages.ResponseErrorCode; + +public class PrepareRenameHandler { + + public PrepareRenameHandler() { + } + + public Either prepareRename(TextDocumentPositionParams params, IProgressMonitor monitor) { + + final ICompilationUnit unit = JDTUtils.resolveCompilationUnit(params.getTextDocument().getUri()); + if (unit != null) { + try { + OccurrencesFinder finder = new OccurrencesFinder(); + CompilationUnit ast = CoreASTProvider.getInstance().getAST(unit, CoreASTProvider.WAIT_YES, monitor); + + if (ast != null) { + int offset = JsonRpcHelpers.toOffset(unit.getBuffer(), params.getPosition().getLine(), params.getPosition().getCharacter()); + String error = finder.initialize(ast, offset, 0); + if (error == null) { + OccurrenceLocation[] occurrences = finder.getOccurrences(); + if (occurrences != null) { + for (OccurrenceLocation loc : occurrences) { + if (monitor.isCanceled()) { + return Either.forLeft(new Range()); + } + if (loc.getOffset() <= offset && loc.getOffset() + loc.getLength() >= offset) { + InnovationContext context = new InnovationContext(unit, loc.getOffset(), loc.getLength()); + context.setASTRoot(ast); + ASTNode node = context.getCoveredNode(); + // Rename package is not fully support yet. + if (!(node instanceof PackageDeclaration) && !(node instanceof QualifiedName && node.getParent() instanceof PackageDeclaration)) { + return Either.forLeft(JDTUtils.toRange(unit, loc.getOffset(), loc.getLength())); + } + } + } + } + } + } + + } catch (CoreException e) { + JavaLanguageServerPlugin.logException("Problem with compute occurrences for" + unit.getElementName() + " in prepareRename", e); + } + } + throw new ResponseErrorException(new ResponseError(ResponseErrorCode.InvalidRequest, "Rename this element is not supported yet.", null)); + } +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/RenameHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/RenameHandler.java index 781aca531a..90d29ab931 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/RenameHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/RenameHandler.java @@ -24,6 +24,7 @@ import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; import org.eclipse.jdt.ls.core.internal.corext.refactoring.rename.RenameSupport; import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager; +import org.eclipse.lsp4j.RenameOptions; import org.eclipse.lsp4j.RenameParams; import org.eclipse.lsp4j.WorkspaceEdit; import org.eclipse.ltk.core.refactoring.Change; @@ -34,6 +35,12 @@ public class RenameHandler { + public static RenameOptions createOptions() { + RenameOptions renameOptions = new RenameOptions(); + renameOptions.setPrepareProvider(true); + return renameOptions; + } + private PreferenceManager preferenceManager; public RenameHandler(PreferenceManager preferenceManager) { @@ -78,7 +85,7 @@ public WorkspaceEdit rename(RenameParams params, IProgressMonitor monitor) { create.run(monitor); Change change = create.getChange(); - ChangeUtil.convertChanges(change, edit); + ChangeUtil.convertCompositeChange(change, edit); } catch (CoreException ex) { JavaLanguageServerPlugin.logException("Problem with rename for " + params.getTextDocument().getUri(), ex); } diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/WorkspaceDiagnosticsHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/WorkspaceDiagnosticsHandler.java index 03e4676561..7d7c9dc291 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/WorkspaceDiagnosticsHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/WorkspaceDiagnosticsHandler.java @@ -131,7 +131,9 @@ public boolean visit(IResourceDelta delta) throws CoreException { markers = Arrays.copyOf(javaMarkers, javaMarkers.length + taskMarkers.length); System.arraycopy(taskMarkers, 0, markers, javaMarkers.length, taskMarkers.length); ICompilationUnit cu = (ICompilationUnit) JavaCore.create(file); - document = JsonRpcHelpers.toDocument(cu.getBuffer()); + if (!cu.isWorkingCopy()) { + document = JsonRpcHelpers.toDocument(cu.getBuffer()); + } } // or a build file else if (projectsManager.isBuildFile(file)) { //all errors on that build file should be relevant @@ -230,10 +232,12 @@ private void publishDiagnostics(List markers) { String uri = JDTUtils.getFileURI(resource); if (JavaCore.isJavaLikeFileName(file.getName())) { ICompilationUnit cu = JDTUtils.resolveCompilationUnit(uri); - try { - document = JsonRpcHelpers.toDocument(cu.getBuffer()); - } catch (JavaModelException e) { - JavaLanguageServerPlugin.logException("Failed to publish diagnostics.", e); + if (cu.isWorkingCopy()) { + try { + document = JsonRpcHelpers.toDocument(cu.getBuffer()); + } catch (JavaModelException e) { + JavaLanguageServerPlugin.logException("Failed to publish diagnostics.", e); + } } } else if (projectsManager.isBuildFile(file)) { document = JsonRpcHelpers.toDocument(file); diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/WorkspaceEventsHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/WorkspaceEventsHandler.java index 8ecbe2d533..284e42b50e 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/WorkspaceEventsHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/WorkspaceEventsHandler.java @@ -30,18 +30,22 @@ import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager; import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager.CHANGE_TYPE; import org.eclipse.lsp4j.DidChangeWatchedFilesParams; +import org.eclipse.lsp4j.DidCloseTextDocumentParams; import org.eclipse.lsp4j.FileChangeType; import org.eclipse.lsp4j.FileEvent; import org.eclipse.lsp4j.PublishDiagnosticsParams; +import org.eclipse.lsp4j.TextDocumentIdentifier; public class WorkspaceEventsHandler { private final ProjectsManager pm ; private final JavaClientConnection connection; + private final DocumentLifeCycleHandler handler; - public WorkspaceEventsHandler(ProjectsManager projects, JavaClientConnection connection ) { + public WorkspaceEventsHandler(ProjectsManager projects, JavaClientConnection connection, DocumentLifeCycleHandler handler) { this.pm = projects; this.connection = connection; + this.handler = handler; } private CHANGE_TYPE toChangeType(FileChangeType vtype){ @@ -63,6 +67,7 @@ void didChangeWatchedFiles(DidChangeWatchedFilesParams param){ CHANGE_TYPE changeType = toChangeType(fileEvent.getType()); if(changeType==CHANGE_TYPE.DELETED){ cleanUpDiagnostics(fileEvent.getUri()); + handler.didClose(new DidCloseTextDocumentParams(new TextDocumentIdentifier(fileEvent.getUri()))); } ICompilationUnit unit = JDTUtils.resolveCompilationUnit(fileEvent.getUri()); if (unit != null && changeType == CHANGE_TYPE.CREATED && !unit.exists()) { diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/ClientPreferences.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/ClientPreferences.java index 4a4d9c1eea..8614863c8d 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/ClientPreferences.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/ClientPreferences.java @@ -18,6 +18,7 @@ import org.eclipse.lsp4j.ClientCapabilities; import org.eclipse.lsp4j.DynamicRegistrationCapabilities; import org.eclipse.lsp4j.MarkupKind; +import org.eclipse.lsp4j.ResourceOperationKind; /** * A wrapper around {@link ClientCapabilities} @@ -173,10 +174,20 @@ public boolean isSupportsCompletionDocumentationMarkdown() { //@formatter:on } + @Deprecated public boolean isWorkspaceEditResourceChangesSupported() { return capabilities.getWorkspace() != null && capabilities.getWorkspace().getWorkspaceEdit() != null && isTrue(capabilities.getWorkspace().getWorkspaceEdit().getResourceChanges()); } + public boolean isResourceOperationSupported() { + //@formatter:off + return capabilities.getWorkspace() != null && capabilities.getWorkspace().getWorkspaceEdit() != null + && capabilities.getWorkspace().getWorkspaceEdit().getResourceOperations().contains(ResourceOperationKind.Create) + && capabilities.getWorkspace().getWorkspaceEdit().getResourceOperations().contains(ResourceOperationKind.Rename) + && capabilities.getWorkspace().getWorkspaceEdit().getResourceOperations().contains(ResourceOperationKind.Delete); + //@formatter:on + } + /** * {@code true} if the client has explicitly set the * {@code textDocument.documentSymbol.hierarchicalDocumentSymbolSupport} to diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/correction/ReorgQuickFixTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/correction/ReorgQuickFixTest.java index 1f65721be6..66eac61f09 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/correction/ReorgQuickFixTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/correction/ReorgQuickFixTest.java @@ -13,23 +13,42 @@ *******************************************************************************/ package org.eclipse.jdt.ls.core.internal.correction; +import static org.mockito.Mockito.when; + import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; +import org.eclipse.jdt.ls.core.internal.preferences.ClientPreferences; +import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +@RunWith(MockitoJUnitRunner.class) public class ReorgQuickFixTest extends AbstractQuickFixTest { private IJavaProject fJProject1; private IPackageFragmentRoot fSourceFolder; + @Mock + private ClientPreferences clientPreferences; + + @Mock + private PreferenceManager preferenceManager; + @Before public void setup() throws Exception { fJProject1 = newEmptyProject(); fJProject1.setOptions(TestOptions.getDefaultOptions()); + JavaLanguageServerPlugin.setPreferencesManager(preferenceManager); + when(preferenceManager.getClientPreferences()).thenReturn(clientPreferences); + when(clientPreferences.isResourceOperationSupported()).thenReturn(false); + fSourceFolder = fJProject1.getPackageFragmentRoot(fJProject1.getProject().getFolder("src")); } diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/RenameHandlerTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/RenameHandlerTest.java index 048d03ab79..7dafc17302 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/RenameHandlerTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/RenameHandlerTest.java @@ -13,7 +13,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -35,9 +34,11 @@ import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager; import org.eclipse.jdt.ls.core.internal.preferences.Preferences; import org.eclipse.jface.text.BadLocationException; +import org.eclipse.lsp4j.CreateFile; import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.RenameFile; import org.eclipse.lsp4j.RenameParams; -import org.eclipse.lsp4j.ResourceChange; +import org.eclipse.lsp4j.ResourceOperation; import org.eclipse.lsp4j.TextDocumentEdit; import org.eclipse.lsp4j.TextDocumentIdentifier; import org.eclipse.lsp4j.TextEdit; @@ -67,7 +68,7 @@ public void setup() throws Exception { sourceFolder = javaProject.getPackageFragmentRoot(javaProject.getProject().getFolder("src")); JavaLanguageServerPlugin.setPreferencesManager(preferenceManager); when(preferenceManager.getClientPreferences()).thenReturn(clientPreferences); - when(clientPreferences.isWorkspaceEditResourceChangesSupported()).thenReturn(false); + when(clientPreferences.isResourceOperationSupported()).thenReturn(false); Preferences p = mock(Preferences.class); when(preferenceManager.getPreferences()).thenReturn(p); when(p.isRenameEnabled()).thenReturn(true); @@ -213,7 +214,7 @@ public void testRenameMethod() throws JavaModelException, BadLocationException { @Test public void testRenameTypeWhithResourceChanges() throws JavaModelException, BadLocationException { - when(clientPreferences.isWorkspaceEditResourceChangesSupported()).thenReturn(true); + when(clientPreferences.isResourceOperationSupported()).thenReturn(true); IPackageFragment pack1 = sourceFolder.createPackageFragment("test1", false, null); @@ -234,18 +235,18 @@ public void testRenameTypeWhithResourceChanges() throws JavaModelException, BadL WorkspaceEdit edit = getRenameEdit(cu, pos, "Newname"); assertNotNull(edit); - List> resourceChanges = edit.getResourceChanges(); + List> resourceChanges = edit.getDocumentChanges(); assertEquals(resourceChanges.size(), 3); - Either change = resourceChanges.get(2); - ResourceChange resourceChange = change.getLeft(); - assertEquals(JDTUtils.toURI(cu), resourceChange.getCurrent()); + Either change = resourceChanges.get(2); + RenameFile resourceChange = (RenameFile) change.getRight(); + assertEquals(JDTUtils.toURI(cu), resourceChange.getOldUri()); assertEquals(JDTUtils.toURI(cu).replace("E", "Newname"), resourceChange.getNewUri()); List testChanges = new LinkedList<>(); - testChanges.addAll(resourceChanges.get(0).getRight().getEdits()); - testChanges.addAll(resourceChanges.get(1).getRight().getEdits()); + testChanges.addAll(resourceChanges.get(0).getLeft().getEdits()); + testChanges.addAll(resourceChanges.get(1).getLeft().getEdits()); String expected = "package test1;\n" + "public class Newname {\n" + @@ -725,7 +726,7 @@ public void testRenameJavadoc() throws JavaModelException, BadLocationException @Test public void testRenamePackage() throws JavaModelException, BadLocationException { - when(clientPreferences.isWorkspaceEditResourceChangesSupported()).thenReturn(true); + when(clientPreferences.isResourceOperationSupported()).thenReturn(true); IPackageFragment pack1 = sourceFolder.createPackageFragment("test1", false, null); IPackageFragment pack2 = sourceFolder.createPackageFragment("parent.test2", false, null); @@ -759,16 +760,16 @@ public void testRenamePackage() throws JavaModelException, BadLocationException WorkspaceEdit edit = getRenameEdit(cuB, pos, "parent.newpackage"); assertNotNull(edit); - List> resourceChanges = edit.getResourceChanges(); + List> resourceChanges = edit.getDocumentChanges(); - assertEquals(5, resourceChanges.size()); + assertEquals(6, resourceChanges.size()); List testChangesA = new LinkedList<>(); - testChangesA.addAll(resourceChanges.get(0).getRight().getEdits()); + testChangesA.addAll(resourceChanges.get(0).getLeft().getEdits()); List testChangesB = new LinkedList<>(); - testChangesB.addAll(resourceChanges.get(1).getRight().getEdits()); - testChangesB.addAll(resourceChanges.get(2).getRight().getEdits()); + testChangesB.addAll(resourceChanges.get(1).getLeft().getEdits()); + testChangesB.addAll(resourceChanges.get(2).getLeft().getEdits()); String expectedA = "package test1;\n" + @@ -790,13 +791,12 @@ public void testRenamePackage() throws JavaModelException, BadLocationException assertEquals(expectedB, TextEditUtil.apply(builderB.toString(), testChangesB)); //moved package - ResourceChange resourceChange = resourceChanges.get(3).getLeft(); - assertNull(resourceChange.getCurrent()); - assertEquals(ResourceUtils.fixURI(pack2.getResource().getRawLocationURI()).replace("test2", "newpackage"), resourceChange.getNewUri()); + CreateFile resourceChange = (CreateFile) resourceChanges.get(3).getRight(); + assertEquals(ResourceUtils.fixURI(pack2.getResource().getRawLocationURI()).replaceFirst("test2[/]?", "newpackage/.temp"), resourceChange.getUri()); //moved class B - ResourceChange resourceChange2 = resourceChanges.get(4).getLeft(); - assertEquals(ResourceUtils.fixURI(cuB.getResource().getRawLocationURI()), resourceChange2.getCurrent()); + RenameFile resourceChange2 = (RenameFile) resourceChanges.get(4).getRight(); + assertEquals(ResourceUtils.fixURI(cuB.getResource().getRawLocationURI()), resourceChange2.getOldUri()); assertEquals(ResourceUtils.fixURI(cuB.getResource().getRawLocationURI()).replace("test2", "newpackage"), resourceChange2.getNewUri()); } From 5142e405c0c9aef7f633a6f2b38b48a39cabd50b Mon Sep 17 00:00:00 2001 From: Yaohai Zheng Date: Fri, 23 Nov 2018 13:56:56 +0800 Subject: [PATCH 2/6] Add error handling for rename operation. Signed-off-by: Yaohai Zheng --- .../core/internal/handlers/RenameHandler.java | 9 +++- .../internal/handlers/RenameHandlerTest.java | 47 +++++++++++++++---- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/RenameHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/RenameHandler.java index 90d29ab931..ea8edc5d4d 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/RenameHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/RenameHandler.java @@ -27,6 +27,9 @@ import org.eclipse.lsp4j.RenameOptions; import org.eclipse.lsp4j.RenameParams; import org.eclipse.lsp4j.WorkspaceEdit; +import org.eclipse.lsp4j.jsonrpc.ResponseErrorException; +import org.eclipse.lsp4j.jsonrpc.messages.ResponseError; +import org.eclipse.lsp4j.jsonrpc.messages.ResponseErrorCode; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.CheckConditionsOperation; import org.eclipse.ltk.core.refactoring.CreateChangeOperation; @@ -81,8 +84,12 @@ public WorkspaceEdit rename(RenameParams params, IProgressMonitor monitor) { } RenameRefactoring renameRefactoring = renameSupport.getRenameRefactoring(); - CreateChangeOperation create = new CreateChangeOperation(new CheckConditionsOperation(renameRefactoring, CheckConditionsOperation.ALL_CONDITIONS), RefactoringStatus.FATAL); + CheckConditionsOperation check = new CheckConditionsOperation(renameRefactoring, CheckConditionsOperation.ALL_CONDITIONS); + CreateChangeOperation create = new CreateChangeOperation(check, RefactoringStatus.FATAL); create.run(monitor); + if (check.getStatus().getSeverity() >= RefactoringStatus.FATAL) { + throw new ResponseErrorException(new ResponseError(ResponseErrorCode.InvalidRequest, check.getStatus().getMessageMatchingSeverity(RefactoringStatus.ERROR), null)); + } Change change = create.getChange(); ChangeUtil.convertCompositeChange(change, edit); diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/RenameHandlerTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/RenameHandlerTest.java index 7dafc17302..5f6bbf9902 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/RenameHandlerTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/RenameHandlerTest.java @@ -19,7 +19,6 @@ import java.util.LinkedList; import java.util.List; -import org.eclipse.core.resources.IFile; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragment; @@ -43,6 +42,7 @@ import org.eclipse.lsp4j.TextDocumentIdentifier; import org.eclipse.lsp4j.TextEdit; import org.eclipse.lsp4j.WorkspaceEdit; +import org.eclipse.lsp4j.jsonrpc.ResponseErrorException; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.junit.Before; import org.junit.Test; @@ -213,7 +213,7 @@ public void testRenameMethod() throws JavaModelException, BadLocationException { } @Test - public void testRenameTypeWhithResourceChanges() throws JavaModelException, BadLocationException { + public void testRenameTypeWithResourceChanges() throws JavaModelException, BadLocationException { when(clientPreferences.isResourceOperationSupported()).thenReturn(true); IPackageFragment pack1 = sourceFolder.createPackageFragment("test1", false, null); @@ -231,8 +231,6 @@ public void testRenameTypeWhithResourceChanges() throws JavaModelException, BadL Position pos = mergeCode(builder, codes); ICompilationUnit cu = pack1.createCompilationUnit("E.java", builder.toString(), false, null); - IFile resource = (IFile) cu.getResource(); - WorkspaceEdit edit = getRenameEdit(cu, pos, "Newname"); assertNotNull(edit); List> resourceChanges = edit.getDocumentChanges(); @@ -262,7 +260,42 @@ public void testRenameTypeWhithResourceChanges() throws JavaModelException, BadL assertEquals(expected, TextEditUtil.apply(builder.toString(), testChanges)); } - @Test + @Test(expected = ResponseErrorException.class) + public void testRenameTypeWithErrors() throws JavaModelException, BadLocationException { + when(clientPreferences.isResourceOperationSupported()).thenReturn(true); + + IPackageFragment pack1 = sourceFolder.createPackageFragment("test1", false, null); + + String[] codes = { "package test1;\n", + "public class Newname {\n", + " }\n", + "}\n" }; + StringBuilder builder = new StringBuilder(); + mergeCode(builder, codes); + ICompilationUnit cu = pack1.createCompilationUnit("Newname.java", builder.toString(), false, null); + + + String[] codes1 = { "package test1;\n", + "public class E|* {\n", + " public E() {\n", + " }\n", + " public int bar() {\n", " }\n", + " public int foo() {\n", + " this.bar();\n", + " }\n", + "}\n" }; + builder = new StringBuilder(); + Position pos = mergeCode(builder, codes1); + cu = pack1.createCompilationUnit("E.java", builder.toString(), false, null); + + WorkspaceEdit edit = getRenameEdit(cu, pos, "Newname"); + assertNotNull(edit); + List> resourceChanges = edit.getDocumentChanges(); + + assertEquals(resourceChanges.size(), 3); + } + + @Test(expected = ResponseErrorException.class) public void testRenameSystemLibrary() throws JavaModelException { IPackageFragment pack1 = sourceFolder.createPackageFragment("test1", false, null); @@ -279,9 +312,7 @@ public void testRenameSystemLibrary() throws JavaModelException { Position pos = mergeCode(builder, codes); ICompilationUnit cu = pack1.createCompilationUnit("E.java", builder.toString(), false, null); - WorkspaceEdit edit = getRenameEdit(cu, pos, "newname"); - assertNotNull(edit); - assertEquals(edit.getChanges().size(), 0); + getRenameEdit(cu, pos, "newname"); } @Test From ea7725ce4310a495519c4d976b71fc2974a339f1 Mon Sep 17 00:00:00 2001 From: Yaohai Zheng Date: Mon, 26 Nov 2018 14:11:00 +0800 Subject: [PATCH 3/6] Add test cases for prepare rename. --- .../handlers/PrepareRenameHandler.java | 3 - .../handlers/PrepareRenameHandlerTest.java | 314 ++++++++++++++++++ 2 files changed, 314 insertions(+), 3 deletions(-) create mode 100644 org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandlerTest.java diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandler.java index 20b6bff245..1765562b41 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandler.java @@ -34,9 +34,6 @@ public class PrepareRenameHandler { - public PrepareRenameHandler() { - } - public Either prepareRename(TextDocumentPositionParams params, IProgressMonitor monitor) { final ICompilationUnit unit = JDTUtils.resolveCompilationUnit(params.getTextDocument().getUri()); diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandlerTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandlerTest.java new file mode 100644 index 0000000000..c9d04dac63 --- /dev/null +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandlerTest.java @@ -0,0 +1,314 @@ +/******************************************************************************* +* Copyright (c) 2018 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Microsoft Corporation - initial API and implementation +*******************************************************************************/ + +package org.eclipse.jdt.ls.core.internal.handlers; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IPackageFragment; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.ls.core.internal.JDTUtils; +import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; +import org.eclipse.jdt.ls.core.internal.managers.AbstractProjectsManagerBasedTest; +import org.eclipse.jdt.ls.core.internal.preferences.ClientPreferences; +import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager; +import org.eclipse.jdt.ls.core.internal.preferences.Preferences; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.PrepareRenameResult; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.eclipse.lsp4j.TextDocumentPositionParams; +import org.eclipse.lsp4j.jsonrpc.ResponseErrorException; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class PrepareRenameHandlerTest extends AbstractProjectsManagerBasedTest { + + private PrepareRenameHandler handler; + + @Mock + private PreferenceManager preferenceManager; + @Mock + private ClientPreferences clientPreferences; + + private IPackageFragmentRoot sourceFolder; + + @Before + public void setup() throws Exception { + IJavaProject javaProject = newEmptyProject(); + sourceFolder = javaProject.getPackageFragmentRoot(javaProject.getProject().getFolder("src")); + JavaLanguageServerPlugin.setPreferencesManager(preferenceManager); + when(preferenceManager.getClientPreferences()).thenReturn(clientPreferences); + when(clientPreferences.isResourceOperationSupported()).thenReturn(false); + Preferences p = mock(Preferences.class); + when(preferenceManager.getPreferences()).thenReturn(p); + when(p.isRenameEnabled()).thenReturn(true); + handler = new PrepareRenameHandler(); + } + + + @Test + public void testRenameParameter() throws JavaModelException, BadLocationException { + IPackageFragment pack1 = sourceFolder.createPackageFragment("test1", false, null); + + String[] codes = { + "package test1;\n", + "public class E {\n", + " public int foo(String str) {\n", + " str|*.length();\n", + " }\n", + " public int bar(String str) {\n", + " str.length();\n", + " }\n", + "}\n" + }; + StringBuilder builder = new StringBuilder(); + Position pos = mergeCode(builder, codes); + ICompilationUnit cu = pack1.createCompilationUnit("E.java", builder.toString(), false, null); + + Either result = prepareRename(cu, pos, "newname"); + + assertNotNull(result.getLeft()); + assertTrue(result.getLeft().getStart().getLine() > 0); + } + + @Test + public void testRenameLocalVariable() throws JavaModelException, BadLocationException { + IPackageFragment pack1 = sourceFolder.createPackageFragment("test1", false, null); + + String[] codes = { + "package test1;\n", + "public class E {\n", + " public int bar() {\n", + " String str = new String();\n", + " str.length();\n", + " }\n", + " public int foo() {\n", + " String str = new String();\n", + " str|*.length()\n", + " }\n", + "}\n" + }; + StringBuilder builder = new StringBuilder(); + Position pos = mergeCode(builder, codes); + ICompilationUnit cu = pack1.createCompilationUnit("E.java", builder.toString(), false, null); + + Either result = prepareRename(cu, pos, "newname"); + assertNotNull(result.getLeft()); + assertTrue(result.getLeft().getStart().getLine() > 0); + } + + @Test + public void testRenameField() throws JavaModelException, BadLocationException { + IPackageFragment pack1 = sourceFolder.createPackageFragment("test1", false, null); + + String[] codes = { + "package test1;\n", + "public class E {\n", + " private int myValue = 2;\n", + " public void bar() {\n", + " myValue|* = 3;\n", + " }\n", + "}\n" + }; + StringBuilder builder = new StringBuilder(); + Position pos = mergeCode(builder, codes); + ICompilationUnit cu = pack1.createCompilationUnit("E.java", builder.toString(), false, null); + + Either result = prepareRename(cu, pos, "newname"); + assertNotNull(result.getLeft()); + assertTrue(result.getLeft().getStart().getLine() > 0); + } + + @Test + public void testRenameMethod() throws JavaModelException, BadLocationException { + IPackageFragment pack1 = sourceFolder.createPackageFragment("test1", false, null); + + String[] codes = { + "package test1;\n", + "public class E {\n", + " public int bar() {\n", + " }\n", + " public int foo() {\n", + " this.bar|*();\n", + " }\n", + "}\n" + }; + StringBuilder builder = new StringBuilder(); + Position pos = mergeCode(builder, codes); + ICompilationUnit cu = pack1.createCompilationUnit("E.java", builder.toString(), false, null); + + Either result = prepareRename(cu, pos, "newname"); + assertNotNull(result.getLeft()); + assertTrue(result.getLeft().getStart().getLine() > 0); + } + + @Test + public void testRenameTypeWithResourceChanges() throws JavaModelException, BadLocationException { + when(clientPreferences.isResourceOperationSupported()).thenReturn(true); + + IPackageFragment pack1 = sourceFolder.createPackageFragment("test1", false, null); + + String[] codes = { "package test1;\n", + "public class E|* {\n", + " public E() {\n", + " }\n", + " public int bar() {\n", " }\n", + " public int foo() {\n", + " this.bar();\n", + " }\n", + "}\n" }; + StringBuilder builder = new StringBuilder(); + Position pos = mergeCode(builder, codes); + ICompilationUnit cu = pack1.createCompilationUnit("E.java", builder.toString(), false, null); + + Either result = prepareRename(cu, pos, "Newname"); + assertNotNull(result.getLeft()); + assertTrue(result.getLeft().getStart().getLine() > 0); + } + + @Test + public void testRenameTypeParameter() throws JavaModelException, BadLocationException { + IPackageFragment pack1 = sourceFolder.createPackageFragment("test1", false, null); + + String[] codes= { + "package test1;\n", + "public class A {\n", + " private T t;\n", + " public T get() { return t; }\n", + "}\n" + }; + + StringBuilder builder = new StringBuilder(); + Position pos = mergeCode(builder, codes); + ICompilationUnit cu = pack1.createCompilationUnit("A.java", builder.toString(), false, null); + + + Either result = prepareRename(cu, pos, "TT"); + assertNotNull(result.getLeft()); + assertTrue(result.getLeft().getStart().getLine() > 0); + } + + + @Test + public void testRenameTypeParameterInMethod() throws JavaModelException, BadLocationException { + IPackageFragment pack1 = sourceFolder.createPackageFragment("test1", false, null); + + String[] codes = { + "package test1;\n", + "public class B {\n", + " private T t;\n", + " public inspect(U u) { return u; }\n", + "}\n" + }; + + StringBuilder builder = new StringBuilder(); + Position pos = mergeCode(builder, codes); + ICompilationUnit cu = pack1.createCompilationUnit("B.java", builder.toString(), false, null); + + Either result = prepareRename(cu, pos, "UU"); + assertNotNull(result.getLeft()); + assertTrue(result.getLeft().getStart().getLine() > 0); + } + + @Test + public void testRenameJavadoc() throws JavaModelException, BadLocationException { + IPackageFragment pack1 = sourceFolder.createPackageFragment("test1", false, null); + + String[] codes = { + "package test1;\n", + "public class E {\n", + " /**\n", + " *@param i int\n", + " */\n", + " public int foo(int i|*) {\n", + " E e = new E();\n", + " e.foo();\n", + " }\n", + "}\n" + }; + StringBuilder builder = new StringBuilder(); + Position pos = mergeCode(builder, codes); + ICompilationUnit cu = pack1.createCompilationUnit("E.java", builder.toString(), false, null); + + Either result = prepareRename(cu, pos, "i2"); + assertNotNull(result.getLeft()); + assertTrue(result.getLeft().getStart().getLine() > 0); + } + + @Test(expected = ResponseErrorException.class) + public void testRenamePackage() throws JavaModelException, BadLocationException { + when(clientPreferences.isResourceOperationSupported()).thenReturn(true); + + IPackageFragment pack1 = sourceFolder.createPackageFragment("test1", false, null); + IPackageFragment pack2 = sourceFolder.createPackageFragment("parent.test2", false, null); + + String[] codes1= { + "package test1;\n", + "import parent.test2.B;\n", + "public class A {\n", + " public void foo(){\n", + " B b = new B();\n", + " b.foo();\n", + " }\n", + "}\n" + }; + + String[] codes2 = { + "package parent.test2|*;\n", + "public class B {\n", + " public B() {}\n", + " public void foo() {}\n", + "}\n" + }; + StringBuilder builderA = new StringBuilder(); + mergeCode(builderA, codes1); + pack1.createCompilationUnit("A.java", builderA.toString(), false, null); + + StringBuilder builderB = new StringBuilder(); + Position pos = mergeCode(builderB, codes2); + ICompilationUnit cuB = pack2.createCompilationUnit("B.java", builderB.toString(), false, null); + + prepareRename(cuB, pos, "parent.newpackage"); + } + + private Position mergeCode(StringBuilder builder, String[] codes) { + Position pos = null; + for (int i = 0; i < codes.length; i++) { + int ind = codes[i].indexOf("|*"); + if (ind > 0) { + pos = new Position(i, ind); + codes[i] = codes[i].replace("|*", ""); + } + builder.append(codes[i]); + } + return pos; + } + + private Either prepareRename(ICompilationUnit cu, Position pos, String newName) { + TextDocumentIdentifier identifier = new TextDocumentIdentifier(JDTUtils.toURI(cu)); + + TextDocumentPositionParams params = new TextDocumentPositionParams(identifier, pos); + return handler.prepareRename(params, monitor); + } +} From 2a2712c6691d4ce01dbff0b2117298725caa4cef Mon Sep 17 00:00:00 2001 From: Yaohai Zheng Date: Tue, 27 Nov 2018 13:47:54 +0800 Subject: [PATCH 4/6] Address review feedback. --- .../core/internal/handlers/PrepareRenameHandler.java | 10 ++++++---- .../ls/core/internal/handlers/RenameHandlerTest.java | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandler.java index 1765562b41..984619ef37 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandler.java @@ -18,6 +18,7 @@ import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.PackageDeclaration; import org.eclipse.jdt.core.dom.QualifiedName; +import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.manipulation.CoreASTProvider; import org.eclipse.jdt.internal.core.manipulation.search.IOccurrencesFinder.OccurrenceLocation; import org.eclipse.jdt.internal.core.manipulation.search.OccurrencesFinder; @@ -56,8 +57,9 @@ public Either prepareRename(TextDocumentPositionPara InnovationContext context = new InnovationContext(unit, loc.getOffset(), loc.getLength()); context.setASTRoot(ast); ASTNode node = context.getCoveredNode(); - // Rename package is not fully support yet. - if (!(node instanceof PackageDeclaration) && !(node instanceof QualifiedName && node.getParent() instanceof PackageDeclaration)) { + // Rename package is not fully supported yet. + if (!(node instanceof PackageDeclaration) + && !(((node instanceof QualifiedName) || (node instanceof SimpleName)) && node.getParent() instanceof PackageDeclaration)) { return Either.forLeft(JDTUtils.toRange(unit, loc.getOffset(), loc.getLength())); } } @@ -67,9 +69,9 @@ public Either prepareRename(TextDocumentPositionPara } } catch (CoreException e) { - JavaLanguageServerPlugin.logException("Problem with compute occurrences for" + unit.getElementName() + " in prepareRename", e); + JavaLanguageServerPlugin.logException("Problem computing occurrences for" + unit.getElementName() + " in prepareRename", e); } } - throw new ResponseErrorException(new ResponseError(ResponseErrorCode.InvalidRequest, "Rename this element is not supported yet.", null)); + throw new ResponseErrorException(new ResponseError(ResponseErrorCode.InvalidRequest, "Renaming this element is not supported.", null)); } } diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/RenameHandlerTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/RenameHandlerTest.java index 5f6bbf9902..30845416d9 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/RenameHandlerTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/RenameHandlerTest.java @@ -240,7 +240,7 @@ public void testRenameTypeWithResourceChanges() throws JavaModelException, BadLo Either change = resourceChanges.get(2); RenameFile resourceChange = (RenameFile) change.getRight(); assertEquals(JDTUtils.toURI(cu), resourceChange.getOldUri()); - assertEquals(JDTUtils.toURI(cu).replace("E", "Newname"), resourceChange.getNewUri()); + assertEquals(JDTUtils.toURI(cu).replaceFirst("(?s)E(?!.*?E)", "Newname"), resourceChange.getNewUri()); List testChanges = new LinkedList<>(); testChanges.addAll(resourceChanges.get(0).getLeft().getEdits()); From 813d6a666dd52d0899f5bb03ac8a7a8378cbb89d Mon Sep 17 00:00:00 2001 From: Fred Bricon Date: Tue, 27 Nov 2018 17:43:26 -0500 Subject: [PATCH 5/6] Better handle case of package renaming Signed-off-by: Fred Bricon --- org.eclipse.jdt.ls.core/foo.xml | 0 .../handlers/PrepareRenameHandler.java | 13 ++++++-- .../handlers/PrepareRenameHandlerTest.java | 31 +++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 org.eclipse.jdt.ls.core/foo.xml diff --git a/org.eclipse.jdt.ls.core/foo.xml b/org.eclipse.jdt.ls.core/foo.xml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandler.java index 984619ef37..a79f74b0bb 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandler.java @@ -58,8 +58,7 @@ public Either prepareRename(TextDocumentPositionPara context.setASTRoot(ast); ASTNode node = context.getCoveredNode(); // Rename package is not fully supported yet. - if (!(node instanceof PackageDeclaration) - && !(((node instanceof QualifiedName) || (node instanceof SimpleName)) && node.getParent() instanceof PackageDeclaration)) { + if (!isPackageDeclaration(node)) { return Either.forLeft(JDTUtils.toRange(unit, loc.getOffset(), loc.getLength())); } } @@ -74,4 +73,14 @@ public Either prepareRename(TextDocumentPositionPara } throw new ResponseErrorException(new ResponseError(ResponseErrorCode.InvalidRequest, "Renaming this element is not supported.", null)); } + + private boolean isPackageDeclaration(ASTNode node) { + if (node instanceof PackageDeclaration) { + return true; + } + if ((node instanceof QualifiedName) || (node instanceof SimpleName)) { + return isPackageDeclaration(node.getParent()); + } + return false; + } } diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandlerTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandlerTest.java index c9d04dac63..ca4f998f95 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandlerTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandlerTest.java @@ -292,6 +292,37 @@ public void testRenamePackage() throws JavaModelException, BadLocationException prepareRename(cuB, pos, "parent.newpackage"); } + @Test(expected = ResponseErrorException.class) + public void testRenameMiddleOfPackage() throws JavaModelException, BadLocationException { + when(clientPreferences.isResourceOperationSupported()).thenReturn(true); + + IPackageFragment pack1 = sourceFolder.createPackageFragment("ex.amples", false, null); + + //@formatter:off + String[] content = { + "package |*ex.amples;\n", + "public class A {}\n" + }; + //@formatter:on + StringBuilder builder = new StringBuilder(); + Position pos = mergeCode(builder, content); + ICompilationUnit cu = pack1.createCompilationUnit("A.java", builder.toString(), false, null); + + prepareRename(cu, pos, "ex.am.ple"); + + //@formatter:off + String[] content2 = { + "package ex.|*amples;\n", + "public class A {}\n" + }; + //@formatter:on + builder = new StringBuilder(); + pos = mergeCode(builder, content2); + cu = pack1.createCompilationUnit("A.java", builder.toString(), false, null); + + prepareRename(cu, pos, "ex.am.ple"); + } + private Position mergeCode(StringBuilder builder, String[] codes) { Position pos = null; for (int i = 0; i < codes.length; i++) { From b0604c90e687a25e33a8396920a708d36be5a8ea Mon Sep 17 00:00:00 2001 From: Fred Bricon Date: Tue, 27 Nov 2018 19:03:37 -0500 Subject: [PATCH 6/6] Don't allow renaming classfiles Signed-off-by: Fred Bricon --- .../handlers/PrepareRenameHandler.java | 24 ++++++++++++--- .../handlers/PrepareRenameHandlerTest.java | 30 +++++++++++++++++++ 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandler.java index a79f74b0bb..dff9d36caa 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandler.java @@ -14,11 +14,13 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.Name; import org.eclipse.jdt.core.dom.PackageDeclaration; -import org.eclipse.jdt.core.dom.QualifiedName; -import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.manipulation.CoreASTProvider; import org.eclipse.jdt.internal.core.manipulation.search.IOccurrencesFinder.OccurrenceLocation; import org.eclipse.jdt.internal.core.manipulation.search.OccurrencesFinder; @@ -58,7 +60,7 @@ public Either prepareRename(TextDocumentPositionPara context.setASTRoot(ast); ASTNode node = context.getCoveredNode(); // Rename package is not fully supported yet. - if (!isPackageDeclaration(node)) { + if (!isPackageDeclaration(node) && !isClassFile(node)) { return Either.forLeft(JDTUtils.toRange(unit, loc.getOffset(), loc.getLength())); } } @@ -74,11 +76,25 @@ public Either prepareRename(TextDocumentPositionPara throw new ResponseErrorException(new ResponseError(ResponseErrorCode.InvalidRequest, "Renaming this element is not supported.", null)); } + private boolean isClassFile(ASTNode node) { + if (node instanceof Name) { + IBinding resolvedBinding = ((Name) node).resolveBinding(); + if (resolvedBinding instanceof ITypeBinding) { + ITypeBinding typeBinding = (ITypeBinding) resolvedBinding; + if (typeBinding.getJavaElement() != null) { + IJavaElement element = typeBinding.getJavaElement(); + return element.getAncestor(IJavaElement.CLASS_FILE) != null; + } + } + } + return false; + } + private boolean isPackageDeclaration(ASTNode node) { if (node instanceof PackageDeclaration) { return true; } - if ((node instanceof QualifiedName) || (node instanceof SimpleName)) { + if (node instanceof Name) { return isPackageDeclaration(node.getParent()); } return false; diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandlerTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandlerTest.java index ca4f998f95..cc51dfada1 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandlerTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/PrepareRenameHandlerTest.java @@ -323,6 +323,36 @@ public void testRenameMiddleOfPackage() throws JavaModelException, BadLocationEx prepareRename(cu, pos, "ex.am.ple"); } + @Test(expected = ResponseErrorException.class) + public void testRenameClassFile() throws JavaModelException, BadLocationException { + testRenameClassFile("Ex|*ception"); + } + + @Test(expected = ResponseErrorException.class) + public void testRenameFQCNClassFile() throws JavaModelException, BadLocationException { + testRenameClassFile("java.lang.Ex|*ception"); + } + + private void testRenameClassFile(String type) throws JavaModelException, BadLocationException { + when(clientPreferences.isResourceOperationSupported()).thenReturn(true); + + IPackageFragment pack1 = sourceFolder.createPackageFragment("ex.amples", false, null); + + { + //@formatter:off + String[] content = { + "package ex.amples;\n", + "public class A extends "+type+"{}\n" + }; + //@formatter:on + StringBuilder builder = new StringBuilder(); + Position pos = mergeCode(builder, content); + ICompilationUnit cu = pack1.createCompilationUnit("A.java", builder.toString(), false, null); + + prepareRename(cu, pos, "MyException"); + } + } + private Position mergeCode(StringBuilder builder, String[] codes) { Position pos = null; for (int i = 0; i < codes.length; i++) {