/** * Copyright (c) 2005-2013 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. */ package org.python.pydev.editor.codecompletion; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IProject; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.python.pydev.core.IModulesManager; import org.python.pydev.editor.codecompletion.ProposalsComparator.CompareContext; import org.python.pydev.shared_ui.proposals.PyCompletionProposal; public class PyCodeCompletionUtils { /** * Filters the python completions so that only the completions we care about are shown (given the qualifier) * @param pythonAndTemplateProposals the completions to sort / filter * @param qualifier the qualifier we care about * @param onlyForCalltips if we should filter having in mind that we're going to show it for a calltip * @return the completions to show to the user */ public static ICompletionProposal[] onlyValid(List pythonAndTemplateProposals, String qualifier, boolean onlyForCalltips, boolean useSubstringMatchInCodeCompletion, IProject project) { //FOURTH: Now, we have all the proposals, only thing is deciding which ones are valid (depending on //qualifier) and sorting them correctly. final Map<String, List<ICompletionProposal>> returnProposals = new HashMap<String, List<ICompletionProposal>>(); int len = pythonAndTemplateProposals.size(); IFilter nameFilter = getNameFilter(useSubstringMatchInCodeCompletion, qualifier); for (int i = 0; i < len; i++) { Object o = pythonAndTemplateProposals.get(i); if (o instanceof ICompletionProposal) { ICompletionProposal proposal = (ICompletionProposal) o; String displayString; if (proposal instanceof IPyCompletionProposal2) { IPyCompletionProposal2 pyCompletionProposal = (IPyCompletionProposal2) proposal; displayString = pyCompletionProposal.getInternalDisplayStringRepresentation(); } else { displayString = proposal.getDisplayString(); } if (onlyForCalltips) { if (displayString.equals(qualifier)) { addProposal(returnProposals, proposal, displayString); } else if (displayString.length() > qualifier.length() && displayString.startsWith(qualifier)) { if (displayString.charAt(qualifier.length()) == '(') { addProposal(returnProposals, proposal, displayString); } } } else if (nameFilter.acceptName(displayString)) { List<ICompletionProposal> existing = returnProposals.get(displayString); if (existing != null) { //a proposal with the same string is already there... boolean addIt = true; if (proposal instanceof PyCompletionProposal) { PyCompletionProposal propP = (PyCompletionProposal) proposal; OUT: for (Iterator<ICompletionProposal> it = existing.iterator(); it.hasNext();) { ICompletionProposal curr = it.next(); int overrideBehavior = propP.getOverrideBehavior(curr); switch (overrideBehavior) { case PyCompletionProposal.BEHAVIOR_COEXISTS: //just go on (it will be added later) break; case PyCompletionProposal.BEHAVIOR_OVERRIDES: it.remove(); break; case PyCompletionProposal.BEHAVIOR_IS_OVERRIDEN: addIt = false; break OUT; } } } if (addIt) { existing.add(proposal); } } else { //it's null, so, 1st insertion... List<ICompletionProposal> lst = new ArrayList<ICompletionProposal>(); lst.add(proposal); returnProposals.put(displayString, lst); } } } else { throw new RuntimeException("Error: expected instanceof ICompletionProposal and received: " + o.getClass().getName()); } } // and fill with list elements Collection<List<ICompletionProposal>> values = returnProposals.values(); ArrayList<ICompletionProposal> tproposals = new ArrayList<ICompletionProposal>(); for (List<ICompletionProposal> value : values) { tproposals.addAll(value); } ICompletionProposal[] proposals = tproposals.toArray(new ICompletionProposal[returnProposals.size()]); return proposals; } public static void sort(ICompletionProposal[] proposals, String qualifier, IProject project) { Arrays.sort(proposals, new ProposalsComparator(qualifier, new CompareContext(project))); } private static void addProposal(Map<String, List<ICompletionProposal>> returnProposals, ICompletionProposal proposal, String displayString) { List<ICompletionProposal> lst = returnProposals.get(displayString); if (lst == null) { lst = new ArrayList<ICompletionProposal>(); returnProposals.put(displayString, lst); } lst.add(proposal); } public static Set<String> getModulesNamesToFilterOn(boolean useSubstringMatchInCodeCompletion, IModulesManager modulesManager, String qual) { if (useSubstringMatchInCodeCompletion) { return modulesManager.getAllModuleNames(false, ""); } else { return modulesManager.getAllModuleNames(false, qual.toLowerCase()); } } public static interface IFilter { boolean acceptName(String name); } public static IFilter getNameFilter(boolean useSubstringMatchInCodeCompletion, String qual) { final String lowerQual = qual.toLowerCase(); if (useSubstringMatchInCodeCompletion) { return new IFilter() { @Override public boolean acceptName(String name) { //START: Get the contents only to the first parens or space for the comparisons. { int iSplit1 = name.indexOf('(', 0); int iSpace1 = name.indexOf(' ', 0); if (iSpace1 >= 0 && iSpace1 < iSplit1) { iSplit1 = iSpace1; } if (iSplit1 >= 0) { name = name.substring(0, iSplit1); } } //END: Get the contents only to the first parens or space for the comparisons. return name.toLowerCase().contains(lowerQual); } }; } else { return new IFilter() { @Override public boolean acceptName(String name) { return name.toLowerCase().startsWith(lowerQual); } }; } } // API optimized for a single match (to avoid creating temporary objects) -- prefer getNameFilter() for multiple matches on the same qualifier. public static boolean acceptName(boolean useSubstringMatchInCodeCompletion, String name, String qualifier) { if (useSubstringMatchInCodeCompletion) { //START: Get the contents only to the first parens or space for the comparisons. { int iSplit1 = name.indexOf('(', 0); int iSpace1 = name.indexOf(' ', 0); if (iSplit1 == -1 || (iSpace1 >= 0 && iSpace1 < iSplit1)) { iSplit1 = iSpace1; } if (iSplit1 >= 0) { name = name.substring(0, iSplit1); } } //END: Get the contents only to the first parens or space for the comparisons. return name.toLowerCase().contains(qualifier.toLowerCase()); } else { return name.toLowerCase().startsWith(qualifier.toLowerCase()); } } }