package com.redhat.ceylon.eclipse.code.correct; import static com.redhat.ceylon.eclipse.code.correct.AssignToAssertExistsProposal.addAssignToAssertExistsProposal; import static com.redhat.ceylon.eclipse.code.correct.AssignToAssertIsProposal.addAssignToAssertIsProposal; import static com.redhat.ceylon.eclipse.code.correct.AssignToAssertNonemptyProposal.addAssignToAssertNonemptyProposal; import static com.redhat.ceylon.eclipse.code.correct.AssignToForProposal.addAssignToForProposal; import static com.redhat.ceylon.eclipse.code.correct.AssignToIfExistsProposal.addAssignToIfExistsProposal; import static com.redhat.ceylon.eclipse.code.correct.AssignToIfIsProposal.addAssignToIfIsProposal; import static com.redhat.ceylon.eclipse.code.correct.AssignToIfNonemptyProposal.addAssignToIfNonemptyProposal; import static com.redhat.ceylon.eclipse.code.correct.AssignToTryProposal.addAssignToTryProposal; import static com.redhat.ceylon.eclipse.code.correct.ConvertFunctionToGetterProposal.addConvertFunctionToGetterProposal; import static com.redhat.ceylon.eclipse.code.correct.ConvertGetterToFunctionProposal.addConvertGetterToFunctionProposal; import static com.redhat.ceylon.eclipse.code.correct.DestructureProposal.addDestructureProposal; import static com.redhat.ceylon.eclipse.code.correct.MoveDirProposal.addMoveDirProposal; import static com.redhat.ceylon.eclipse.code.correct.PrintProposal.addPrintProposal; import static com.redhat.ceylon.eclipse.code.correct.RemoveAliasProposal.addRemoveAliasProposal; import static com.redhat.ceylon.eclipse.code.correct.RenameAliasProposal.addRenameAliasProposal; import static com.redhat.ceylon.eclipse.code.correct.RenameVersionProposal.addRenameVersionProposals; import static com.redhat.ceylon.eclipse.code.correct.UseAliasProposal.addUseAliasProposal; import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.MODULE_DEPENDENCY_PROBLEM_MARKER_ID; import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.PROBLEM_MARKER_ID; import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.getProjectTypeChecker; import static com.redhat.ceylon.eclipse.core.builder.MarkerCreator.ERROR_CODE_KEY; import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.correctJ2C; import static com.redhat.ceylon.eclipse.util.AnnotationUtils.getAnnotationsForLine; import static com.redhat.ceylon.eclipse.util.EditorUtil.getCurrentEditor; import static com.redhat.ceylon.eclipse.util.Nodes.findArgument; import static com.redhat.ceylon.eclipse.util.Nodes.findDeclaration; import static com.redhat.ceylon.eclipse.util.Nodes.findImport; import static com.redhat.ceylon.eclipse.util.Nodes.findNode; import static com.redhat.ceylon.eclipse.util.Nodes.findOperator; import static com.redhat.ceylon.eclipse.util.Nodes.findStatement; import static org.eclipse.ui.PlatformUI.getWorkbench; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext; import org.eclipse.jface.text.quickassist.IQuickAssistProcessor; import org.eclipse.jface.text.quickassist.QuickAssistAssistant; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.part.FileEditorInput; import org.eclipse.ui.texteditor.MarkerAnnotation; import com.redhat.ceylon.compiler.typechecker.TypeChecker; import com.redhat.ceylon.compiler.typechecker.analyzer.UsageWarning; import com.redhat.ceylon.compiler.typechecker.context.PhasedUnit; import com.redhat.ceylon.compiler.typechecker.tree.Node; import com.redhat.ceylon.compiler.typechecker.tree.Tree; import com.redhat.ceylon.eclipse.code.editor.CeylonAnnotation; import com.redhat.ceylon.eclipse.code.editor.CeylonEditor; import com.redhat.ceylon.eclipse.code.parse.CeylonParseController; import com.redhat.ceylon.eclipse.core.builder.MarkerCreator; import com.redhat.ceylon.eclipse.util.EditorUtil; import com.redhat.ceylon.eclipse.util.MarkerUtils; public class CeylonCorrectionProcessor extends QuickAssistAssistant implements IQuickAssistProcessor { private static final ProblemLocation[] NO_PROBLEM_LOCATIONS = new ProblemLocation[0]; private static final ICompletionProposal[] NO_PROPOSALS = new ICompletionProposal[0]; CeylonEditor editor; //may only be used for quick assists!!! private Tree.CompilationUnit model; private IFile file; //may only be used for markers! public CeylonCorrectionProcessor(CeylonEditor editor) { this.editor = editor; setQuickAssistProcessor(this); } public CeylonCorrectionProcessor(IMarker marker) { IFileEditorInput input = MarkerUtils.getInput(marker); if (input!=null) { file = input.getFile(); IProject project = file.getProject(); IJavaProject javaProject = JavaCore.create(project); TypeChecker tc = getProjectTypeChecker(project); if (tc!=null) { try { for (IPackageFragmentRoot pfr: javaProject.getPackageFragmentRoots()) { if (pfr.getPath() .isPrefixOf(file.getFullPath())) { IPath relPath = file.getFullPath() .makeRelativeTo( pfr.getPath()); PhasedUnit pu = tc.getPhasedUnitFromRelativePath( relPath.toString()); model = pu.getCompilationUnit(); } } } catch (JavaModelException e) { e.printStackTrace(); } } } setQuickAssistProcessor(this); } private IFile getFile() { if (editor!=null) { IEditorInput ei = editor.getEditorInput(); if (ei instanceof FileEditorInput) { FileEditorInput input = (FileEditorInput) ei; if (input!=null) { return input.getFile(); } } } return file; } private Tree.CompilationUnit getRootNode() { if (editor!=null) { Tree.CompilationUnit upToDateRootNode = editor.getParseController() .getTypecheckedRootNode(); if (upToDateRootNode != null) { return upToDateRootNode; } } if (model!=null) { return (Tree.CompilationUnit) model; } return null; } @Override public String getErrorMessage() { return null; } private void collectProposals( IQuickAssistInvocationContext context, IAnnotationModel model, Collection<Annotation> annotations, boolean addQuickFixes, boolean addQuickAssists, Collection<ICompletionProposal> proposals) { ArrayList<ProblemLocation> problems = new ArrayList<ProblemLocation>(); // collect problem locations and corrections from marker annotations for (Annotation curr: annotations) { if (curr instanceof CeylonAnnotation) { CeylonAnnotation ca = (CeylonAnnotation) curr; ProblemLocation problemLocation = getProblemLocation(ca, model); if (problemLocation != null) { problems.add(problemLocation); } } else if (curr instanceof MarkerAnnotation) { MarkerAnnotation ma = (MarkerAnnotation) curr; ProblemLocation problemLocation = getProblemLocation(ma, model); if (problemLocation != null) { problems.add(problemLocation); } } } ProblemLocation[] problemLocations = problems.toArray(NO_PROBLEM_LOCATIONS); Arrays.sort(problemLocations); if (addQuickFixes) { collectCorrections(context, problemLocations, proposals); } if (addQuickAssists) { collectAssists(context, problemLocations, proposals); } if (addQuickFixes) { addSuppressWarningsProposals(context, model, annotations, proposals); } } public void addSuppressWarningsProposals( IQuickAssistInvocationContext context, IAnnotationModel model, Collection<Annotation> annotations, Collection<ICompletionProposal> proposals) { for (Annotation curr: annotations) { if (curr instanceof CeylonAnnotation) { CeylonAnnotation ca = (CeylonAnnotation) curr; if (ca.getSeverity()==IMarker.SEVERITY_WARNING) { ProblemLocation problemLocation = getProblemLocation(ca, model); if (problemLocation != null) { collectWarningProposals(ca, context, problemLocation, proposals); break; } } } } } private static ProblemLocation getProblemLocation( CeylonAnnotation annotation, IAnnotationModel model) { int problemId = annotation.getId(); if (problemId != -1) { Position pos = model.getPosition(annotation); if (pos != null) { return new ProblemLocation( pos.getOffset(), pos.getLength(), problemId); // java problems all handled by the quick assist processors } } return null; } private static ProblemLocation getProblemLocation( MarkerAnnotation annotation, IAnnotationModel model) { Integer problemId = null; try { problemId = (Integer) annotation.getMarker() .getAttribute(MarkerCreator.ERROR_CODE_KEY); } catch (CoreException e) { e.printStackTrace(); } if (problemId != null) { Position pos = model.getPosition(annotation); if (pos != null) { return new ProblemLocation( pos.getOffset(), pos.getLength(), problemId); // java problems all handled by the quick assist processors } } return null; } private void collectAssists( IQuickAssistInvocationContext context, ProblemLocation[] locations, Collection<ICompletionProposal> proposals) { if (proposals.isEmpty()) { addProposalsWithProgress(context, editor, proposals); } } @Override public ICompletionProposal[] computeQuickAssistProposals( IQuickAssistInvocationContext context) { ArrayList<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>(); ISourceViewer viewer = context.getSourceViewer(); List<Annotation> annotations = getAnnotationsForLine(viewer, getLine(context, viewer)); collectProposals(context, viewer.getAnnotationModel(), annotations, true, true, proposals); return proposals.toArray(NO_PROPOSALS); } private void addProposalsWithProgress( final IQuickAssistInvocationContext context, final ProblemLocation location, final IFile file, final Tree.CompilationUnit rootNode, final Collection<ICompletionProposal> proposals) { class Runnable implements IRunnableWithProgress { @Override public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { monitor.beginTask("Preparing fix proposals...", IProgressMonitor.UNKNOWN); addProposals(context, location, file, rootNode, proposals); monitor.done(); } } Runnable runnable = new Runnable(); try { getWorkbench() .getActiveWorkbenchWindow() .run(true, true, runnable); } catch (Exception e) { e.printStackTrace(); } } private void addProposalsWithProgress( final IQuickAssistInvocationContext context, final CeylonEditor editor, final Collection<ICompletionProposal> proposals) { class Runnable implements IRunnableWithProgress { @Override public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { monitor.beginTask("Preparing assist proposals...", IProgressMonitor.UNKNOWN); addProposals(context, editor, proposals); monitor.done(); } } Runnable runnable = new Runnable(); try { getWorkbench() .getActiveWorkbenchWindow() //we have to run this in the UI thread .run(false, true, runnable); } catch (Exception e) { e.printStackTrace(); } } private int getLine( IQuickAssistInvocationContext context, ISourceViewer viewer) { try { return viewer.getDocument() .getLineOfOffset(context.getOffset()); } catch (BadLocationException e) { e.printStackTrace(); return 0; } } public void collectCorrections( IQuickAssistInvocationContext context, ProblemLocation location, Collection<ICompletionProposal> proposals) { Tree.CompilationUnit rootNode = getRootNode(); if (rootNode!=null) { addProposals(context, location, getFile(), rootNode, proposals); } } private void collectCorrections( IQuickAssistInvocationContext context, ProblemLocation[] locations, Collection<ICompletionProposal> proposals) { ISourceViewer viewer = context.getSourceViewer(); Tree.CompilationUnit rootNode = getRootNode(); if (rootNode == null) { return; } for (int i=locations.length-1; i>=0; i--) { ProblemLocation loc = locations[i]; if (loc.getOffset()<=viewer.getSelectedRange().x) { for (int j=i; j>=0; j--) { ProblemLocation location = locations[j]; if (location.getOffset()!=loc.getOffset()) { break; } addProposalsWithProgress(context, location, getFile(), rootNode, proposals); } if (!proposals.isEmpty()) { viewer.setSelectedRange(loc.getOffset(), loc.getLength()); return; } } } for (int i=0; i<locations.length; i++) { ProblemLocation loc = locations[i]; for (int j=i; j<locations.length; j++) { ProblemLocation location = locations[j]; if (location.getOffset()!=loc.getOffset()) break; addProposalsWithProgress(context, location, getFile(), rootNode, proposals); } if (!proposals.isEmpty()) { viewer.setSelectedRange(loc.getOffset(), loc.getLength()); return; } } } public static boolean canFix(IMarker marker) { try { String mt = marker.getType(); if (mt.equals(PROBLEM_MARKER_ID) || mt.equals(MODULE_DEPENDENCY_PROBLEM_MARKER_ID)) { int code = marker.getAttribute(ERROR_CODE_KEY, 0); return code>0; } else { return false; } } catch (CoreException e) { return false; } } @Override public boolean canFix(Annotation annotation) { if (annotation instanceof CeylonAnnotation) { CeylonAnnotation ceylonAnnotation = (CeylonAnnotation) annotation; return ceylonAnnotation.isFixable(); } else if (annotation instanceof MarkerAnnotation) { MarkerAnnotation markerAnnotation = (MarkerAnnotation) annotation; return canFix(markerAnnotation.getMarker()); } else { return false; } } @Override public boolean canAssist(IQuickAssistInvocationContext context) { //oops, all this is totally useless, because //this method never gets called :-/ /*Tree.CompilationUnit cu = (CompilationUnit) context.getModel() .getAST(new NullMessageHandler(), new NullProgressMonitor()); return CeylonSourcePositionLocator.findNode(cu, null, context.getOffset(), context.getOffset()+context.getLength()) instanceof Tree.Term;*/ return true; } CeylonEditor getCurrentCeylonEditor() { if (editor != null) { return editor; } IEditorPart editorPart = getCurrentEditor(); if (editorPart instanceof CeylonEditor) { return (CeylonEditor) editorPart; } return null; } private void addProposals( IQuickAssistInvocationContext context, ProblemLocation problem, IFile file, Tree.CompilationUnit rootNode, Collection<ICompletionProposal> proposals) { if (file==null) return; IProject project = file.getProject(); TypeChecker tc = getProjectTypeChecker(project); int start = problem.getOffset(); int end = start + problem.getLength(); Node node = findNode(rootNode, null, start, end); ISourceViewer sourceViewer = context.getSourceViewer(); correctJ2C().addQuickFixes(problem, rootNode, node, project, proposals, getCurrentCeylonEditor(), tc, file, sourceViewer == null ? null : sourceViewer.getDocument()); switch (problem.getProblemId()) { // case 100: // addDeclareLocalProposal(rootNode, node, proposals, file, editor); // //fall through: // case 102: // if (tc!=null) { // importProposals().addImportProposals(rootNode, node, proposals, file); // } // addCreateEnumProposal(rootNode, node, problem, proposals, project); // addCreationProposals(rootNode, node, problem, proposals, project, file); // if (tc!=null) { // addChangeReferenceProposals(rootNode, node, problem, proposals, file); // } // break; // case 101: // addCreateParameterProposals(rootNode, node, problem, proposals, project); // if (tc!=null) { // addChangeArgumentReferenceProposals(rootNode, node, problem, proposals, file); // } // break; // case 200: // addSpecifyTypeProposal(rootNode, node, proposals, null); // break; // case 300: // addRefineFormalMembersProposal(proposals, node, rootNode, false); // addMakeAbstractDecProposal(proposals, project, node); // break; // case 350: // addRefineFormalMembersProposal(proposals, node, rootNode, true); // addMakeAbstractDecProposal(proposals, project, node); // break; // case 310: // addMakeAbstractDecProposal(proposals, project, node); // break; // case 320: // addRemoveAnnotationProposal(node, "formal", proposals, project); // break; // case 400: // case 402: // addMakeSharedProposal(proposals, project, node); // break; // case 705: // addMakeSharedDecProposal(proposals, project, node); // break; // case 500: // case 510: // addMakeDefaultProposal(proposals, project, node); // break; // case 600: // addMakeActualDecProposal(proposals, project, node); // break; // case 701: // addMakeSharedDecProposal(proposals, project, node); // addRemoveAnnotationDecProposal(proposals, "actual", project, node); // break; // case 702: // addMakeSharedDecProposal(proposals, project, node); // addRemoveAnnotationDecProposal(proposals, "formal", project, node); // break; // case 703: // addMakeSharedDecProposal(proposals, project, node); // addRemoveAnnotationDecProposal(proposals, "default", project, node); // break; // case 710: // case 711: // addMakeSharedProposal(proposals, project, node); // break; // case 712: // addExportModuleImportProposal(proposals, project, node); // break; // case 713: // addMakeSharedProposalForSupertypes(proposals, project, node); // break; // case 714: // addExportModuleImportProposalForSupertypes(proposals, project, node, rootNode); // break; // case 800: // case 804: // addMakeVariableProposal(proposals, project, node); // break; // case 803: // addMakeVariableProposal(proposals, project, node); // break; // case 801: // addMakeVariableDecProposal(proposals, project, rootNode, node); // break; // case 802: // break; // case 905: // addMakeContainerAbstractProposal(proposals, project, node); // break; // case 1100: // addMakeContainerAbstractProposal(proposals, project, node); // addRemoveAnnotationDecProposal(proposals, "formal", project, node); // break; // case 1101: // addRemoveAnnotationDecProposal(proposals, "formal", project, node); // //TODO: replace body with ; // break; // case 1000: // case 1001: // addEmptyParameterListProposal(file, proposals, node); // addParameterListProposal(file, proposals, node, rootNode, false); // addConstructorProposal(file, proposals, node, rootNode); // addChangeDeclarationProposal(problem, file, proposals, node); // break; // case 1020: // addImportWildcardProposal(file, proposals, node); // break; // case 1050: // addFixAliasProposal(proposals, file, problem); // break; // case 1200: // case 1201: // addRemoveAnnotationDecProposal(proposals, "shared", project, node); // break; // case 1300: // case 1301: // addMakeRefinedSharedProposal(proposals, project, node); // addRemoveAnnotationDecProposal(proposals, "actual", project, node); // break; // case 1303: // case 1313: // case 1320: // addRemoveAnnotationDecProposal(proposals, "formal", project, node); // addRemoveAnnotationDecProposal(proposals, "default", project, node); // break; // case 1350: // addRemoveAnnotationDecProposal(proposals, "default", project, node); // addMakeContainerNonfinalProposal(proposals, project, node); // break; // case 1400: // case 1401: // addMakeFormalDecProposal(proposals, project, node); // break; // case 1450: // addMakeFormalDecProposal(proposals, project, node); // addParameterProposals(proposals, file, rootNode, node); // addInitializerProposals(proposals, file, rootNode, node); // addParameterListProposal(file, proposals, node, rootNode, false); // addConstructorProposal(file, proposals, node, rootNode); // break; // case 1610: // addRemoveAnnotationDecProposal(proposals, "shared", project, node); // addRemoveAnnotationDecProposal(proposals, "abstract", project, node); // break; // case 1500: // case 1501: // addRemoveAnnotationDecProposal(proposals, "variable", project, node); // break; // case 1600: // case 1601: // addRemoveAnnotationDecProposal(proposals, "abstract", project, node); // break; // case 1700: // addRemoveAnnotationDecProposal(proposals, "final", project, node); // break; // case 1800: // case 1801: // addRemoveAnnotationDecProposal(proposals, "sealed", project, node); // break; // case 1900: // addRemoveAnnotationDecProposal(proposals, "late", project, node); // break; // case 1950: // case 1951: // addRemoveAnnotationDecProposal(proposals, "annotation", project, node); // break; // case 2000: // addCreateParameterProposals(rootNode, node, problem, proposals, project); // break; // case 2100: // addAppendMemberReferenceProposals(rootNode, node, problem, proposals, file); // addChangeTypeProposals(rootNode, node, problem, proposals, project); // addSatisfiesProposals(rootNode, node, proposals, project); // break; // case 2102: // addChangeTypeArgProposals(rootNode, node, problem, proposals, project); // addSatisfiesProposals(rootNode, node, proposals, project); // break; // case 2101: // addSpreadToSequenceParameterProposal(rootNode, node, proposals, file); // break; // case 2500: // addTypeParameterProposal(file, rootNode, proposals, node); // break; case 3000: CeylonEditor currentEditor = getCurrentCeylonEditor(); // addAssignToLocalProposal(currentEditor, rootNode, proposals, node, start); addDestructureProposal(currentEditor, rootNode, proposals, node, start); addAssignToForProposal(currentEditor, rootNode, proposals, node, start); addAssignToIfExistsProposal(currentEditor, rootNode, proposals, node, start); addAssignToAssertExistsProposal(currentEditor, rootNode, proposals, node, start); addAssignToIfNonemptyProposal(currentEditor, rootNode, proposals, node, start); addAssignToAssertNonemptyProposal(currentEditor, rootNode, proposals, node, start); addAssignToTryProposal(currentEditor, rootNode, proposals, node, start); addAssignToIfIsProposal(currentEditor, rootNode, proposals, node, start); addAssignToAssertIsProposal(currentEditor, rootNode, proposals, node, start); addPrintProposal(rootNode, proposals, node, start); break; // case 3100: // addShadowReferenceProposal(file, node, rootNode, proposals); // break; // case 3101: // case 3102: // addShadowSwitchReferenceProposal(file, node, rootNode, proposals); // break; // case 5001: // case 5002: // addChangeIdentifierCaseProposal(node, proposals, file); // break; // case 6000: // addFixMultilineStringIndentation(proposals, file, rootNode, node); // break; // case 7000: // addModuleImportProposals(proposals, project, tc, node); // break; case 8000: // addRenameDescriptorProposal(rootNode, context, problem, proposals, file); if (sourceViewer!=null) { addMoveDirProposal(file, rootNode, project, proposals, context); } break; // case 9000: // addChangeRefiningTypeProposal(file, rootNode, proposals, node); // break; // case 9100: // case 9200: // addChangeRefiningParametersProposal(file, rootNode, proposals, node); // break; // case 10000: // addElseProposal(file, rootNode, proposals, node); // addCasesProposal(file, rootNode, proposals, node); // break; // case 11000: // addNamedArgumentsProposal(file, rootNode, proposals, node); // break; // case 12000: // case 12100: // changeToVoid(file, rootNode, node, proposals); // break; // case 13000: // changeToFunction(file, rootNode, node, proposals); // break; // case 20000: // addMakeNativeProposal(proposals, project, node, rootNode, file); // break; } } private void addProposals( IQuickAssistInvocationContext context, CeylonEditor editor, Collection<ICompletionProposal> proposals) { if (editor==null) return; IDocument doc = context.getSourceViewer().getDocument(); IEditorInput input = editor.getEditorInput(); IProject project = EditorUtil.getProject(input); IFile file = EditorUtil.getFile(input); IRegion selection = editor.getSelection(); CeylonParseController parseController = editor.getParseController(); Tree.CompilationUnit rootNode = parseController.getTypecheckedRootNode(); if (rootNode!=null) { int start = context.getOffset(); int len = context.getLength(); int end = start + (len>0?len:0); //len==-1 means missing info Node node = findNode(rootNode, parseController.getTokens(), start, end); int currentOffset = selection.getOffset(); CeylonEditor currentEditor = getCurrentCeylonEditor(); // addAssignToLocalProposal(currentEditor, rootNode, proposals, node, currentOffset); addDestructureProposal(currentEditor, rootNode, proposals, node, currentOffset); addAssignToForProposal(currentEditor, rootNode, proposals, node, currentOffset); addAssignToIfExistsProposal(currentEditor, rootNode, proposals, node, currentOffset); addAssignToAssertExistsProposal(currentEditor, rootNode, proposals, node, currentOffset); addAssignToIfNonemptyProposal(currentEditor, rootNode, proposals, node, currentOffset); addAssignToAssertNonemptyProposal(currentEditor, rootNode, proposals, node, currentOffset); addAssignToTryProposal(currentEditor, rootNode, proposals, node, currentOffset); addAssignToIfIsProposal(currentEditor, rootNode, proposals, node, currentOffset); addAssignToAssertIsProposal(currentEditor, rootNode, proposals, node, currentOffset); addPrintProposal(rootNode, proposals, node, currentOffset); // addConvertToNamedArgumentsProposal(proposals, file, rootNode, // editor, currentOffset); // addConvertToPositionalArgumentsProposal(proposals, file, rootNode, // editor, currentOffset); Tree.Statement statement = findStatement(rootNode, node); Tree.Declaration declaration = findDeclaration(rootNode, node); Tree.NamedArgument argument = findArgument(rootNode, node); Tree.ImportMemberOrType imp = findImport(rootNode, node); Tree.OperatorExpression oe = findOperator(rootNode, node); correctJ2C().addQuickAssists(rootNode, node, project, proposals, editor, file, doc, statement, declaration, argument, imp, oe, currentOffset); // addOperatorProposals(proposals, file, oe); // addParenthesesProposals(proposals, file, node, rootNode, oe); // addVerboseRefinementProposal(proposals, file, statement, rootNode); // addAnnotationProposals(proposals, project, declaration, // doc, currentOffset); // addTypingProposals(proposals, file, rootNode, node, declaration, editor); // addAnonymousFunctionProposals(editor, proposals, doc, file, rootNode, // currentOffset); // addDeclarationProposals(editor, proposals, doc, file, rootNode, // declaration, currentOffset); // addAssignToFieldProposal(file, statement, declaration, proposals); // addChangeToIfProposal(proposals, doc, file, rootNode, statement); // addConvertToDefaultConstructorProposal(proposals, doc, file, rootNode, statement); // addConvertToClassProposal(proposals, declaration, editor); // addAssertExistsDeclarationProposals(proposals, doc, file, rootNode, declaration); // addSplitDeclarationProposals(proposals, doc, file, rootNode, declaration, statement); // addJoinDeclarationProposal(proposals, rootNode, statement, file); // addParameterProposals(proposals, file, rootNode, declaration); // addArgumentProposals(proposals, doc, file, argument); addUseAliasProposal(imp, proposals, editor); addRenameAliasProposal(imp, proposals, editor); addRemoveAliasProposal(imp, proposals, file, editor); addRenameVersionProposals(node, proposals, rootNode, editor); // addConvertToIfElseProposal(doc, proposals, file, statement); // addConvertToThenElseProposal(rootNode, doc, proposals, file, statement); // addInvertIfElseProposal(doc, proposals, file, statement, node, rootNode); // addConvertSwitchToIfProposal(proposals, doc, file, statement); // addConvertIfToSwitchProposal(proposals, doc, file, statement); // addSplitIfStatementProposal(proposals, doc, file, statement); // addJoinIfStatementsProposal(proposals, doc, file, statement); addConvertGetterToFunctionProposal(proposals, editor, statement); addConvertFunctionToGetterProposal(proposals, editor, statement); // addThrowsAnnotationProposal(proposals, statement, rootNode, file, doc); // addRefineFormalMembersProposal(proposals, node, rootNode, false); // addRefineEqualsHashProposal(proposals, node, rootNode); // addConvertToVerbatimProposal(proposals, file, rootNode, node, doc); // addConvertFromVerbatimProposal(proposals, file, rootNode, node, doc); // addConvertToConcatenationProposal(proposals, file, rootNode, node, doc); // addConvertToInterpolationProposal(proposals, file, rootNode, node, doc); // addExpandTypeProposal(editor, statement, file, doc, proposals); RenameProposal.add(proposals, editor); InlineDeclarationProposal.add(proposals, editor); ChangeParametersProposal.add(proposals, editor); ExtractValueProposal.add(proposals, editor, node); ExtractFunctionProposal.add(proposals, editor, node); ExtractParameterProposal.add(proposals, editor, node); CollectParametersProposal.add(proposals, editor); MoveOutProposal.add(proposals, editor, node); MakeReceiverProposal.add(proposals, editor, node); InvertBooleanProposal.add(proposals, editor); MoveToNewUnitProposal.add(proposals, editor); MoveToUnitProposal.add(proposals, editor); } } public void collectWarningProposals( CeylonAnnotation annotation, IQuickAssistInvocationContext context, ProblemLocation location, Collection<ICompletionProposal> proposals) { if (annotation.getSeverity()==IMarker.SEVERITY_WARNING) { Tree.CompilationUnit rootNode = getRootNode(); if (rootNode == null) { return; } Node node = findNode(rootNode, null, location.getOffset(), location.getOffset() + location.getLength()); IEditorInput ei = editor.getEditorInput(); IFile file = EditorUtil.getFile(ei); IDocument doc = context.getSourceViewer() .getDocument(); if (annotation.getError() instanceof UsageWarning) { correctJ2C().addWarningFixes(location, (UsageWarning) annotation.getError(), getRootNode(), node, file.getProject(), proposals, getCurrentCeylonEditor(), file, doc); } proposals.add(new ConfigureWarningsProposal(editor)); } } } //private void addArgumentProposals( //Collection<ICompletionProposal> proposals, //IDocument doc, IFile file, //Tree.StatementOrArgument node) { //if (node instanceof Tree.MethodArgument) { //Tree.MethodArgument ma = // (Tree.MethodArgument) node; //Tree.SpecifierOrInitializerExpression se = // ma.getSpecifierExpression(); //if (se instanceof Tree.LazySpecifierExpression) { // addConvertToBlockProposal(doc, proposals, file, node); //} //Tree.Block b = ma.getBlock(); //if (b!=null) { // addConvertToSpecifierProposal(doc, proposals, file, b); //} //} //if (node instanceof Tree.AttributeArgument) { //Tree.AttributeArgument aa = // (Tree.AttributeArgument) node; //Tree.SpecifierOrInitializerExpression se = // aa.getSpecifierExpression(); //if (se instanceof Tree.LazySpecifierExpression) { // addConvertToBlockProposal(doc, proposals, file, node); //} //Tree.Block b = aa.getBlock(); //if (b!=null) { // addConvertToSpecifierProposal(doc, proposals, file, b); //} //} //if (node instanceof Tree.SpecifiedArgument) { //Tree.SpecifiedArgument sa = // (Tree.SpecifiedArgument) node; //addFillInArgumentNameProposal(proposals, doc, file, sa); //} //} //private void addAnnotationProposals( //Collection<ICompletionProposal> proposals, //IProject project, Tree.Declaration decNode, //IDocument doc, int offset) { //if (decNode!=null) { //try { // Node in = getIdentifyingNode(decNode); // if (in==null || // doc.getLineOfOffset(in.getStartIndex())!= // doc.getLineOfOffset(offset)) { // return; // } //} //catch (BadLocationException e) { // e.printStackTrace(); //} //Declaration d = decNode.getDeclarationModel(); //if (d!=null) { // if (decNode instanceof Tree.AttributeDeclaration) { // addMakeVariableDecProposal(proposals, project, decNode); // } // if ((d.isClassOrInterfaceMember()||d.isToplevel()) && // !d.isShared()) { // addMakeSharedDecProposal(proposals, project, decNode); // } // if (d.isClassOrInterfaceMember() && // !d.isDefault() && !d.isFormal()) { // if (decNode instanceof Tree.AnyClass) { // addMakeDefaultDecProposal(proposals, project, decNode); // } // else if (decNode instanceof Tree.AnyAttribute) { // addMakeDefaultDecProposal(proposals, project, decNode); // } // else if (decNode instanceof Tree.AnyMethod) { // addMakeDefaultDecProposal(proposals, project, decNode); // } // if (decNode instanceof Tree.ClassDefinition) { // addMakeFormalDecProposal(proposals, project, decNode); // } // else if (decNode instanceof Tree.AttributeDeclaration) { // AttributeDeclaration ad = (Tree.AttributeDeclaration) decNode; // if (ad.getSpecifierOrInitializerExpression()==null) { // addMakeFormalDecProposal(proposals, project, decNode); // } // } // else if (decNode instanceof Tree.MethodDeclaration) { // MethodDeclaration md = (Tree.MethodDeclaration) decNode; // if (md.getSpecifierExpression()==null) { // addMakeFormalDecProposal(proposals, project, decNode); // } // } // } //} //} //} //@Deprecated //// see ChangeToQuickFix //private void changeToFunction(IFile file, // Tree.CompilationUnit rootNode, Node node, // Collection<ICompletionProposal> proposals) { //Tree.Declaration dec = // findDeclarationWithBody(rootNode, node); //if (dec instanceof Tree.AnyMethod) { // Tree.Return ret = (Tree.Return) node; // Tree.AnyMethod m = (Tree.AnyMethod) dec; // Tree.Type type = m.getType(); // if (type instanceof Tree.VoidModifier) { // TextFileChange tfc = // new TextFileChange("Change To Function", // file); // Unit unit = rootNode.getUnit(); // Type rt = // ret.getExpression() // .getTypeModel(); // tfc.setEdit(new ReplaceEdit( // type.getStartIndex(), // type.getDistance(), // isTypeUnknown(rt) ? "function" : // rt.asSourceCodeString(unit))); // proposals.add(new CorrectionProposal( // "make function non-'void'", tfc, null)); // } //} //} //@Deprecated //// see ChangeToQuickFix //private void changeToVoid(IFile file, // Tree.CompilationUnit rootNode, Node node, // Collection<ICompletionProposal> proposals) { //Tree.Declaration dec = // findDeclarationWithBody(rootNode, node); //if (dec instanceof Tree.AnyMethod) { // Tree.AnyMethod m = (Tree.AnyMethod) dec; // Tree.Type type = m.getType(); // if (!(type instanceof Tree.VoidModifier)) { // TextFileChange tfc = // new TextFileChange("Change To Void", // file); // tfc.setEdit(new ReplaceEdit( // type.getStartIndex(), // type.getDistance(), // "void")); // proposals.add(new CorrectionProposal( // "make function 'void'", tfc, null)); // } //} //} //@Deprecated //// See AddNamedArgumentQuickFix //private void addNamedArgumentsProposal(IFile file, // Tree.CompilationUnit rootNode, // Collection<ICompletionProposal> proposals, // Node node) { // if (node instanceof Tree.NamedArgumentList) { // TextFileChange tfc = // new TextFileChange("Add Named Arguments", // file); // IDocument doc = EditorUtil.getDocument(tfc); // tfc.setEdit(new MultiTextEdit()); // Tree.NamedArgumentList nal = // (Tree.NamedArgumentList) node; // NamedArgumentList args = // nal.getNamedArgumentList(); // int start = nal.getStartIndex(); // int stop = nal.getEndIndex()-1; // int loc = start+1; // String sep = " "; // List<Tree.NamedArgument> nas = // nal.getNamedArguments(); // if (!nas.isEmpty()) { // Tree.NamedArgument last = // nas.get(nas.size()-1); // loc = last.getEndIndex(); // try { // int firstLine = // doc.getLineOfOffset(start); // int lastLine = // doc.getLineOfOffset(stop); // if (firstLine!=lastLine) { // sep = utilJ2C().indents().getDefaultLineDelimiter(doc) + // utilJ2C().indents().getIndent(last, doc); // } // } // catch (BadLocationException e) { // e.printStackTrace(); // } // } // ParameterList params = args.getParameterList(); // String result = null; // boolean multipleResults = false; // for (Parameter param: params.getParameters()) { // if (!param.isDefaulted() && // !args.getArgumentNames() // .contains(param.getName())) { // multipleResults = result!=null; // result = param.getName(); // tfc.addEdit(new InsertEdit(loc, // sep + param.getName() + // " = nothing;")); // } // } // if (loc==stop) { // tfc.addEdit(new InsertEdit(stop, " ")); // } // String name = multipleResults ? // "Fill in missing named arguments" : // "Fill in missing named argument '" // + result + "'"; // proposals.add(new CorrectionProposal(name, tfc, // new Region(loc, 0))); // } //} //@Deprecated //// see SwitchQuickFix //private void addElseProposal(IFile file, // Tree.CompilationUnit rootNode, // Collection<ICompletionProposal> proposals, // Node node) { // if (node instanceof Tree.SwitchClause) { // Tree.Statement st = // findStatement(rootNode, node); // if (st instanceof Tree.SwitchStatement) { // int offset = st.getEndIndex(); // TextFileChange tfc = // new TextFileChange("Add Else", file); // IDocument doc = getDocument(tfc); // String text = // utilJ2C().indents().getDefaultLineDelimiter(doc) + // utilJ2C().indents().getIndent(node, doc) + // "else {}"; // tfc.setEdit(new InsertEdit(offset, text)); // Region selection = // new Region(offset+text.length()-1, 0); // proposals.add(new CorrectionProposal( // "Add 'else' clause", // tfc, selection)); // } // //TODO: else handle switch *expressions* // } //} //@Deprecated //// see SwitchQuickFix //private void addCasesProposal(IFile file, // Tree.CompilationUnit rootNode, // Collection<ICompletionProposal> proposals, // Node node) { // if (node instanceof Tree.SwitchClause) { // Tree.SwitchClause sc = (Tree.SwitchClause) node; // Tree.Statement st = // findStatement(rootNode, node); // if (st instanceof Tree.SwitchStatement) { // //TODO: handle switch expressions! // Tree.SwitchStatement ss = // (Tree.SwitchStatement) st; // Tree.Expression e = // sc.getSwitched() // .getExpression(); // if (e!=null) { // Type type = e.getTypeModel(); // if (type!=null) { // Tree.SwitchCaseList scl = // ss.getSwitchCaseList(); // for (Tree.CaseClause cc: // scl.getCaseClauses()) { // Tree.CaseItem item = // cc.getCaseItem(); // if (item instanceof Tree.IsCase) { // Tree.IsCase ic = // (Tree.IsCase) item; // Tree.Type tn = ic.getType(); // if (tn!=null) { // Type t = // tn.getTypeModel(); // if (!isTypeUnknown(t)) { // type = type.minus(t); // } // } // } // else if (item instanceof Tree.MatchCase) { // Tree.MatchCase ic = // (Tree.MatchCase) item; // Tree.ExpressionList il = // ic.getExpressionList(); // for (Tree.Expression ex: // il.getExpressions()) { // if (ex!=null) { // Type t = ex.getTypeModel(); // if (t!=null && // !isTypeUnknown(t)) { // type = type.minus(t); // } // } // } // } // } // TextFileChange tfc = // new TextFileChange( // "Add Cases", file); // IDocument doc = getDocument(tfc); // String text = ""; // List<Type> list; // List<Type> cts = type.getCaseTypes(); // if (cts!=null) { // list = cts; // } // else { // list = singletonList(type); // } // for (Type pt: list) { // String is = // pt.getDeclaration() // .isAnonymous() ? // "" : "is "; // Unit unit = rootNode.getUnit(); // text += utilJ2C().indents().getDefaultLineDelimiter(doc) + // utilJ2C().indents().getIndent(node, doc) + // "case (" + // is + // pt.asString(unit) + // ") {}"; // } // int offset = ss.getEndIndex(); // tfc.setEdit(new InsertEdit(offset, text)); // proposals.add(new CorrectionProposal( // "Add missing 'case' clauses", tfc, // new Region(offset+text.length()-1, 0))); // } // } // } // } //} //@Deprecated //// see AddTypeParameterQuickFix //void addTypeParameterProposal(IFile file, // Tree.CompilationUnit rootNode, // Collection<ICompletionProposal> proposals, // Node node) { // Tree.TypeConstraint tcn = (Tree.TypeConstraint) node; // TypeParameter tp = tcn.getDeclarationModel(); // Tree.Declaration decNode = // (Tree.Declaration) // getReferencedNodeInUnit( // tp.getDeclaration(), // rootNode); // Tree.TypeParameterList tpl; // if (decNode instanceof Tree.ClassOrInterface) { // Tree.ClassOrInterface ci = // (Tree.ClassOrInterface) decNode; // tpl = ci.getTypeParameterList(); // } // else if (decNode instanceof Tree.AnyMethod) { // Tree.AnyMethod am = (Tree.AnyMethod) decNode; // tpl = am.getTypeParameterList(); // } // else if (decNode instanceof Tree.TypeAliasDeclaration) { // Tree.TypeAliasDeclaration ad = // (Tree.TypeAliasDeclaration) decNode; // tpl = ad.getTypeParameterList(); // } // else { // return; // } // TextFileChange tfc = // new TextFileChange("Add Type Parameter", file); // InsertEdit edit; // if (tpl==null) { // Tree.Identifier id = decNode.getIdentifier(); // edit = new InsertEdit(id.getEndIndex(), // "<" + tp.getName() + ">"); // } // else { // edit = new InsertEdit(tpl.getEndIndex()-1, // ", " + tp.getName()); // } // tfc.setEdit(edit); // proposals.add(new CorrectionProposal( // "Add '" + tp.getName() + // "' to type parameter list of '" + // decNode.getDeclarationModel().getName() + "'", // tfc, null)); //} //@Deprecated //private static void addOperatorProposals( // Collection<ICompletionProposal> proposals, // IFile file, // Tree.OperatorExpression oe) { // if (oe instanceof Tree.BinaryOperatorExpression) { // Tree.BinaryOperatorExpression boe = // (Tree.BinaryOperatorExpression) oe; // addReverseOperatorProposal(proposals, file, boe); // addInvertOperatorProposal(proposals, file, boe); // addSwapBinaryOperandsProposal(proposals, file, boe); // } //} //@Deprecated //private static void addAnonymousFunctionProposals( // CeylonEditor editor, // Collection<ICompletionProposal> proposals, // IDocument doc, IFile file, // Tree.CompilationUnit rootNode, // final int currentOffset) { // class FindAnonFunctionVisitor extends Visitor { // Tree.FunctionArgument result; // public void visit(Tree.FunctionArgument that) { // if (currentOffset>=that.getStartIndex() && // currentOffset<=that.getEndIndex()) { // result = that; // } // super.visit(that); // } // } // FindAnonFunctionVisitor v = new FindAnonFunctionVisitor(); // v.visit(rootNode); // Tree.FunctionArgument fun = v.result; // if (fun!=null) { // if (fun.getExpression()!=null) { // addConvertToBlockProposal(doc, proposals, file, fun); // } // if (fun.getBlock()!=null) { // addConvertToSpecifierProposal(doc, proposals, file, // fun.getBlock(), true); // } // } //} //@Deprecated //private static void addDeclarationProposals( // CeylonEditor editor, // Collection<ICompletionProposal> proposals, // IDocument doc, IFile file, // Tree.CompilationUnit rootNode, // Tree.Declaration decNode, // int currentOffset) { // // if (decNode==null) return; // // if (decNode.getAnnotationList()!=null) { // Integer endIndex = // decNode.getAnnotationList().getEndIndex(); // if (endIndex!=null && currentOffset<=endIndex) { // return; // } // } // if (decNode instanceof Tree.TypedDeclaration) { // Tree.TypedDeclaration tdn = // (Tree.TypedDeclaration) decNode; // if (tdn.getType()!=null) { // Integer endIndex = tdn.getType().getEndIndex(); // if (endIndex!=null && currentOffset<=endIndex) { // return; // } // } // } // // if (decNode instanceof Tree.AttributeDeclaration) { // Tree.AttributeDeclaration attDecNode = // (Tree.AttributeDeclaration) decNode; // Tree.SpecifierOrInitializerExpression se = // attDecNode.getSpecifierOrInitializerExpression(); // if (se instanceof Tree.LazySpecifierExpression) { // addConvertToBlockProposal(doc, proposals, file, decNode); // } // else { // addConvertToGetterProposal(doc, proposals, file, attDecNode); // } // } // if (decNode instanceof Tree.MethodDeclaration) { // Tree.MethodDeclaration methodDecNode = // (Tree.MethodDeclaration) decNode; // Tree.SpecifierOrInitializerExpression se = // methodDecNode.getSpecifierExpression(); // if (se instanceof Tree.LazySpecifierExpression) { // addConvertToBlockProposal(doc, proposals, file, decNode); // } // } // if (decNode instanceof Tree.AttributeSetterDefinition) { // Tree.AttributeSetterDefinition setterDefNode = // (Tree.AttributeSetterDefinition) decNode; // Tree.SpecifierOrInitializerExpression se = // setterDefNode.getSpecifierExpression(); // if (se instanceof Tree.LazySpecifierExpression) { // addConvertToBlockProposal(doc, proposals, file, decNode); // } // Tree.Block b = setterDefNode.getBlock(); // if (b!=null) { // addConvertToSpecifierProposal(doc, proposals, file, b); // } // } // if (decNode instanceof Tree.AttributeGetterDefinition) { // Tree.AttributeGetterDefinition getterDefNode = // (Tree.AttributeGetterDefinition) decNode; // Tree.Block b = getterDefNode.getBlock(); // if (b!=null) { // addConvertToSpecifierProposal(doc, proposals, file, b); // } // } // if (decNode instanceof Tree.MethodDefinition) { // Tree.MethodDefinition methodDefNode = // (Tree.MethodDefinition) decNode; // Tree.Block b = methodDefNode.getBlock(); // if (b!=null) { // addConvertToSpecifierProposal(doc, proposals, file, b); // } // } // //} //@Deprecated //private void addCreationProposals( // Tree.CompilationUnit cu, // final Node node, // ProblemLocation problem, // Collection<ICompletionProposal> proposals, // IProject project, // IFile file) { // if (node instanceof Tree.MemberOrTypeExpression) { // addCreateProposals(cu, node, proposals, project, file); // } // else if (node instanceof Tree.SimpleType) { // class FindExtendedTypeExpressionVisitor extends Visitor { // Tree.InvocationExpression invocationExpression; // @Override // public void visit(Tree.ExtendedType that) { // super.visit(that); // if (that.getType()==node) { // invocationExpression = // that.getInvocationExpression(); // } // } // } // FindExtendedTypeExpressionVisitor v = // new FindExtendedTypeExpressionVisitor(); // v.visit(cu); // if (v.invocationExpression!=null) { // addCreateProposals(cu, // v.invocationExpression.getPrimary(), // proposals, project, file); // } // } // //TODO: should we add this stuff back in?? // /*else if (node instanceof Tree.BaseType) { // Tree.BaseType bt = (Tree.BaseType) node; // String brokenName = bt.getIdentifier().getText(); // String idef = "interface " + brokenName + " {}"; // String idesc = "interface '" + brokenName + "'"; // String cdef = "class " + brokenName + "() {}"; // String cdesc = "class '" + brokenName + "()'"; // //addCreateLocalProposals(proposals, project, idef, idesc, INTERFACE, cu, bt); // addCreateLocalProposals(proposals, project, cdef, cdesc, CLASS, cu, bt, null, null); // addCreateToplevelProposals(proposals, project, idef, idesc, INTERFACE, cu, bt, null, null); // addCreateToplevelProposals(proposals, project, cdef, cdesc, CLASS, cu, bt, null, null); // CreateInNewUnitProposal.addCreateToplevelProposal(proposals, idef, idesc, // INTERFACE, file, brokenName, null, null); // CreateInNewUnitProposal.addCreateToplevelProposal(proposals, cdef, cdesc, // CLASS, file, brokenName, null, null); // // }*/ // if (node instanceof Tree.BaseType) { // Tree.BaseType bt = (Tree.BaseType) node; // Tree.Identifier id = bt.getIdentifier(); // if (id!=null) { // String brokenName = id.getText(); // addCreateTypeParameterProposal(proposals, // project, cu, bt, brokenName); // } // } //}