package com.redhat.ceylon.eclipse.code.refactor; import static com.redhat.ceylon.eclipse.code.correct.ImportProposals.importProposals; import static com.redhat.ceylon.eclipse.util.EditorUtil.getCurrentEditor; import static com.redhat.ceylon.eclipse.util.EditorUtil.getSelection; import static org.eclipse.core.resources.ResourcesPlugin.getWorkspace; import static org.eclipse.ltk.core.refactoring.RefactoringCore.getUndoManager; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextSelection; import org.eclipse.ltk.core.refactoring.PerformChangeOperation; import org.eclipse.ltk.core.refactoring.TextChange; import org.eclipse.ltk.core.refactoring.TextFileChange; import org.eclipse.text.edits.InsertEdit; import org.eclipse.text.edits.MultiTextEdit; import org.eclipse.text.edits.ReplaceEdit; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IFileEditorInput; import com.redhat.ceylon.compiler.typechecker.tree.Node; import com.redhat.ceylon.compiler.typechecker.tree.Tree; import com.redhat.ceylon.compiler.typechecker.tree.Tree.LocalModifier; import com.redhat.ceylon.compiler.typechecker.tree.Visitor; import com.redhat.ceylon.eclipse.code.correct.correctJ2C; import com.redhat.ceylon.eclipse.code.editor.CeylonEditor; import com.redhat.ceylon.eclipse.platform.platformJ2C; import com.redhat.ceylon.model.typechecker.model.Declaration; import com.redhat.ceylon.model.typechecker.model.Type; import com.redhat.ceylon.model.typechecker.model.Unit; import ceylon.interop.java.CeylonMutableSet; import ceylon.interop.java.CeylonSet; public class RevealInferredTypeHandler extends AbstractHandler { @Override public boolean isEnabled() { IEditorPart ce = getCurrentEditor(); if (!(ce instanceof CeylonEditor)) return false; CeylonEditor editor = (CeylonEditor) ce; List<Tree.LocalModifier> localModifiers = new ArrayList<Tree.LocalModifier>(); List<Tree.ValueIterator> valueIterators = new ArrayList<Tree.ValueIterator>(); findCandidatesForRevelation(editor, localModifiers, valueIterators); return !localModifiers.isEmpty() || !valueIterators.isEmpty(); } @Override public Object execute(ExecutionEvent event) throws ExecutionException { CeylonEditor editor = (CeylonEditor) getCurrentEditor(); Tree.CompilationUnit rootNode = editor.getParseController().getLastCompilationUnit(); Set<Declaration> imports = new HashSet<Declaration>(); List<Tree.LocalModifier> localModifiers = new ArrayList<Tree.LocalModifier>(); List<Tree.ValueIterator> valueIterators = new ArrayList<Tree.ValueIterator>(); findCandidatesForRevelation(editor, localModifiers, valueIterators); if( !localModifiers.isEmpty() || !valueIterators.isEmpty() ) { IFileEditorInput input = (IFileEditorInput) editor.getEditorInput(); TextChange tfc = new TextFileChange("Reveal Inferred Types", input.getFile()); tfc.setEdit(new MultiTextEdit()); tfc.initializeValidationData(null); Unit unit = rootNode.getUnit(); for (Tree.LocalModifier localModifier : localModifiers) { if( localModifier.getStartIndex() != null && localModifier.getTypeModel() != null ) { Type pt = localModifier.getTypeModel(); tfc.addEdit(new ReplaceEdit( localModifier.getStartIndex(), localModifier.getText().length(), pt.asSourceCodeString(unit))); importProposals().importType( new CeylonMutableSet<>(null, imports), pt, rootNode); } } for (Tree.ValueIterator valueIterator : valueIterators) { Tree.Variable variable = valueIterator.getVariable(); if( variable != null && variable.getStartIndex() != null && variable.getType() != null && variable.getType().getTypeModel() != null ) { Type pt = variable.getType().getTypeModel(); tfc.addEdit(new InsertEdit( variable.getStartIndex(), pt.asSourceCodeString(unit) + " ")); importProposals() .importType( new CeylonMutableSet<>(null, imports), variable.getType().getTypeModel(), rootNode); } } try { IDocument doc = tfc.getCurrentDocument(null); com.redhat.ceylon.ide.common.platform.TextChange chg = new platformJ2C().newChange(tfc.getName(), tfc); importProposals() .applyImports(chg, new CeylonSet<>(null, imports), rootNode, new correctJ2C().newDocument(doc)); PerformChangeOperation changeOperation = new PerformChangeOperation(tfc); changeOperation.setUndoManager(getUndoManager(), "Reveal Inferred Types"); getWorkspace().run(changeOperation, new NullProgressMonitor()); } catch (CoreException ce) { throw new ExecutionException("Error reveal inferred types", ce); } } return null; } private void findCandidatesForRevelation(CeylonEditor editor, final List<Tree.LocalModifier> localModifiers, final List<Tree.ValueIterator> valueIterators) { if (editor!=null && editor.getParseController()!=null) { final Tree.CompilationUnit rootNode = editor.getParseController().getTypecheckedRootNode(); final ITextSelection selection = getSelection(editor); if (rootNode==null || selection==null) { return; } final int selectionStart = selection.getOffset(); final int selectionStop = selection.getOffset() + selection.getLength(); rootNode.visit(new Visitor() { @Override public void visit(Tree.TypedDeclaration typedDeclaration) { if( isInSelection(typedDeclaration) ) { Tree.Type type = typedDeclaration.getType(); if( type instanceof Tree.LocalModifier && type.getToken() != null ) { localModifiers.add((LocalModifier) type); } } super.visit(typedDeclaration); } @Override public void visit(Tree.ValueIterator valueIterator) { if (isInSelection(valueIterator)) { Tree.Variable variable = valueIterator.getVariable(); Tree.Type type = variable.getType(); if (type instanceof Tree.ValueModifier) { valueIterators.add(valueIterator); } } super.visit(valueIterator); } private boolean isInSelection(Node node) { Integer startIndex = node.getStartIndex(); Integer endIndex = node.getEndIndex(); if (startIndex != null && endIndex != null) { if (selection.getLength() == 0 /* if selection is empty, process whole file */ || (startIndex >= selectionStart && endIndex <= selectionStop) ) { return true; } } return false; } }); } } }