| /******************************************************************************* |
| * Copyright (c) 2004, 2017 Tasktop Technologies and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Tasktop Technologies - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.dltk.internal.mylyn.editor; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.dltk.core.IModelElement; |
| import org.eclipse.dltk.internal.mylyn.DLTKUiBridgePlugin; |
| import org.eclipse.dltk.ui.text.completion.AbstractScriptCompletionProposal; |
| import org.eclipse.dltk.ui.text.completion.IScriptCompletionProposalComputer; |
| import org.eclipse.dltk.ui.text.completion.ScriptCompletionProposal; |
| import org.eclipse.jface.text.contentassist.ICompletionProposal; |
| import org.eclipse.mylyn.commons.core.StatusHandler; |
| import org.eclipse.mylyn.commons.ui.CommonImages; |
| import org.eclipse.mylyn.context.core.ContextCore; |
| import org.eclipse.mylyn.context.core.IInteractionElement; |
| |
| /** |
| * TODO: parametrize relevance levels (requires JDT changes, bug 119063) |
| * |
| * @author Mik Kersten |
| */ |
| public class FocusedDLTKProposalProcessor { |
| |
| /** |
| * Range above which elements are part of the context. |
| */ |
| private static final int THRESHOLD_INTEREST = 10000; |
| |
| /** |
| * Range for implicitly interesting element, such as method parameters. |
| */ |
| private static final int THRESHOLD_IMPLICIT_INTEREST = THRESHOLD_INTEREST * 2; |
| |
| /** |
| * Threshold for determining which JDT proposals should be implicitly interesting. |
| */ |
| private static final int RELEVANCE_IMPLICIT_INTEREST_JAVA = 600; |
| |
| /** |
| * Threshold for implicit interest of IModelElement proposals. |
| */ |
| private static final int RELEVANCE_IMPLICIT_INTEREST_MISC = 300; |
| |
| private static final String IDENTIFIER_THIS = "this"; //$NON-NLS-1$ |
| |
| public static final String LABEL_SEPARATOR = " -------------------------------------------- "; //$NON-NLS-1$ |
| |
| public static final FocusedProposalSeparator PROPOSAL_SEPARATOR = new FocusedProposalSeparator(); |
| |
| private final List<IScriptCompletionProposalComputer> monitoredProposalComputers = new ArrayList<>(); |
| |
| private final List<IScriptCompletionProposalComputer> alreadyComputedProposals = new ArrayList<>(); |
| |
| private final List<IScriptCompletionProposalComputer> alreadyContainSeparator = new ArrayList<>(); |
| |
| private final List<IScriptCompletionProposalComputer> containsSingleInterestingProposal = new ArrayList<>(); |
| |
| private static FocusedDLTKProposalProcessor INSTANCE = new FocusedDLTKProposalProcessor(); |
| |
| private FocusedDLTKProposalProcessor() { |
| } |
| |
| public static FocusedDLTKProposalProcessor getDefault() { |
| return INSTANCE; |
| } |
| |
| public void addMonitoredComputer(IScriptCompletionProposalComputer proposalComputer) { |
| monitoredProposalComputers.add(proposalComputer); |
| } |
| |
| public List<ICompletionProposal> projectInterestModel(IScriptCompletionProposalComputer proposalComputer, |
| List<ICompletionProposal> proposals) { |
| try { |
| if (!ContextCore.getContextManager().isContextActive()) { |
| return proposals; |
| } else { |
| boolean hasInterestingProposals = false; |
| for (Object object : proposals) { |
| if (object instanceof AbstractScriptCompletionProposal) { |
| boolean foundInteresting = boostRelevanceWithInterest( |
| (AbstractScriptCompletionProposal) object); |
| if (!hasInterestingProposals && foundInteresting) { |
| hasInterestingProposals = true; |
| } |
| } |
| } |
| |
| // NOTE: this annoying state needs to be maintainted to ensure |
| // the |
| // separator is added only once, and not added for single |
| // proposals |
| if (containsSingleInterestingProposal.size() > 0 && proposals.size() > 0) { |
| proposals.add(FocusedDLTKProposalProcessor.PROPOSAL_SEPARATOR); |
| } else if (hasInterestingProposals && alreadyContainSeparator.isEmpty()) { |
| if (proposals.size() == 1) { |
| containsSingleInterestingProposal.add(proposalComputer); |
| } else { |
| proposals.add(FocusedDLTKProposalProcessor.PROPOSAL_SEPARATOR); |
| alreadyContainSeparator.add(proposalComputer); |
| } |
| } |
| |
| alreadyComputedProposals.add(proposalComputer); |
| if (alreadyComputedProposals.size() == monitoredProposalComputers.size()) { |
| alreadyComputedProposals.clear(); |
| alreadyContainSeparator.clear(); |
| containsSingleInterestingProposal.clear(); |
| } |
| |
| return proposals; |
| } |
| } catch (Throwable t) { |
| StatusHandler.log(new Status(IStatus.ERROR, DLTKUiBridgePlugin.ID_PLUGIN, |
| "Failed to project interest onto propsals", t)); //$NON-NLS-1$ |
| return proposals; |
| } |
| } |
| |
| private boolean boostRelevanceWithInterest(AbstractScriptCompletionProposal proposal) { |
| boolean hasInteresting = false; |
| IModelElement javaElement = proposal.getModelElement(); |
| if (javaElement != null) { |
| IInteractionElement interactionElement = ContextCore.getContextManager() |
| .getElement(javaElement.getHandleIdentifier()); |
| float interest = interactionElement.getInterest().getValue(); |
| if (interest > ContextCore.getCommonContextScaling().getInteresting()) { |
| // TODO: losing precision here, only going to one decimal place |
| proposal.setRelevance(THRESHOLD_INTEREST + (int) (interest * 10)); |
| hasInteresting = true; |
| } else if (proposal.getRelevance() > RELEVANCE_IMPLICIT_INTEREST_JAVA) { |
| proposal.setRelevance(THRESHOLD_IMPLICIT_INTEREST + proposal.getRelevance()); |
| } |
| } else if (isImplicitlyInteresting(proposal)) { |
| proposal.setRelevance(THRESHOLD_IMPLICIT_INTEREST + proposal.getRelevance()); |
| hasInteresting = true; |
| } |
| return hasInteresting; |
| } |
| |
| public boolean isImplicitlyInteresting(AbstractScriptCompletionProposal proposal) { |
| return proposal.getRelevance() > RELEVANCE_IMPLICIT_INTEREST_MISC |
| && !IDENTIFIER_THIS.equals(proposal.getDisplayString()); |
| } |
| |
| static class FocusedProposalSeparator extends ScriptCompletionProposal { |
| public FocusedProposalSeparator() { |
| super("", 0, 0, CommonImages.getImage(CommonImages.SEPARATOR_LIST), LABEL_SEPARATOR, //$NON-NLS-1$ |
| FocusedDLTKProposalProcessor.THRESHOLD_INTEREST); |
| } |
| |
| @Override |
| protected boolean isSmartTrigger(char trigger) { |
| return false; |
| } |
| } |
| } |