Index: src/org/codehaus/groovy/eclipse/quickassist/GroovyQuickAssist.java =================================================================== --- src/org/codehaus/groovy/eclipse/quickassist/GroovyQuickAssist.java (revision 23204) +++ src/org/codehaus/groovy/eclipse/quickassist/GroovyQuickAssist.java (working copy) @@ -41,7 +41,8 @@ new ConvertToSingleLineStringCompletionProposal(context).hasProposals() || new RemoveUnnecessarySemicolonsCompletionProposal(context).hasProposals() || new SwapOperandsCompletionProposal(context).hasProposals() || - new SplitAssigmentCompletionProposal(context).hasProposals(); + new SplitAssigmentCompletionProposal(context).hasProposals() || + new ConvertToForLoopProposal(context).hasProposals(); } return false; } @@ -94,6 +95,11 @@ proposalList.add(swapOperands); } + ConvertToForLoopProposal convertToFor = new ConvertToForLoopProposal(context); + if (convertToFor.hasProposals()) { + proposalList.add(convertToFor); + } + return proposalList.toArray(new IJavaCompletionProposal[0]); } Index: src/org/codehaus/groovy/eclipse/quickassist/ConvertToForLoopProposal.java =================================================================== --- src/org/codehaus/groovy/eclipse/quickassist/ConvertToForLoopProposal.java (revision 0) +++ src/org/codehaus/groovy/eclipse/quickassist/ConvertToForLoopProposal.java (revision 0) @@ -0,0 +1,152 @@ +/* + * Copyright 2011 SpringSource, a division of VMware, Inc + * + * andrew - Initial API and implementation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.codehaus.groovy.eclipse.quickassist; + +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.ModuleNode; +import org.codehaus.groovy.ast.expr.ArgumentListExpression; +import org.codehaus.groovy.ast.expr.ClosureExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.eclipse.codebrowsing.requestor.ASTNodeFinder; +import org.codehaus.groovy.eclipse.codebrowsing.requestor.Region; +import org.codehaus.groovy.eclipse.core.GroovyCore; +import org.codehaus.jdt.groovy.model.GroovyCompilationUnit; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.internal.ui.JavaPluginImages; +import org.eclipse.jdt.ui.text.java.IInvocationContext; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.contentassist.ContextInformation; +import org.eclipse.jface.text.contentassist.IContextInformation; +import org.eclipse.swt.graphics.Point; +import org.eclipse.text.edits.MultiTextEdit; +import org.eclipse.text.edits.ReplaceEdit; +import org.eclipse.text.edits.TextEdit; + +/** + * Converts an each expression to a for loop + * + * @author Andrew Eisenberg + * @created Mar 13, 2012 + */ +public class ConvertToForLoopProposal extends + AbstractGroovyCompletionProposal { + + private class MethodCallFinder extends ASTNodeFinder { + public MethodCallFinder(Region r) { + super(r); + } + + @Override + public void visitMethodCallExpression(MethodCallExpression call) { + if (call.getMethodAsString().equals("each")) { + check(call); + } + super.visitMethodCallExpression(call); + } + } + + private final GroovyCompilationUnit unit; + private final int length; + private final int offset; + + private MethodCallExpression expr; + public ConvertToForLoopProposal(IInvocationContext context) { + super(context); + ICompilationUnit compUnit = context.getCompilationUnit(); + if (compUnit instanceof GroovyCompilationUnit) { + this.unit = (GroovyCompilationUnit) compUnit; + } else { + this.unit = null; + } + length = context.getSelectionLength(); + offset = context.getSelectionOffset(); + + } + + public int getRelevance() { + return 0; + } + + public void apply(IDocument document) { + TextEdit thisEdit = findReplacement(document); + try { + if (thisEdit != null) { + thisEdit.apply(document); + } + } catch (Exception e) { + GroovyCore.logException("Problem applying quickfix.", e); + } + } + + public Point getSelection(IDocument document) { + // this is not right. We should be updating the position based on the text changes + return new Point(offset, length+offset); + } + + public String getAdditionalProposalInfo() { + return getDisplayString(); + } + + public String getDisplayString() { + return "Convert to for loop"; + } + + public IContextInformation getContextInformation() { + return new ContextInformation(getImage(), getDisplayString(), getDisplayString()); + } + + @Override + protected String getImageBundleLocation() { + return JavaPluginImages.IMG_CORRECTION_CHANGE; + } + + @Override + public boolean hasProposals() { + if (unit == null) { + return false; + } + Region region = new Region(offset, length); + ModuleNode moduleNode = unit.getModuleNode(); + + MethodCallFinder finder = new MethodCallFinder(region); + ASTNode node = finder.doVisit(moduleNode); + + if (node instanceof MethodCallExpression) { + expr = (MethodCallExpression) node; + + // method name has already been checked. No need to check it again. +// String name = expr.getMethodAsString(); +// if (!name.equals("each")) { +// return false; +// } + if ((expr.getArguments() instanceof ArgumentListExpression) + && (((ArgumentListExpression) expr.getArguments()).getExpressions().size() == 1) + && (expr.getObjectExpression() != null)) { + return true; + } + } + return false; + } + + // TODO more work to do here. + private TextEdit findReplacement(IDocument doc) { + return null; + } +}