package ts.eclipse.ide.jsdt.internal.ui.refactoring; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Status; import org.eclipse.jface.text.Position; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.CompositeChange; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.ltk.core.refactoring.TextFileChange; import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext; import org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant; import org.eclipse.ltk.core.refactoring.participants.RenameProcessor; import org.eclipse.ltk.core.refactoring.participants.SharableParticipants; import org.eclipse.text.edits.MultiTextEdit; import org.eclipse.text.edits.ReplaceEdit; import ts.client.TextSpan; import ts.client.rename.RenameInfo; import ts.client.rename.RenameResponseBody; import ts.client.rename.SpanGroup; import ts.eclipse.ide.core.utils.WorkbenchResourceUtil; import ts.eclipse.ide.jsdt.core.JSDTTypeScriptCorePlugin; import ts.eclipse.ide.ui.utils.EditorUtils; import ts.resources.ITypeScriptFile; public class TypeScriptRenameProcessor extends RenameProcessor { private static final String TEXT_TYPE = "ts"; private static final String ID = "ts.eclipse.ide.core.refactoring.rename"; private final ITypeScriptFile tsFile; private final int offset; private final String oldName; private String newName; private boolean findInComments; private boolean findInStrings; private RenameResponseBody rename; public TypeScriptRenameProcessor(ITypeScriptFile tsFile, int offset, String oldName) { this.tsFile = tsFile; this.offset = offset; this.oldName = oldName; } @Override public Object[] getElements() { return null; } @Override public String getIdentifier() { return ID; } @Override public String getProcessorName() { return RefactoringMessages.TypeScriptRenameProcessor_name; } @Override public boolean isApplicable() throws CoreException { return true; } @Override public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException { rename = null; return new RefactoringStatus(); } @Override public RefactoringStatus checkFinalConditions(IProgressMonitor pm, CheckConditionsContext context) throws CoreException, OperationCanceledException { RefactoringStatus status = new RefactoringStatus(); // Consume "rename" tsserver command. try { rename = tsFile.rename(offset, isFindInComments(), isFindInStrings()).get(1000, TimeUnit.MILLISECONDS); RenameInfo info = rename.getInfo(); if (!info.isCanRename()) { // Refactoring cannot be done. status.addError(info.getLocalizedErrorMessage()); } } catch (Exception e) { status.addError(e.getMessage()); } return status; } @Override public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { try { if (rename == null) { throw new CoreException(new Status(IStatus.ERROR, JSDTTypeScriptCorePlugin.PLUGIN_ID, "TypeScript rename cannot be null")); } // Convert TypeScript changes to Eclipse changes. List<SpanGroup> locs = rename.getLocs(); List<Change> fileChanges = new ArrayList<>(); for (SpanGroup loc : locs) { IFile file = WorkbenchResourceUtil.findFileFromWorkspace(loc.getFile()); TextFileChange change = new TextFileChange(file.getName(), file); change.setEdit(new MultiTextEdit()); change.setTextType(TEXT_TYPE); List<TextSpan> spans = loc.getLocs(); for (TextSpan textSpan : spans) { Position position = EditorUtils.getPosition(file, textSpan); ReplaceEdit edit = new ReplaceEdit(position.offset, position.length, this.newName); change.addEdit(edit); } fileChanges.add(change); } return new CompositeChange(RefactoringMessages.TypeScriptRenameProcessor_change_name, fileChanges.toArray(new Change[fileChanges.size()])); } catch (CoreException | OperationCanceledException e) { throw e; } catch (Exception e) { throw new CoreException( new Status(IStatus.ERROR, JSDTTypeScriptCorePlugin.PLUGIN_ID, "Error while rename", e)); } } @Override public RefactoringParticipant[] loadParticipants(RefactoringStatus status, SharableParticipants sharedParticipants) throws CoreException { return null; } public String getOldName() { return this.oldName; } public void setNewName(String newName) { Assert.isNotNull(newName); this.newName = newName; } public int getSaveMode() { return RefactoringSaveHelper.SAVE_ALL_ALWAYS_ASK; } public boolean isFindInComments() { return findInComments; } public void setFindInComments(boolean findInComments) { this.findInComments = findInComments; } public boolean isFindInStrings() { return findInStrings; } public void setFindInStrings(boolean findInStrings) { this.findInStrings = findInStrings; } }