/** * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the Eclipse Public License (EPL). * Please see the license.txt included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ /* * Created on Sep 23, 2004 * * @author Fabio Zadrozny */ package org.python.pydev.editor.correctionassist; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; 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.source.Annotation; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.ui.internal.texteditor.spelling.NoCompletionsProposal; import org.eclipse.ui.texteditor.spelling.SpellingAnnotation; import org.eclipse.ui.texteditor.spelling.SpellingCorrectionProcessor; import org.eclipse.ui.texteditor.spelling.SpellingProblem; import org.python.pydev.core.ExtensionHelper; import org.python.pydev.core.IPythonNature; import org.python.pydev.core.MisconfigurationException; import org.python.pydev.core.bundle.ImageCache; import org.python.pydev.core.docutils.PySelection; import org.python.pydev.core.log.Log; import org.python.pydev.editor.IPySyntaxHighlightingAndCodeCompletionEditor; import org.python.pydev.editor.PyEdit; import org.python.pydev.editor.actions.PyAction; import org.python.pydev.editor.codecompletion.IPyCodeCompletion; import org.python.pydev.editor.correctionassist.docstrings.AssistDocString; import org.python.pydev.editor.correctionassist.heuristics.AssistAssign; import org.python.pydev.editor.correctionassist.heuristics.AssistImport; import org.python.pydev.editor.correctionassist.heuristics.AssistPercentToFormat; import org.python.pydev.editor.correctionassist.heuristics.AssistSurroundWith; import org.python.pydev.editor.correctionassist.heuristics.IAssistProps; import org.python.pydev.plugin.PydevPlugin; /** * This class should be used to give context help * * Help depending on context (Ctrl+1): * * class A: pass * * class C: * * def __init__(self, param): * self.newMethod()<- create new method on class C (with params if needed) * <- assign result to new local variable * <- assign result to new field * * a = A() * a.newMethod() <- create new method on class A * <- assign result to new local variable * <- assign result to new field * * param.b() <- don't show anything. * * self.a1 = A() * self.a1.newMethod() <- create new method on class A (difficult part is discovering class) * <- assign result to new local variable * <- assign result to new field * * def m(self): * self.a1.newMethod() <- create new method on class A * <- assign result to new local variable * <- assign result to new field * * import compiler <- move import to global context * NewClass() <- Create class NewClass (Depends on new class wizard) * * a() <-- make this a new method in this class * * @author Fabio Zadrozny */ public class PythonCorrectionProcessor implements IQuickAssistProcessor { private IPySyntaxHighlightingAndCodeCompletionEditor edit; /** * Contains additional assists (used from the jython scripting: pyedit_assign_params_to_attributes.py to add new assists) */ private static Map<String, IAssistProps> additionalAssists = new HashMap<String, IAssistProps>(); /** * Checks if some assist with the given id is already added. * * @param id the id of the assist * @return true if it's already added and false otherwise */ public static boolean hasAdditionalAssist(String id) { synchronized (additionalAssists) { return additionalAssists.containsKey(id); } } /** * Adds some additional assist to Ctrl+1 (used from the scripting engine) * * @param id the id of the assist * @param assist the assist to be added */ public static void addAdditionalAssist(String id, IAssistProps assist) { synchronized (additionalAssists) { additionalAssists.put(id, assist); } } /** * Removes some additional assist from Ctrl+1 * * @param id id of the assist to be removed */ public static void removeAdditionalAssist(String id) { synchronized (additionalAssists) { additionalAssists.remove(id); } } /** * @param edit */ public PythonCorrectionProcessor(IPySyntaxHighlightingAndCodeCompletionEditor edit) { this.edit = edit; } public boolean canAssist(IQuickAssistInvocationContext invocationContext) { return true; } public boolean canFix(Annotation annotation) { return false; } public ICompletionProposal[] computeQuickAssistProposals(IQuickAssistInvocationContext invocationContext) { int offset = invocationContext.getOffset(); PySelection base = edit.createPySelection(); if (!(this.edit instanceof PyEdit) || base == null) { return new ICompletionProposal[0]; } PyEdit editor = (PyEdit) this.edit; List<ICompletionProposal> results = new ArrayList<ICompletionProposal>(); String sel = PyAction.getLineWithoutComments(base); List<IAssistProps> assists = new ArrayList<IAssistProps>(); synchronized (PythonCorrectionProcessor.additionalAssists) { for (IAssistProps prop : additionalAssists.values()) { assists.add(prop); } } assists.add(new AssistSurroundWith()); assists.add(new AssistImport()); assists.add(new AssistDocString()); assists.add(new AssistAssign()); assists.add(new AssistPercentToFormat()); assists.addAll(ExtensionHelper.getParticipants(ExtensionHelper.PYDEV_CTRL_1)); ImageCache imageCache = PydevPlugin.getImageCache(); File editorFile = edit.getEditorFile(); IPythonNature pythonNature = null; try { pythonNature = edit.getPythonNature(); } catch (MisconfigurationException e1) { Log.log(e1); } for (IAssistProps assist : assists) { //Always create a new for each assist, as any given assist may change it. PySelection ps = new PySelection(base); try { if (assist.isValid(ps, sel, editor, offset)) { try { results.addAll(assist.getProps(ps, imageCache, editorFile, pythonNature, editor, offset)); } catch (Exception e) { Log.log(e); } } } catch (Exception e) { Log.log(e); } } Collections.sort(results, IPyCodeCompletion.PROPOSAL_COMPARATOR); try { //handling spelling... (we only want to show spelling fixes if a spell problem annotation is found at the current location). //we'll only show some spelling proposal if there's some spelling problem (so, we don't have to check the preferences at this place, //as no annotations on spelling will be here if the spelling is not enabled). ICompletionProposal[] spellProps = null; IAnnotationModel annotationModel = editor.getPySourceViewer().getAnnotationModel(); Iterator<Object> it = annotationModel.getAnnotationIterator(); while (it.hasNext()) { Object annotation = it.next(); if (annotation instanceof SpellingAnnotation) { SpellingAnnotation spellingAnnotation = (SpellingAnnotation) annotation; SpellingProblem spellingProblem = spellingAnnotation.getSpellingProblem(); int problemOffset = spellingProblem.getOffset(); int problemLen = spellingProblem.getLength(); if (problemOffset <= offset && problemOffset + problemLen >= offset) { SpellingCorrectionProcessor spellingCorrectionProcessor = new SpellingCorrectionProcessor(); spellProps = spellingCorrectionProcessor.computeQuickAssistProposals(invocationContext); break; } } } if (spellProps == null || (spellProps.length == 1 && spellProps[0] instanceof NoCompletionsProposal)) { //no proposals from the spelling return (ICompletionProposal[]) results.toArray(new ICompletionProposal[results.size()]); } //ok, add the spell problems and return... ICompletionProposal[] ret = (ICompletionProposal[]) results.toArray(new ICompletionProposal[results.size() + spellProps.length]); System.arraycopy(spellProps, 0, ret, results.size(), spellProps.length); return ret; } catch (Throwable e) { if (e instanceof ClassNotFoundException || e instanceof LinkageError || e instanceof NoSuchMethodException || e instanceof NoSuchMethodError || e instanceof NoClassDefFoundError) { //Eclipse 3.2 support return (ICompletionProposal[]) results.toArray(new ICompletionProposal[results.size()]); } throw new RuntimeException(e); } } public String getErrorMessage() { return null; } }