| author | mclay | 2009-03-08 16:42:18 (EDT) |
|---|---|---|
| committer | sefftinge | 2009-03-08 16:42:18 (EDT) |
| commit | 99fad5578572a3fa87098ba6c2b22a7332b7ff18 (patch) (side-by-side diff) | |
| tree | 1acf3681e313ba73e7fbe13a23480791d613f278 | |
| parent | fd900f5f2c54fcdc3db22ebcc52814453646358b (diff) | |
| download | org.eclipse.xtext-99fad5578572a3fa87098ba6c2b22a7332b7ff18.zip org.eclipse.xtext-99fad5578572a3fa87098ba6c2b22a7332b7ff18.tar.gz org.eclipse.xtext-99fad5578572a3fa87098ba6c2b22a7332b7ff18.tar.bz2 | |
[Code Completion] Compute proposals for two different contexts https://bugs.eclipse.org/bugs/show_bug.cgi?id=263770
11 files changed, 248 insertions, 461 deletions
diff --git a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/IProposalProvider.java b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/IProposalProvider.java index 05ba96d..1b7691f 100644 --- a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/IProposalProvider.java +++ b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/IProposalProvider.java @@ -1,3 +1,11 @@ +/******************************************************************************* + * Copyright (c) 2008 Michael Clay 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 + * + *******************************************************************************/ package org.eclipse.xtext.ui.common.editor.contentassist; import java.util.List; @@ -10,20 +18,16 @@ import org.eclipse.xtext.Keyword; import org.eclipse.xtext.RuleCall; /** - * * @author Sven Efftinge - Initial contribution and API * @author Michael Clay */ public interface IProposalProvider { - /** * Is invoked by the framework if (with respect to the grammar) it is * possible that the keyword passed as first parameter can occur next up. * - * @param keyword - * the <code>Keyword</code> to be completed - * @param contentAssistContext - * the current context of the content assist + * @param keyword the <code>Keyword</code> to be completed + * @param contentAssistContext the current context of the content assist * @return a list of matching {@link ICompletionProposal} */ List<? extends ICompletionProposal> completeKeyword(Keyword keyword, IContentAssistContext contentAssistContext); @@ -32,10 +36,8 @@ public interface IProposalProvider { * Is invoked by the framework if (with respect to the grammar) it is * possible that the rule call passed as first parameter can occur next up. * - * @param ruleCall - * the <code>RuleCall</code> to be completed - * @param contentAssistContext - * the current context of the content assist + * @param ruleCall the <code>RuleCall</code> to be completed + * @param contentAssistContext the current context of the content assist * @return a list of matching {@link ICompletionProposal} */ List<? extends ICompletionProposal> completeRuleCall(RuleCall ruleCall, IContentAssistContext contentAssistContext); @@ -44,10 +46,8 @@ public interface IProposalProvider { * Is invoked by the framework if (with respect to the grammar) it is * possible that the assignment passed as first parameter can occur next up. * - * @param assignment - * the <code>Assignment</code> to be completed - * @param contentAssistContext - * the current context of the content assist + * @param assignment the <code>Assignment</code> to be completed + * @param contentAssistContext the current context of the content assist * @return a list of matching {@link ICompletionProposal} */ List<? extends ICompletionProposal> completeAssignment(Assignment assignment, IContentAssistContext contentAssistContext); @@ -56,10 +56,8 @@ public interface IProposalProvider { * Returns the context type that can handle template insertion at the given * region in the viewer's document. * - * @param keyword - * the <code>Keyword</code> to be completed - * @param contentAssistContext - * the current context of the content assist + * @param keyword the <code>Keyword</code> to be completed + * @param contentAssistContext the current context of the content assist * @return the context type that can handle template expansion for the given * location, or <code>null</code> if none exists */ @@ -69,37 +67,40 @@ public interface IProposalProvider { * Returns the context type that can handle template insertion at the given * region in the viewer's document. * - * @param ruleCall - * the <code>RuleCall</code> - * @param contentAssistContext - * the current context of the content assist + * @param ruleCall the <code>RuleCall</code> + * @param contentAssistContext the current context of the content assist * @return the context type that can handle template expansion for the given * location, or <code>null</code> if none exists */ TemplateContextType getTemplateContextType(RuleCall ruleCall, IContentAssistContext contentAssistContext); - + /** * Returns the templates valid for the context type specified by * <code>contextTypeId</code>. * - * @param contextTypeId - * the context type id + * @param contextTypeId the context type id * @return the templates valid for this context type id */ Template[] getTemplates(String contextTypeId); - + /** - * Used to filter and sort a list of completion proposals. This method is - * invoked by the framework after all possible completions have been - * collected. + * Used to filter the given list of completion proposals. + * <p/> + * This method is after all possible completions and templates have been collected. * - * @param completionProposalList - * matching {@link ICompletionProposal} to sort and filter - * @param contentAssistContext - * the current context of the content assist - * @return the sorted and filtered <code>ICompletionProposal</code> list. + * @param completionProposalList the collected list of {@link ICompletionProposal} to filter + * @param contentAssistContext the current context of the content assist + * @return the filtered <code>ICompletionProposal</code> list. */ - List<? extends ICompletionProposal> sortAndFilter(List<? extends ICompletionProposal> completionProposalList, - IContentAssistContext contentAssistContext); - + List<? extends ICompletionProposal> filter(List<ICompletionProposal> completionProposalList,IContentAssistContext contentAssistContext); + + /** + * Used to sort the given list of completion proposals. + * <p/> + * This method is after all possible completions and templates have been collected and filtered. + * + * @param completionProposalList the collected list of {@link ICompletionProposal} to sort + * @return the sorted <code>ICompletionProposal</code> list. + */ + List<? extends ICompletionProposal> sort(List<ICompletionProposal> completionProposalList); } diff --git a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/AbstractJavaProposalProvider.java b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/AbstractJavaProposalProvider.java index c28854d..5b01ea7 100644 --- a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/AbstractJavaProposalProvider.java +++ b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/AbstractJavaProposalProvider.java @@ -45,16 +45,13 @@ import com.google.inject.Inject; * @author Jan Köhnlein - Initial contribution and API */ public abstract class AbstractJavaProposalProvider implements IProposalProvider { - // constants // protected static final String LEXER_RULE_ID = "ID"; protected static final String LEXER_RULE_INT = "INT"; protected static final String LEXER_RULE_STRING = "STRING"; // logger available to subclasses - protected final static Logger logger = Logger - .getLogger(IProposalProvider.class); - + protected final static Logger logger = Logger.getLogger(IProposalProvider.class); @Inject protected IScopeProvider scopeProvider; @@ -63,7 +60,6 @@ public abstract class AbstractJavaProposalProvider implements IProposalProvider protected AbstractJavaProposalProvider() { invoker = new JavaReflectiveMethodInvoker(this); } - /** * @see org.eclipse.xtext.ui.common.editor.contentassist.IProposalProvider#completeKeyword(Keyword, * IContentAssistContext) @@ -71,10 +67,8 @@ public abstract class AbstractJavaProposalProvider implements IProposalProvider public List<? extends ICompletionProposal> completeKeyword(Keyword keyword, IContentAssistContext contentAssistContext) { if (logger.isDebugEnabled()) { - logger.debug("completeKeyword '" + keyword.getValue() - + "' for model '" + contentAssistContext.getModel() - + "' and prefix '" - + contentAssistContext.getMatchString().trim() + "'"); + logger.debug("completeKeyword '" + keyword.getValue()+ "' for model '" + contentAssistContext.getModel() + + "' and prefix '"+ contentAssistContext.getMatchString().trim() + "'"); } return Collections.singletonList(createCompletionProposal(keyword, keyword.getValue(), contentAssistContext)); @@ -87,41 +81,21 @@ public abstract class AbstractJavaProposalProvider implements IProposalProvider public List<? extends ICompletionProposal> completeRuleCall( RuleCall ruleCall, IContentAssistContext contentAssistContext) { if (logger.isDebugEnabled()) { - logger - .debug("completeRuleCall '" - + ruleCall.getRule().getName() - + "' cardinality '" - + ruleCall.getCardinality() - + "' for model '" - + contentAssistContext.getModel() - + "' and prefix '" - + contentAssistContext.getMatchString().trim() - .trim() + "'"); + logger.debug("completeRuleCall '"+ ruleCall.getRule().getName()+ "' cardinality '" + + ruleCall.getCardinality()+ "' for model '"+ contentAssistContext.getModel() + + "' and prefix '"+ contentAssistContext.getMatchString().trim().trim() + "'"); } - AbstractRule calledRule = ruleCall.getRule(); - if (calledRule instanceof TerminalRule) { - return completeTerminalRuleRuleCall((TerminalRule) calledRule, ruleCall, - contentAssistContext); + return completeTerminalRuleRuleCall((TerminalRule) calledRule, ruleCall,contentAssistContext); } else if (calledRule.getType() != null) { - TypeRef typeRef = calledRule.getType(); - - return invokeMethod( - "complete" - + Strings.toFirstUpper(typeRef.getMetamodel() - .getAlias()) + "_" + return invokeMethod("complete"+ Strings.toFirstUpper(typeRef.getMetamodel().getAlias()) + "_" + Strings.toFirstUpper(typeRef.getClassifier().getName()), - Arrays - .<Class<?>> asList( - RuleCall.class, - contentAssistContext.getModel() == null ? EObject.class - : contentAssistContext.getModel() - .getClass(), - IContentAssistContext.class), Arrays - .asList(ruleCall, contentAssistContext.getModel(), - contentAssistContext)); + Arrays.<Class<?>> asList(RuleCall.class,contentAssistContext.getModel() == null ? + EObject.class : contentAssistContext.getModel().getClass(), + IContentAssistContext.class), Arrays.asList(ruleCall, + contentAssistContext.getModel(),contentAssistContext)); } return Collections.emptyList(); } @@ -134,12 +108,9 @@ public abstract class AbstractJavaProposalProvider implements IProposalProvider Assignment assignment, IContentAssistContext contentAssistContext) { ParserRule parserRule = GrammarUtil.containingParserRule(assignment); // TODO : Better call completeRuleCall ? - return invokeMethod("complete" - + Strings.toFirstUpper(parserRule.getName()) + "_" - + Strings.toFirstUpper(assignment.getFeature()), Arrays - .<Class<?>> asList(Assignment.class, - IContentAssistContext.class), Arrays.asList(assignment, - contentAssistContext)); + return invokeMethod("complete"+ Strings.toFirstUpper(parserRule.getName()) + "_"+ + Strings.toFirstUpper(assignment.getFeature()), Arrays.<Class<?>> asList(Assignment.class, + IContentAssistContext.class), Arrays.asList(assignment,contentAssistContext)); } /** @@ -149,20 +120,13 @@ public abstract class AbstractJavaProposalProvider implements IProposalProvider protected ICompletionProposal createCompletionProposal( AbstractElement abstractElement, String displayString, IContentAssistContext contentAssistContext) { - return new XtextCompletionProposal(abstractElement, displayString, - contentAssistContext); + return new XtextCompletionProposal(abstractElement, displayString,contentAssistContext); } @SuppressWarnings("unchecked") - protected List<? extends ICompletionProposal> invokeMethod( - String methodName, List<Class<?>> parameterTypes, + protected List<? extends ICompletionProposal> invokeMethod(String methodName, List<Class<?>> parameterTypes, List<?> parameterValues) { - try { - return (List<? extends ICompletionProposal>) invoker.invoke( - methodName, parameterTypes, parameterValues); - } catch (NullPointerException exc) { - return null; - } + return (List<? extends ICompletionProposal>) invoker.invoke(methodName, parameterTypes, parameterValues); } /** @@ -231,23 +195,21 @@ public abstract class AbstractJavaProposalProvider implements IProposalProvider */ protected abstract String getDefaultImageFilePath(); - /** - * Concrete subclasses can override this for custom sort and filter - * behavior. Called right after all completion proposals have been - * collected. - * - * The default behavior of this implementation is to filter duplicates and - * to trim matching <code>ICompletionProposal#displayString</code> with - * matching prefix values. - * - * @see #sortAndFilter(List, EObject, String, IDocument, int, AbstractNode, - * LeafNode) + /* + * (non-Javadoc) + * @see org.eclipse.xtext.ui.common.editor.contentassist.IProposalProvider#filter(java.util.List, org.eclipse.xtext.ui.common.editor.contentassist.IContentAssistContext) */ - public List<? extends ICompletionProposal> sortAndFilter( - List<? extends ICompletionProposal> completionProposalList, + public List<? extends ICompletionProposal> filter(List<ICompletionProposal> completionProposalList, IContentAssistContext contentAssistContext) { - return ProposalFilterSorterUtil.sortAndFilter(completionProposalList, - contentAssistContext); + return ProposalFilterSorterUtil.filter(completionProposalList,contentAssistContext); + } + + /* + * (non-Javadoc) + * @see org.eclipse.xtext.ui.common.editor.contentassist.IProposalProvider#sort(java.util.List) + */ + public List<? extends ICompletionProposal> sort(List<ICompletionProposal> completionProposalList) { + return ProposalFilterSorterUtil.sort(completionProposalList); } /** @@ -259,29 +221,21 @@ public abstract class AbstractJavaProposalProvider implements IProposalProvider * assignment */ protected List<? extends ICompletionProposal> lookupCrossReference( - CrossReference crossReference, - IContentAssistContext contentAssistContext) { - + CrossReference crossReference,IContentAssistContext contentAssistContext) { List<ICompletionProposal> completionProposalList = new ArrayList<ICompletionProposal>(); - if (scopeProvider != null) { ParserRule containingParserRule = GrammarUtil .containingParserRule(crossReference); if (!GrammarUtil.isDatatypeRule(containingParserRule)) { - final EClass eClass = (EClass) containingParserRule.getType().getClassifier(); - final EReference ref = GrammarUtil.getReference(crossReference, - eClass); - final String trimmedPrefix = contentAssistContext - .getMatchString().trim(); - final Iterable<IScopedElement> candidates = scopeProvider - .getScope(contentAssistContext.getModel(), ref) + EClass eClass = (EClass) containingParserRule.getType().getClassifier(); + EReference ref = GrammarUtil.getReference(crossReference,eClass); + String trimmedPrefix = contentAssistContext.getMatchString().trim(); + Iterable<IScopedElement> candidates = scopeProvider.getScope(contentAssistContext.getModel(), ref) .getAllContents(); for (IScopedElement candidate : candidates) { - if (candidate.name() != null - && isCandidateMatchingPrefix(contentAssistContext + if (candidate.name() != null && isCandidateMatchingPrefix(contentAssistContext .getModel(), ref, candidate, trimmedPrefix)) { - completionProposalList.add(createCompletionProposal( - crossReference, candidate.name(), + completionProposalList.add(createCompletionProposal(crossReference, candidate.name(), contentAssistContext)); } } @@ -294,9 +248,7 @@ public abstract class AbstractJavaProposalProvider implements IProposalProvider protected boolean isCandidateMatchingPrefix(EObject model, EReference ref, IScopedElement candidate, String prefix) { if (candidate.name() == null) - throw new IllegalArgumentException( - "unnamed candidates may not be proposed"); - return candidate.name().regionMatches(true, 0, prefix, 0, - prefix.length()); + throw new IllegalArgumentException("unnamed candidates may not be proposed"); + return candidate.name().regionMatches(true, 0, prefix, 0,prefix.length()); } } diff --git a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/DefaultContentAssistCalculator.java b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/DefaultContentAssistCalculator.java index 42624a5..3887552 100644 --- a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/DefaultContentAssistCalculator.java +++ b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/DefaultContentAssistCalculator.java @@ -11,35 +11,27 @@ package org.eclipse.xtext.ui.common.editor.contentassist.impl; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; -import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.EReference; import org.eclipse.xtext.AbstractElement; import org.eclipse.xtext.AbstractRule; import org.eclipse.xtext.Alternatives; import org.eclipse.xtext.Assignment; import org.eclipse.xtext.CrossReference; -import org.eclipse.xtext.EcoreUtil2; import org.eclipse.xtext.GrammarUtil; import org.eclipse.xtext.Group; import org.eclipse.xtext.Keyword; import org.eclipse.xtext.ParserRule; import org.eclipse.xtext.RuleCall; -import org.eclipse.xtext.crossref.ILinkingService; -import org.eclipse.xtext.crossref.impl.IllegalNodeException; import org.eclipse.xtext.parsetree.AbstractNode; -import org.eclipse.xtext.parsetree.NodeUtil; import org.eclipse.xtext.parsetree.ParseTreeUtil; import org.eclipse.xtext.ui.common.editor.contentassist.IContentAssistCalculator; import org.eclipse.xtext.ui.common.editor.contentassist.IContentAssistContext; import org.eclipse.xtext.util.XtextSwitch; -import com.google.inject.Inject; - /** * Provides a default implementation of interface {@link IContentAssistContext} designed as <b>Switch</b> over the * Xtext ecore inheritance hierarchy to calculate and resolve (or flatten) 'container' level elements like @@ -53,59 +45,19 @@ import com.google.inject.Inject; */ public class DefaultContentAssistCalculator extends XtextSwitch<List<AbstractElement>> implements IContentAssistCalculator { - @Inject - private ILinkingService linkingService; - public List<AbstractElement> computeProposalElements(IContentAssistContext contentAssistContext) { - List<AbstractElement> computedElementList = new ArrayList<AbstractElement>(); - - Set<AbstractElement> nextValidElementSet = new LinkedHashSet<AbstractElement>(); - AbstractNode referenceNode=contentAssistContext.getReferenceNode(); - /** - * in case of a cross reference which isn't linked properly we evaluate or - * propose it again - */ - if (referenceNode != null && referenceNode.getGrammarElement() instanceof CrossReference && !isLinked(referenceNode)) { - nextValidElementSet.add(getAbstractElement(referenceNode)); - nextValidElementSet.addAll(ParseTreeUtil.getElementSetValidFromOffset(contentAssistContext.getRootNode(), - referenceNode, contentAssistContext.getOffSet())); - } else if (referenceNode == contentAssistContext.getNode()) { - if (referenceNode == null) - throw new NullPointerException("Unexpected: referenceNode is null."); - /** - * in case of 'at-the-end' of the previous,completed element we evaluate - * it again for 'right-to-left-backtracking' cases (e.g. for keyword - * 'kind' kind>|< |=cursorpos) - */ - Assignment containingAssignment = GrammarUtil.containingAssignment(referenceNode.getGrammarElement()); - - if (referenceNode.getGrammarElement() instanceof RuleCall && containingAssignment != null) { - nextValidElementSet.add(containingAssignment); - nextValidElementSet.addAll(ParseTreeUtil.getElementSetValidFromOffset(contentAssistContext - .getRootNode(), referenceNode, contentAssistContext.getOffSet())); - } - else { - nextValidElementSet = ParseTreeUtil.getElementSetValidFromOffset(contentAssistContext.getRootNode(), - referenceNode, contentAssistContext.getOffSet()); - nextValidElementSet.add(getAbstractElement(referenceNode)); - } - } - else if (referenceNode != null){ - nextValidElementSet = ParseTreeUtil.getElementSetValidFromOffset(contentAssistContext.getRootNode(), - referenceNode, contentAssistContext.getOffSet()); - } - + Set<AbstractElement> nextValidElementSet = ParseTreeUtil.getElementSetValidFromOffset( + contentAssistContext.getRootNode(),referenceNode, contentAssistContext.getOffSet()); + for (Iterator<AbstractElement> iterator = nextValidElementSet.iterator(); iterator.hasNext();) { AbstractElement abstractElement = iterator.next(); computedElementList.addAll(doSwitch(abstractElement)); } - return computedElementList; } - @Override public List<AbstractElement> caseAlternatives(Alternatives alternatives) { List<AbstractElement> elementList = new ArrayList<AbstractElement>(); @@ -144,12 +96,8 @@ public class DefaultContentAssistCalculator extends XtextSwitch<List<AbstractEle @Override public List<AbstractElement> caseRuleCall(RuleCall ruleCall) { - List<AbstractElement> elementList = new ArrayList<AbstractElement>(); - - elementList.add(ruleCall); - + List<AbstractElement> elementList = new ArrayList<AbstractElement>(Collections.singleton(ruleCall)); AbstractRule abstractRule = ruleCall.getRule(); - if (abstractRule instanceof ParserRule) { addWithNullCheck(elementList, doSwitch(((ParserRule) abstractRule).getAlternatives())); } @@ -172,35 +120,11 @@ public class DefaultContentAssistCalculator extends XtextSwitch<List<AbstractEle } } - private AbstractElement getAbstractElement(AbstractNode lastCompleteNode) { - return (AbstractElement) (lastCompleteNode.getGrammarElement() instanceof ParserRule ? - ((ParserRule)lastCompleteNode.getGrammarElement()).getAlternatives(): lastCompleteNode.getGrammarElement()); - } - - private boolean isLinked(AbstractNode lastCompleteNode) { - EObject semanticModel = NodeUtil.getNearestSemanticObject(lastCompleteNode); - CrossReference crossReference = (CrossReference) lastCompleteNode.getGrammarElement(); - EReference eReference = GrammarUtil.getReference(crossReference, semanticModel.eClass()); - - List<EObject> referencedObjects = EcoreUtil2.getAllReferencedObjects(semanticModel, eReference); - - if (referencedObjects.isEmpty()) - return false; - try { - List<EObject> linkCandidates = linkingService.getLinkedObjects(semanticModel, eReference, lastCompleteNode); - return !linkCandidates.isEmpty() && referencedObjects.containsAll(linkCandidates); - } catch( IllegalNodeException ex) { - return false; - } - } - private boolean isOptional(AbstractElement groupElement) { boolean isOptional = true; - if ((groupElement instanceof Group || groupElement instanceof Alternatives) && !GrammarUtil.isOptionalCardinality(groupElement)) { - - EList<AbstractElement> abstractTokens = groupElement instanceof Group ? ((Group) groupElement).getTokens() : ((Alternatives) groupElement).getGroups(); - + EList<AbstractElement> abstractTokens = groupElement instanceof Group ? + ((Group) groupElement).getTokens() : ((Alternatives) groupElement).getGroups(); for (Iterator<AbstractElement> iterator = abstractTokens.iterator(); isOptional && iterator.hasNext();) { AbstractElement abstractElement = iterator.next(); isOptional = isOptional(abstractElement); @@ -211,6 +135,4 @@ public class DefaultContentAssistCalculator extends XtextSwitch<List<AbstractEle } return isOptional; } - - } diff --git a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/DefaultContentAssistProcessor.java b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/DefaultContentAssistProcessor.java index a820d9f..a20759e 100644 --- a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/DefaultContentAssistProcessor.java +++ b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/DefaultContentAssistProcessor.java @@ -24,16 +24,16 @@ import org.eclipse.jface.text.contentassist.IContentAssistProcessor; import org.eclipse.jface.text.contentassist.IContextInformation; import org.eclipse.jface.text.contentassist.IContextInformationValidator; import org.eclipse.jface.text.templates.TemplateContextType; -import org.eclipse.swt.custom.StyledText; import org.eclipse.xtext.AbstractElement; import org.eclipse.xtext.Assignment; +import org.eclipse.xtext.CrossReference; import org.eclipse.xtext.Keyword; import org.eclipse.xtext.RuleCall; +import org.eclipse.xtext.TerminalRule; import org.eclipse.xtext.XtextPackage; import org.eclipse.xtext.parser.IParseResult; import org.eclipse.xtext.parsetree.AbstractNode; import org.eclipse.xtext.parsetree.CompositeNode; -import org.eclipse.xtext.parsetree.LeafNode; import org.eclipse.xtext.parsetree.NodeUtil; import org.eclipse.xtext.parsetree.ParseTreeUtil; import org.eclipse.xtext.resource.XtextResource; @@ -43,6 +43,7 @@ import org.eclipse.xtext.ui.common.editor.contentassist.IProposalProvider; import org.eclipse.xtext.ui.common.editor.contentassist.ITemplateContentAssistProcessor; import org.eclipse.xtext.ui.core.editor.model.IXtextDocument; import org.eclipse.xtext.ui.core.editor.model.UnitOfWork; +import org.eclipse.xtext.util.Strings; import com.google.inject.Inject; @@ -54,54 +55,22 @@ import com.google.inject.Inject; * @author Jan Köhnlein */ public class DefaultContentAssistProcessor implements IContentAssistProcessor { - // logger available to subclasses protected final Logger logger = Logger.getLogger(getClass()); - @Inject protected ITemplateContentAssistProcessor templateContentAssistProcessor; - @Inject protected IContentAssistCalculator contentAssistCalculator; - @Inject protected IProposalProvider proposalProvider; - /** * Computes the possible grammar elements following the one at the given offset and calls the respective methods on * the proposal provider. */ public ICompletionProposal[] computeCompletionProposals(final ITextViewer viewer, final int offset) { - IXtextDocument document = (IXtextDocument) viewer.getDocument(); - - return document.readOnly(new UnitOfWork<ICompletionProposal[]>() { - - public ICompletionProposal[] exec(XtextResource resource) throws Exception { - - IContentAssistContext contentAssistContext = createContext(resource, viewer, offset); - - List<AbstractElement> computeProposalElements = contentAssistCalculator - .computeProposalElements(contentAssistContext); - - List<ICompletionProposal> completionProposalList = collectCompletionProposals(computeProposalElements, - contentAssistContext); - - for (TemplateContextType templateContextType : collectTemplateContextTypes(computeProposalElements, - contentAssistContext)) { - addTemplates(viewer, offset, contentAssistContext, templateContextType, completionProposalList); - } - - List<? extends ICompletionProposal> processedCompletionProposalList = proposalProvider.sortAndFilter( - completionProposalList, contentAssistContext); - - return processedCompletionProposalList.toArray(new ICompletionProposal[processedCompletionProposalList - .size()]); - } - - }); + return document.readOnly(new ContentAssistProcessorUow(offset, viewer)); } - /* * (non-Javadoc) * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getCompletionProposalAutoActivationCharacters() @@ -141,26 +110,7 @@ public class DefaultContentAssistProcessor implements IContentAssistProcessor { public IContextInformationValidator getContextInformationValidator() { return new ContextInformationValidator(this); } - - /** - * adds templates to the list of proposals - * - * @param viewer the viewer whose document is used to compute the proposals - * @param offset an offset within the document for which completions should be computed - * @param contentAssistContext the current context of the content assist proposal request - * @param templateContextType within which templates are resolved - * @param completionProposalList list of proposal to add to - */ - protected void addTemplates(ITextViewer viewer, int offset, IContentAssistContext contentAssistContext, - TemplateContextType templateContextType, List<ICompletionProposal> completionProposalList) { - if (templateContentAssistProcessor != null) { - templateContentAssistProcessor.setContentAssistContext(contentAssistContext); - templateContentAssistProcessor.setContextType(templateContextType); - completionProposalList.addAll(Arrays.asList(templateContentAssistProcessor.computeCompletionProposals( - viewer, offset))); - } - } - + protected List<ICompletionProposal> collectCompletionProposals(List<AbstractElement> computeProposalElements, IContentAssistContext contentAssistContext) { List<ICompletionProposal> completionProposalList = new ArrayList<ICompletionProposal>(); for (AbstractElement computeProposalElement : computeProposalElements) { @@ -201,82 +151,116 @@ public class DefaultContentAssistProcessor implements IContentAssistProcessor { break; case XtextPackage.ASSIGNMENT: // TODO: change interface - // templateContextType = - // proposalProvider.getTemplateContextType((Assignment) - // computeProposalElement, contentAssistContext); + // templateContextType = + // proposalProvider.getTemplateContextType((Assignment)computeProposalElement, contentAssistContext); // addIfNotNull(templateContextTypes, templateContextType); break; } } return templateContextTypes; } - + /** + * Adds templates to the list of proposals, + * + * @param viewer the viewer whose document is used to compute the proposals + * @param offset an offset within the document for which completions should be computed + * @param contentAssistContext the current context of the content assist proposal request + * @param templateContextType within which templates are resolved + * @param completionProposalList list of proposal to add to + */ + protected void addTemplates(ITextViewer viewer, int offset, IContentAssistContext contentAssistContext, + TemplateContextType templateContextType, List<ICompletionProposal> completionProposalList) { + if (templateContentAssistProcessor != null) { + templateContentAssistProcessor.setContentAssistContext(contentAssistContext); + templateContentAssistProcessor.setContextType(templateContextType); + completionProposalList.addAll(Arrays.asList(templateContentAssistProcessor.computeCompletionProposals( + viewer, offset))); + } + } /** * Creates a new <code>IContentAssistContext</code> with all required informations for the current CA request. * * @param resource the underlying EMF resource to read (parse) from * @param viewer the viewer whose document is used to compute the proposals - * @param offset an offset within the document for which the context should be created - * @return a matching context containing all necessary informations for the current content assist request + * @param offset the offset within the resource for which the context should be created + * @return a list of applicable <code>IContentAssistContext</code> */ - protected IContentAssistContext createContext(XtextResource resource, ITextViewer viewer, final int offset) { - + protected List<IContentAssistContext> createContextList(XtextResource resource, String text, final int offset) { + List<IContentAssistContext> result = new ArrayList<IContentAssistContext>(); IParseResult parseResult = resource.getParseResult(); - Assert.isNotNull(parseResult); - CompositeNode rootNode = parseResult.getRootNode(); - AbstractNode referenceNode = ParseTreeUtil.getLastCompleteNodeByOffset(rootNode, offset); - - EObject model = NodeUtil.getNearestSemanticObject(referenceNode); - - AbstractNode node = ParseTreeUtil.getCurrentOrFollowingNodeByOffset(rootNode, offset); - - String matchingString = node == null ? "" : calculateMatchString(viewer, offset, node); - - /** - * - * if cursor is behind a complete keyword, accept any input => empty - * - * TODO: Find a way to distinguish between keywords like "+" or "-" and - * "extends" or "class" in the latter case, the prefix "" would not - * always be sufficient - * XXX SZ: the formatter may help to answer this question - */ - if (node != null && node.getGrammarElement() instanceof Keyword - && (node instanceof LeafNode && ((LeafNode) node).getText().equals(matchingString))) { - matchingString = ""; + AbstractNode nodeAtOffset = ParseTreeUtil.getCurrentOrFollowingNodeByOffset(rootNode, offset); + + if (referenceNode.getOffset()+referenceNode.getLength() == offset) { + AbstractNode precedingReferenceNode = ParseTreeUtil.getLastCompleteNodeByOffset(rootNode,Math.max(0, referenceNode.getOffset()-1)); + String matchingString = calculateMatchString(nodeAtOffset,text, offset); + result.add(newCompletionProposal(matchingString, offset, rootNode, precedingReferenceNode)); + if (!Strings.isEmpty(matchingString) && + referenceNode.getGrammarElement() instanceof Keyword || + referenceNode.getGrammarElement() instanceof CrossReference || + (referenceNode.getGrammarElement() instanceof RuleCall && + ((RuleCall)referenceNode.getGrammarElement()).getRule() instanceof TerminalRule)) { + result.add(newCompletionProposal("", offset, rootNode, referenceNode)); + } + } else { + String matchingString = calculateMatchString(nodeAtOffset,text, offset); + result.add(newCompletionProposal(matchingString, offset, rootNode, referenceNode)); } - - return new ContentAssistContext(model, offset, matchingString, node, referenceNode, rootNode); + + return result; } - /** - * Calculates the match string of the current <code>IContentAssistContext</code> based on the - * specified location within the text viewer. + * Calculates the match string of the based on the specified location within the given text. * - * @param viewer the viewer whose document is used to compute the proposals + * @param caretNode the node at the current caret position + * @param text the text to compute the matching string * @param offset an offset within the document for which the matchstring should be computed * @return a matching string */ - protected String calculateMatchString(ITextViewer viewer, int offset,AbstractNode node) { - - String prefix = ""; - - StyledText textWidget = viewer.getTextWidget(); + protected String calculateMatchString(AbstractNode caretNode, String text, int offset) { + StringBuilder matchString = new StringBuilder(); + char c = ' '; + while (offset>caretNode.getOffset() && !Character.isWhitespace(c = text.charAt(--offset))) { + matchString.insert(0, c); + } + return matchString.toString(); + } + + private ContentAssistContext newCompletionProposal(String matchingString, final int offset, CompositeNode rootNode, + AbstractNode referenceNode) { + EObject model = NodeUtil.getNearestSemanticObject(referenceNode); + AbstractNode node = ParseTreeUtil.getCurrentOrFollowingNodeByOffset(rootNode, offset); + return new ContentAssistContext(model, offset, matchingString, node, referenceNode, rootNode); + } + + private final class ContentAssistProcessorUow implements UnitOfWork<ICompletionProposal[]> { + private final int offset; + private final ITextViewer viewer; + + private ContentAssistProcessorUow(int offset, ITextViewer viewer) { + this.offset = offset; + this.viewer = viewer; + } - if (textWidget.getCharCount() > 0) { - int boundedOffset = Math.min(offset, textWidget.getCharCount()) - 1; - if (node.getTotalOffset() <= boundedOffset) { - prefix = textWidget.getText(node.getTotalOffset(), boundedOffset).trim(); + public ICompletionProposal[] exec(XtextResource resource) throws Exception { + List<ICompletionProposal> completionProposalList = new ArrayList<ICompletionProposal>(); + for (IContentAssistContext contentAssistContext : createContextList( + resource, viewer.getTextWidget().getText(), offset)) { + List<AbstractElement> computedElements = contentAssistCalculator + .computeProposalElements(contentAssistContext); + completionProposalList.addAll(collectCompletionProposals(computedElements,contentAssistContext)); + for (TemplateContextType templateContextType : collectTemplateContextTypes(computedElements, + contentAssistContext)) { + addTemplates(viewer, offset, contentAssistContext, templateContextType, completionProposalList); + } + proposalProvider.filter(completionProposalList,contentAssistContext); } + proposalProvider.sort(completionProposalList); + return completionProposalList.toArray(new ICompletionProposal[completionProposalList.size()]); } - - return prefix; } - - } diff --git a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/JavaReflectiveMethodInvoker.java b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/JavaReflectiveMethodInvoker.java index fe2dd04..410017f 100644 --- a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/JavaReflectiveMethodInvoker.java +++ b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/JavaReflectiveMethodInvoker.java @@ -39,8 +39,7 @@ public class JavaReflectiveMethodInvoker { if (method == null) { return null; } - Object result = invokeMethod(method, target, parameterValues.toArray(new Object[] {})); - return result; + return invokeMethod(method, target, parameterValues.toArray(new Object[] {})); } private final Method findMethod(Class<?> clazz, String name, Class<?>... paramTypes) { diff --git a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/ProposalFilterSorterUtil.java b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/ProposalFilterSorterUtil.java index 5aa1fc9..1ce2b99 100644 --- a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/ProposalFilterSorterUtil.java +++ b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/ProposalFilterSorterUtil.java @@ -18,29 +18,31 @@ import org.apache.log4j.Logger; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.xtext.ui.common.editor.contentassist.IContentAssistContext; import org.eclipse.xtext.ui.common.editor.contentassist.IProposalProvider; +import org.eclipse.xtext.util.Strings; /** * @author Jan Köhnlein - Initial contribution and API + * @author Michael Clay - Initial contribution and API */ public class ProposalFilterSorterUtil { + private static Logger logger = Logger.getLogger(IProposalProvider.class); + protected static final Comparator<ICompletionProposal> PROPOSAL_COMPARATOR = new Comparator<ICompletionProposal>() { public int compare(ICompletionProposal o1, ICompletionProposal o2) { return o1.getDisplayString().compareTo(o2.getDisplayString()); } }; - private static Logger logger = Logger.getLogger(IProposalProvider.class); - - public static List<? extends ICompletionProposal> sortAndFilter( + public static List<? extends ICompletionProposal> filter( List<? extends ICompletionProposal> completionProposalList, IContentAssistContext contentAssistContext) { Map<String, ICompletionProposal> displayString2ICompletionProposalMap = new HashMap<String, ICompletionProposal>(); for (Iterator<? extends ICompletionProposal> iterator = completionProposalList.iterator(); iterator.hasNext();) { ICompletionProposal completionProposal = iterator.next(); - // filter duplicate if (!displayString2ICompletionProposalMap.containsKey(completionProposal.getDisplayString())) { displayString2ICompletionProposalMap.put(completionProposal.getDisplayString(), completionProposal); - // filter by prefix - if (isFiltered(completionProposal)) { + if (!Strings.isEmpty(contentAssistContext.getMatchString()) && + (!completionProposal.getDisplayString().toUpperCase().startsWith(contentAssistContext.getMatchString().toUpperCase()) || + completionProposal.getDisplayString().equalsIgnoreCase(contentAssistContext.getMatchString()))) { if (logger.isDebugEnabled()) { logger.debug("filter completionProposal '" + completionProposal + "'"); } @@ -54,27 +56,12 @@ public class ProposalFilterSorterUtil { iterator.remove(); } } - Collections.sort(completionProposalList, PROPOSAL_COMPARATOR); return completionProposalList; } - - /** - * The default behavior of this method delegates to - * {@link XtextCompletionProposal#matches(String)} to test if the given - * prefix string matches or not. - * - * @param completionProposal - * contains information used to present the proposed completion - * to the user - * @return true or false whether the given prefix matches the text of this - * completion proposal - */ - protected static boolean isFiltered(ICompletionProposal completionProposal) { - if (completionProposal instanceof XtextCompletionProposal) { - XtextCompletionProposal xtextCompletionProposal = (XtextCompletionProposal) completionProposal; - return !xtextCompletionProposal.matches(); - } - return false; + + public static List<? extends ICompletionProposal> sort(List<? extends ICompletionProposal> completionProposalList) { + Collections.sort(completionProposalList, PROPOSAL_COMPARATOR); + return completionProposalList; } } diff --git a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/XtextCompletionProposal.java b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/XtextCompletionProposal.java index 2dfcceb..93aa551 100644 --- a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/XtextCompletionProposal.java +++ b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/XtextCompletionProposal.java @@ -1,7 +1,6 @@ package org.eclipse.xtext.ui.common.editor.contentassist.impl; import org.apache.log4j.Logger; -import org.eclipse.emf.ecore.EObject; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.contentassist.ICompletionProposal; @@ -9,10 +8,6 @@ import org.eclipse.jface.text.contentassist.IContextInformation; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.xtext.AbstractElement; -import org.eclipse.xtext.CrossReference; -import org.eclipse.xtext.GrammarUtil; -import org.eclipse.xtext.Keyword; -import org.eclipse.xtext.ParserRule; import org.eclipse.xtext.ui.common.editor.contentassist.IContentAssistContext; /** @@ -28,7 +23,6 @@ public class XtextCompletionProposal implements ICompletionProposal { // logger available to subclasses protected final Logger logger = Logger.getLogger(XtextCompletionProposal.class); - private final AbstractElement abstractElement; private final String text; private int selectionOffset; @@ -37,19 +31,14 @@ public class XtextCompletionProposal implements ICompletionProposal { private int nodeTotalOffset; private int nodeTotalLength; private String matchString; - private EObject grammarElement; - private boolean isCusorAtEndOfLastCompleteNode; public XtextCompletionProposal(AbstractElement abstractElement, String displayString, IContentAssistContext contentAssistContext) { - this.abstractElement = abstractElement; this.text = displayString; this.offset = contentAssistContext.getOffSet(); this.nodeTotalOffset = contentAssistContext.getNode().getTotalOffset(); this.nodeTotalLength = contentAssistContext.getNode().getTotalLength(); this.matchString = contentAssistContext.getMatchString(); - this.grammarElement = contentAssistContext.getNode().getGrammarElement(); - this.isCusorAtEndOfLastCompleteNode = contentAssistContext.getNode() == contentAssistContext.getReferenceNode(); } public void apply(IDocument document) { @@ -87,52 +76,9 @@ public class XtextCompletionProposal implements ICompletionProposal { return new Point(this.selectionOffset, 0); } - /** - * - * @return true or false whether the given prefix matches the text of this - * completion proposal - */ - public boolean matches() { - boolean matches = true; - AbstractElement abstractElement = null; - if (this.abstractElement instanceof Keyword || this.abstractElement instanceof CrossReference) { - abstractElement = GrammarUtil.containingAssignment(this.abstractElement); - } - if (null == abstractElement) { - abstractElement = this.abstractElement; - } - boolean candidateToCompare = false; - - // means if we are at the end of a complete token we want to filter only - // equal grammarelements (not the 'next' ones) - if (isCusorAtEndOfLastCompleteNode && abstractElement.equals(getCurrentGrammarElement())) { - candidateToCompare = true; - } - else if (!isCusorAtEndOfLastCompleteNode) { - candidateToCompare = true; - } - if (candidateToCompare - && (!"".equals(matchString.trim()) && !getDisplayString().toUpperCase().trim().startsWith( - matchString.toUpperCase().trim()))) { - matches = false; - } - return matches; - } - @Override public String toString() { return "XtextCompletionPoposal[text='" + this.text + "']"; } - /** - * @return the grammar element of the current node - */ - public EObject getCurrentGrammarElement() { - EObject containingAssignment = GrammarUtil.containingAssignment(grammarElement); - if (containingAssignment == null) { - return grammarElement instanceof ParserRule ? ((ParserRule) grammarElement).getAlternatives() - : grammarElement; - } - return containingAssignment; - } } diff --git a/plugins/org.eclipse.xtext.xtend/src/org/eclipse/xtext/xtend/contentassist/AbstractXtendProposalProvider.java b/plugins/org.eclipse.xtext.xtend/src/org/eclipse/xtext/xtend/contentassist/AbstractXtendProposalProvider.java index 5e19132..d614583 100644 --- a/plugins/org.eclipse.xtext.xtend/src/org/eclipse/xtext/xtend/contentassist/AbstractXtendProposalProvider.java +++ b/plugins/org.eclipse.xtext.xtend/src/org/eclipse/xtext/xtend/contentassist/AbstractXtendProposalProvider.java @@ -12,8 +12,6 @@ import java.util.Collections; import java.util.List; import org.apache.log4j.Logger; -import org.eclipse.emf.ecore.EObject; -import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.templates.Template; import org.eclipse.jface.text.templates.TemplateContextType; @@ -25,8 +23,6 @@ import org.eclipse.xtext.ParserRule; import org.eclipse.xtext.RuleCall; import org.eclipse.xtext.TerminalRule; import org.eclipse.xtext.TypeRef; -import org.eclipse.xtext.parsetree.AbstractNode; -import org.eclipse.xtext.parsetree.LeafNode; import org.eclipse.xtext.ui.common.editor.contentassist.IContentAssistContext; import org.eclipse.xtext.ui.common.editor.contentassist.IProposalProvider; import org.eclipse.xtext.ui.common.editor.contentassist.impl.AbstractJavaProposalProvider; @@ -168,21 +164,21 @@ public abstract class AbstractXtendProposalProvider extends AbstractXtendService protected String getDefaultImageFilePath() { return "icons/editor.gif"; } + + /* + * (non-Javadoc) + * @see org.eclipse.xtext.ui.common.editor.contentassist.IProposalProvider#filter(java.util.List, org.eclipse.xtext.ui.common.editor.contentassist.IContentAssistContext) + */ + public List<? extends ICompletionProposal> filter(List<ICompletionProposal> completionProposalList, + IContentAssistContext contentAssistContext) { + return ProposalFilterSorterUtil.filter(completionProposalList,contentAssistContext); + } - /** - * Concrete subclasses can override this for custom sort and filter - * behavior. Called right after all completion proposals have been - * collected. - * - * The default behavior of this implementation is to filter duplicates and - * to trim matching <code>ICompletionProposal#displayString</code> with - * matching prefix values. - * - * @see #sortAndFilter(List, EObject, String, IDocument, int, AbstractNode, - * LeafNode) + /* + * (non-Javadoc) + * @see org.eclipse.xtext.ui.common.editor.contentassist.IProposalProvider#sort(java.util.List) */ - public List<? extends ICompletionProposal> sortAndFilter( - List<? extends ICompletionProposal> completionProposalList, IContentAssistContext contentAssistContext) { - return ProposalFilterSorterUtil.sortAndFilter(completionProposalList, contentAssistContext); + public List<? extends ICompletionProposal> sort(List<ICompletionProposal> completionProposalList) { + return ProposalFilterSorterUtil.sort(completionProposalList); } } diff --git a/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/ContentAssistProcessorTestBuilder.java b/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/ContentAssistProcessorTestBuilder.java index 3b769ab..c4bd6e9 100644 --- a/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/ContentAssistProcessorTestBuilder.java +++ b/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/ContentAssistProcessorTestBuilder.java @@ -13,7 +13,8 @@ *******************************************************************************/ package org.eclipse.xtext.ui.common.editor.contentassist.impl; -import static org.easymock.EasyMock.*; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; @@ -128,24 +129,19 @@ public class ContentAssistProcessorTestBuilder extends AbstractXtextTests { public ContentAssistProcessorTestBuilder assertMatchString(String matchString) throws Exception { - - final String currentModelToParse = getModel(); - + String currentModelToParse = getModel(); final XtextResource xtextResource = getResource(new StringInputStream(currentModelToParse)); - - final IXtextDocument xtextDocument = getDocument(xtextResource); - - IContentAssistContext contentAssistContext = new DefaultContentAssistProcessor() { + List<IContentAssistContext> contentAssistContextList = new DefaultContentAssistProcessor() { @Override - public IContentAssistContext createContext(XtextResource resource, ITextViewer viewer, int offset) { - IContentAssistContext createContext = super.createContext(resource,viewer,offset); - return createContext; + public List<IContentAssistContext> createContextList(XtextResource resource, String text, final int offset) { + return super.createContextList(xtextResource, text, offset); } - }.createContext(xtextResource, resetTextViewerMock(currentModelToParse, xtextDocument),cursorPosition); - - - assertEquals(matchString, contentAssistContext.getMatchString()); - + }.createContextList(xtextResource, currentModelToParse,cursorPosition); + + for (IContentAssistContext contentAssistContext : contentAssistContextList) { + assertEquals(matchString, contentAssistContext.getMatchString()); + break; + } return this; } @@ -169,8 +165,15 @@ public class ContentAssistProcessorTestBuilder extends AbstractXtextTests { ICompletionProposal[] computeCompletionProposals = computeCompletionProposals(currentModelToParse, cursorPosition); + StringBuffer computedProposals = new StringBuffer(); + for (int i = 0; i < computeCompletionProposals.length; i++) { + computedProposals.append(computeCompletionProposals[i].getDisplayString()); + if (i<(computeCompletionProposals.length-1)) { + computedProposals.append(","); + } + } assertEquals("expect only " + completionProposalCount + " CompletionProposal item for model '" - + currentModelToParse + "'", completionProposalCount, computeCompletionProposals.length); + + currentModelToParse + "' but got '"+computedProposals+"'", completionProposalCount, computeCompletionProposals.length); return this; } @@ -211,7 +214,7 @@ public class ContentAssistProcessorTestBuilder extends AbstractXtextTests { private ITextViewer resetTextViewerMock(final String currentModelToParse, final IXtextDocument xtextDocument) { EasyMock.reset(textViewerMock); expect(textViewerMock.getDocument()).andReturn(xtextDocument); - expect(textViewerMock.getTextWidget()).andReturn(newStyledTextWidgetMock(currentModelToParse)); + expect(textViewerMock.getTextWidget()).andReturn(newStyledTextWidgetMock(currentModelToParse)).times(2); replay(textViewerMock); return textViewerMock; } @@ -234,6 +237,11 @@ public class ContentAssistProcessorTestBuilder extends AbstractXtextTests { public String getText(int start, int end) { return testDslModel.substring(start, end + 1); } + + @Override + public String getText() { + return testDslModel; + } }; } diff --git a/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/DefaultContentAssistProcessorTest.java b/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/DefaultContentAssistProcessorTest.java index da97e85..c45ee91 100644 --- a/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/DefaultContentAssistProcessorTest.java +++ b/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/DefaultContentAssistProcessorTest.java @@ -40,9 +40,8 @@ import com.google.inject.Injector; * @author Jan Koehnlein
* @see org.eclipse.xtext.ui.common.editor.contentassist.impl.DefaultContentAssistProcessor
*/
-public class DefaultContentAssistProcessorTest extends AbstractXtextTests
-{
-
+public class DefaultContentAssistProcessorTest extends AbstractXtextTests {
+
private ISetup getRefGrammarSetup() {
return new ReferenceGrammarTestLanguageStandaloneSetup() {
@Override
@@ -80,11 +79,12 @@ public class DefaultContentAssistProcessorTest extends AbstractXtextTests }
public void testComputePrefix() throws Exception {
+ String model = "spielplatz 1 \"SpielplatzBeschreibung\" { kind(k1 0) kind(k2 0) erwachsener(e1 0) erwachsener(e2 0) ";
newBuilder(getRefGrammarSetup())
- .append("foo fvdf dfv(").assertMatchString("(").reset()
- .append("foo fvdf dfv").assertMatchString("dfv").reset()
- .append("foo fvdf dfv_").assertMatchString("dfv_").reset()
- .append("foo fvdf dfv_ ").assertMatchString("").reset()
+ .append(model+" familie( dfv(").assertMatchString("(").reset()
+ .append(model+" familie( dfv").assertMatchString("dfv").reset()
+ .append(model+" familie( dfv_").assertMatchString("dfv_").reset()
+ .append(model+" familie( dfv_ ").assertMatchString("").reset()
.append("").assertMatchString("");
}
@@ -134,13 +134,13 @@ public class DefaultContentAssistProcessorTest extends AbstractXtextTests builder.append(" ER").assertText("erwachsener");
builder.append(" SP").assertText("spielzeug");
builder.append(" FA").assertText("familie");
- builder.append(" familie ( KEY").assertText("keyword");
- builder.append(" familie ( K").assertText("keyword");
+ builder.append(" familie ( KEY").assertText("keyword","e1","e2");
+ builder.append(" familie ( K").assertText("keyword","e1","e2");
builder.append(" familie ( keyword E").assertText("e1", "e2");
- builder.append(" familie ( keyword e1 E").assertText("e1", "e2");
+ builder.append(" familie ( keyword e1 E").assertText("e1", "e2","k1","k2");
builder.append(" familie ( keyword e1 e2 K").assertText("k1", "k2", ",", ")");
- builder.append(" familie ( keyword e1 e2 k1,K").assertText("k1", "k2", ",", ")");
- builder.append(" familie ( keyword e1 e2 k1,k2").assertText("k2", ",", ")");
+ builder.append(" familie ( keyword e1 e2 k1,K").assertText(",", ")");
+ builder.append(" familie ( keyword e1 e2 k1,k2").assertText(",", ")");
}
/**
@@ -189,8 +189,7 @@ public class DefaultContentAssistProcessorTest extends AbstractXtextTests "R3",
"\"Keyword_Value\"",
"(",
- "[",
- "+=" // TODO: Why does this proposal come up?
+ "["
);
}
@@ -200,8 +199,7 @@ public class DefaultContentAssistProcessorTest extends AbstractXtextTests .appendNl("R1 ();")
.append("R2 rule :").assertText(
"R1",
- "R2",
- ":" // TODO: Why does this proposal come up?
+ "R2"
);
}
@@ -223,25 +221,22 @@ public class DefaultContentAssistProcessorTest extends AbstractXtextTests .appendNl("MyRule : 'foo' name=ID; ").assertText(
"ParserRule_Name", "terminal"
);
-
}
/**
- * SZ: uncomment because it fails and neither the result nor the excpectation
- * seem to be right
* regression test for:
*
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=260825
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=262313
*/
-// public void testCompleteAssignmentWithBacktracking() throws Exception {
-// newBuilder(getXtextGrammarSetup())
-// .appendNl("grammar foo with org.eclipse.xtext.common.Terminals")
-// .appendNl("generate foo \"foo\"")
-// .append("MyRule : 'foo' name").assertText(
-// "*", "+", "+=", ";", "=", "?", "?="
-// );
-// }
+ public void testCompleteAssignmentWithBacktracking() throws Exception {
+ newBuilder(getXtextGrammarSetup())
+ .appendNl("grammar foo with org.eclipse.xtext.common.Terminals")
+ .appendNl("generate foo \"foo\"")
+ .append("MyRule : 'foo' name").assertText("\"Keyword_Value\"", "(", "*", "+", "+=", ";", "=", "?", "?=",
+ "Assignment_Feature", "MyRule", "{");
+
+ }
public void testKeywordWithBackslashes() throws Exception {
newBuilder(getKeywordsLangSetup())
diff --git a/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/TwoContextsContentAssistTest.java b/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/TwoContextsContentAssistTest.java index 8d86507..f642c9c 100644 --- a/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/TwoContextsContentAssistTest.java +++ b/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/editor/contentassist/impl/TwoContextsContentAssistTest.java @@ -27,10 +27,7 @@ import com.google.inject.Injector; public class TwoContextsContentAssistTest extends AbstractXtextTests { public void testTwoContexts() throws Exception { - System.err.println(getClass().getSimpleName()+" says: PLEASE ACTIVATE ME!"); -// newBuilder(getGrammarSetup()) -// .append("Foo; FooBar; Bar refersTo Foo").assertText( -// ";", "FooBar"); + newBuilder(getGrammarSetup()).append("Foo; FooBar; Bar refersTo Foo").assertText(";", "FooBar"); } /** |

