package org.rubypeople.rdt.internal.ui.text.correction;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.ISharedImages;
import org.jruby.ast.Node;
import org.jruby.lexer.yacc.ISourcePosition;
import org.rubypeople.rdt.core.IRubyScript;
import org.rubypeople.rdt.core.RubyModelException;
import org.rubypeople.rdt.core.compiler.IProblem;
import org.rubypeople.rdt.core.formatter.EditableFormatHelper;
import org.rubypeople.rdt.core.formatter.FormatHelper;
import org.rubypeople.rdt.core.formatter.ReWriteVisitor;
import org.rubypeople.rdt.core.formatter.ReWriterContext;
import org.rubypeople.rdt.internal.core.util.ASTUtil;
import org.rubypeople.rdt.internal.ui.RubyPlugin;
import org.rubypeople.rdt.ui.RubyUI;
import org.rubypeople.rdt.ui.text.correction.CorrectionProposal;
import org.rubypeople.rdt.ui.text.ruby.IInvocationContext;
import org.rubypeople.rdt.ui.text.ruby.IProblemLocation;
import org.rubypeople.rdt.ui.text.ruby.IQuickFixProcessor;
import org.rubypeople.rdt.ui.text.ruby.IRubyCompletionProposal;
public class QuickFixProcessor implements IQuickFixProcessor {
public IRubyCompletionProposal[] getCorrections(IInvocationContext context, IProblemLocation[] locations) throws CoreException {
if (locations == null || locations.length == 0) {
return null;
}
HashSet<Integer> handledProblems = new HashSet<Integer>(locations.length);
ArrayList<IRubyCompletionProposal> resultingCollections = new ArrayList<IRubyCompletionProposal>();
for (int i = 0; i < locations.length; i++) {
IProblemLocation curr = locations[i];
Integer id = new Integer(curr.getProblemId());
if (handledProblems.add(id)) {
process(context, curr, resultingCollections);
}
}
return (IRubyCompletionProposal[]) resultingCollections.toArray(new IRubyCompletionProposal[resultingCollections.size()]);
}
private void process(IInvocationContext context, IProblemLocation problem, Collection<IRubyCompletionProposal> proposals) throws CoreException {
int id = problem.getProblemId();
if (id == 0) { // no proposals for none-problem locations
return;
}
switch (id) {
case IProblem.UnusedPrivateMethod:
case IProblem.UnusedPrivateField:
case IProblem.LocalVariableIsNeverUsed:
case IProblem.ArgumentIsNeverUsed:
addUnusedMemberProposal(context, problem, proposals);
break;
case IProblem.MultineCommentNotAtFirstColumn:
addShiftMultilineCommentProposal(context, problem, proposals);
break;
case IProblem.ParenthesizeArguments:
addParenthesizeArgumentsProposal(context, problem, proposals);
break;
case IProblem.HashCommaSyntax:
addFixHashSyntaxProposal(context, problem, proposals);
break;
case IProblem.ColonAfterWhenStatement:
addFixWhenStatementProposal(context, problem, proposals);
break;
default:
addIgnoreWarningFix(context, problem, proposals);
}
}
private void addFixWhenStatementProposal(IInvocationContext context, IProblemLocation problem, Collection<IRubyCompletionProposal> proposals) {
String corrected = "then";
Image image= RubyUI.getSharedImages().getImage(org.rubypeople.rdt.ui.ISharedImages.IMG_OBJS_CORRECTION_CHANGE);
CorrectionProposal proposal = new CorrectionProposal(corrected, problem.getOffset(), 1, image, "Replace with 'then'", 100);
proposals.add(proposal);
}
private void addFixHashSyntaxProposal(IInvocationContext context, IProblemLocation problem, Collection<IRubyCompletionProposal> proposals) {
HashSyntaxCorrectionProposal proposal = new HashSyntaxCorrectionProposal(context, problem, 100);
proposals.add(proposal);
}
private void addIgnoreWarningFix(IInvocationContext context, IProblemLocation problem, Collection<IRubyCompletionProposal> proposals) {
proposals.add(new IgnoreWarningProposal(context, problem));
}
private void addParenthesizeArgumentsProposal(IInvocationContext context, IProblemLocation problem, Collection<IRubyCompletionProposal> proposals) throws RubyModelException {
Image image= RubyPlugin.getDefault().getWorkbench().getSharedImages().getImage(ISharedImages.IMG_TOOL_DELETE);
StringWriter out = new StringWriter();
String src = getSource(context);
ReWriterContext config = new ReWriterContext(out, getSource(context), getFormatHelper());
ReWriteVisitor visitor = new ReWriteVisitor(config);
Node covering = problem.getCoveredNode(context.getASTRoot());
String corrected = ASTUtil.stringRepresentation(covering) + "(";
ISourcePosition pos = covering.getPosition();
covering.accept(visitor);
corrected += out.toString() + ")\n";
CorrectionProposal proposal = new CorrectionProposal(corrected, pos.getStartOffset(), corrected.length(), image, "Insert missing parentheses", 100);
proposals.add(proposal);
}
private void addShiftMultilineCommentProposal(IInvocationContext context, IProblemLocation problem, Collection<IRubyCompletionProposal> proposals) throws RubyModelException {
Image image= RubyPlugin.getDefault().getWorkbench().getSharedImages().getImage(ISharedImages.IMG_TOOL_DELETE);
String contents = getSource(context);
String doc = contents.substring(problem.getOffset(), problem.getOffset() + problem.getLength());
// get where the first column is before the offset
String before = contents.substring(0, problem.getOffset());
int replaceOffset = before.lastIndexOf("\n") + 1;
int shift = problem.getOffset() - replaceOffset;
String[] lines = doc.split("\n");
String indent = "";
for (int i = 0; i < shift; i++) {
indent += " ";
}
StringBuffer correctedDoc = new StringBuffer();
for (int j = 0; j < lines.length; j++) {
String line = lines[j];
if (line.startsWith(indent)) {
line = line.substring(indent.length());
}
correctedDoc.append(line);
}
CorrectionProposal proposal = new CorrectionProposal(correctedDoc.toString(), replaceOffset, problem.getLength() + shift, image, "Shift multiline comment to first column", 100);
proposals.add(proposal);
}
private String getSource(IInvocationContext context) throws RubyModelException {
return context.getRubyScript().getBuffer().getContents();
}
protected FormatHelper getFormatHelper() {
// FIXME Hook these settings up to format prefs
EditableFormatHelper helper = new EditableFormatHelper();
helper.setAlwaysParanthesizeMethodCalls(true);
helper.setAlwaysParanthesizeMethodDefs(true);
helper.setSpacesAroundHashAssignment(true);
return helper;
}
public boolean hasCorrections(IRubyScript unit, int problemId) {
return true; // we always have at least the "ignore this warning" option
}
public static void addUnusedMemberProposal(IInvocationContext context, IProblemLocation problem, Collection<IRubyCompletionProposal> proposals) {
Image image= RubyPlugin.getDefault().getWorkbench().getSharedImages().getImage(ISharedImages.IMG_TOOL_DELETE);
CorrectionProposal proposal = new CorrectionProposal("", problem.getOffset(), problem.getLength(), image, "clean up unused code", 100);
proposals.add(proposal);
}
}