summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormclay2008-12-08 11:41:25 (EST)
committer sefftinge2008-12-08 11:41:25 (EST)
commit683549db652665646a12f40a177cda9c21c15897 (patch)
treee4f95b9bb02cf20d420ed68a4c5d1af7d02bb97c
parent4f0a72c5fc39729f59b87c5ccbc631c471a93758 (diff)
downloadorg.eclipse.xtext-683549db652665646a12f40a177cda9c21c15897.zip
org.eclipse.xtext-683549db652665646a12f40a177cda9c21c15897.tar.gz
org.eclipse.xtext-683549db652665646a12f40a177cda9c21c15897.tar.bz2
NEW - bug 257940: [Content Assist] new bug for expression like grammar reported from TMF mailinglist
https://bugs.eclipse.org/bugs/show_bug.cgi?id=257940
-rw-r--r--plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/AbstractProposalProvider.java146
-rw-r--r--plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/ContentAssistContextAdapter.java150
-rw-r--r--plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/DefaultContentAssistProcessor.java194
-rw-r--r--plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/XtextCompletionProposal.java129
-rw-r--r--tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/parsetree/ParseTreeUtilTest.java12
-rw-r--r--tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/editor/outline/OutlineViewTest.java15
6 files changed, 422 insertions, 224 deletions
diff --git a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/AbstractProposalProvider.java b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/AbstractProposalProvider.java
index f360507..3d84e4a 100644
--- a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/AbstractProposalProvider.java
+++ b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/AbstractProposalProvider.java
@@ -2,6 +2,7 @@ package org.eclipse.xtext.ui.common.editor.codecompletion;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -26,10 +27,7 @@ import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.crossref.IScopedElement;
import org.eclipse.xtext.crossref.impl.SimpleAttributeResolver;
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;
import org.eclipse.xtext.service.Inject;
import org.eclipse.xtext.util.Strings;
@@ -47,6 +45,8 @@ public abstract class AbstractProposalProvider implements IProposalProvider {
protected static final String LEXER_RULE_ID = "ID";
protected static final String LEXER_RULE_INT = "INT";
protected static final String LEXER_RULE_STRING = "STRING";
+
+ protected static final Comparator<ICompletionProposal> PROPOSAL_COMPARATOR= new ProposalComparator();
// logger available to subclasses
protected final Logger logger = Logger.getLogger(getClass());
@@ -66,9 +66,7 @@ public abstract class AbstractProposalProvider implements IProposalProvider {
/*
* (non-Javadoc)
*
- * @see
- * org.eclipse.xtext.ui.common.editor.codecompletion.IProposalProvider#completeKeyword(org.eclipse.xtext.Keyword,
- * org.eclipse.emf.ecore.EObject, java.lang.String, org.eclipse.jface.text.IDocument, int)
+ * @see org.eclipse.xtext.ui.common.editor.codecompletion.IProposalProvider#completeKeyword(org.eclipse.xtext.Keyword,org.eclipse.emf.ecore.EObject, java.lang.String, org.eclipse.jface.text.IDocument, int)
*/
public List<? extends ICompletionProposal> completeKeyword(Keyword keyword, EObject model, String prefix,
IDocument doc, int offset) {
@@ -83,9 +81,7 @@ public abstract class AbstractProposalProvider implements IProposalProvider {
/*
* (non-Javadoc)
*
- * @see
- * org.eclipse.xtext.ui.common.editor.codecompletion.IProposalProvider#completeRuleCall(org.eclipse.xtext.RuleCall,
- * org.eclipse.emf.ecore.EObject, java.lang.String, org.eclipse.jface.text.IDocument, int)
+ * @see org.eclipse.xtext.ui.common.editor.codecompletion.IProposalProvider#completeRuleCall(org.eclipse.xtext.RuleCall, org.eclipse.emf.ecore.EObject, java.lang.String, org.eclipse.jface.text.IDocument, int)
*/
public List<? extends ICompletionProposal> completeRuleCall(RuleCall ruleCall, EObject model, String prefix,
IDocument doc, int offset) {
@@ -106,9 +102,7 @@ public abstract class AbstractProposalProvider implements IProposalProvider {
/*
* (non-Javadoc)
*
- * @see org.eclipse.xtext.ui.common.editor.codecompletion.IProposalProvider#sortAndFilter(java.util.List,
- * org.eclipse.emf.ecore.EObject, java.lang.String, org.eclipse.jface.text.IDocument, int,
- * org.eclipse.xtext.parsetree.AbstractNode, org.eclipse.xtext.parsetree.LeafNode)
+ * @see org.eclipse.xtext.ui.common.editor.codecompletion.IProposalProvider#sortAndFilter(java.util.List, org.eclipse.emf.ecore.EObject, java.lang.String, org.eclipse.jface.text.IDocument, int, org.eclipse.xtext.parsetree.AbstractNode, org.eclipse.xtext.parsetree.LeafNode)
*/
public List<? extends ICompletionProposal> sortAndFilter(
List<? extends ICompletionProposal> completionProposalList, EObject model, String prefix,
@@ -125,12 +119,9 @@ public abstract class AbstractProposalProvider implements IProposalProvider {
* (e.i. ParserRuleName+AssignmentFeatureName+LexerRuleName) or {@link #getDefaultIntegerValue()} if its <i>INT</i>
* based LexerRule.
*
- * @param lexerRule
- * the 'called' LexerRule instance
- * @param ruleCall
- * the ruleCall for the provided lexerRule
- * @param offset
- * an offset within the document for which completions should be computed
+ * @param lexerRule the 'called' LexerRule instance
+ * @param ruleCall the ruleCall for the provided lexerRule
+ * @param offset an offset within the document for which completions should be computed
* @return a computed list of <code>ICompletionProposal</code> for the given <code>LexerRule</code>
*/
protected List<? extends ICompletionProposal> doCompleteLexerRuleRuleCall(LexerRule lexerRule, RuleCall ruleCall,
@@ -190,15 +181,23 @@ public abstract class AbstractProposalProvider implements IProposalProvider {
if (linkingCandidatesService != null) {
- final XtextResource xtextResource = (XtextResource) model.eResource();
- final ParserRule containingParserRule = GrammarUtil.containingParserRule(crossReference);
- final EClass eClass = xtextResource.getElementFactory().getEClass(GrammarUtil.getReturnTypeName(containingParserRule));
- final EReference ref = GrammarUtil.getReference(crossReference, eClass);
- final Iterable<IScopedElement> candidates = linkingCandidatesService.getLinkingCandidates(model, ref);
- final String trimmedPrefix = prefix.trim();
+ XtextResource xtextResource = (XtextResource) model.eResource();
+
+ ParserRule containingParserRule = GrammarUtil.containingParserRule(crossReference);
+
+ EClass eClass = xtextResource.getElementFactory().
+ getEClass(GrammarUtil.getReturnTypeName(containingParserRule));
+
+ EReference ref = GrammarUtil.getReference(crossReference, eClass);
+
+ Iterable<IScopedElement> candidates = linkingCandidatesService.getLinkingCandidates(model, ref);
+
+ String trimmedPrefix = prefix.trim();
+
for (IScopedElement candidate : candidates) {
if (isCandidateMatchingPrefix(model, ref, candidate, trimmedPrefix)) {
- completionProposalList.add(createCompletionProposal(crossReference, model, candidate.name(), offset));
+ completionProposalList.add(
+ createCompletionProposal(crossReference, model, candidate.name(), offset));
}
}
}
@@ -224,7 +223,7 @@ public abstract class AbstractProposalProvider implements IProposalProvider {
}
/**
- * Concrete subclasses can override this for custom sort and filter behavior. Gets called after all completion
+ * 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 sort duplicates and to trim matching
@@ -242,82 +241,71 @@ public abstract class AbstractProposalProvider implements IProposalProvider {
ICompletionProposal completionProposal = iterator.next();
- // filter duplicate displayString
+ // filter duplicate
if (!displayString2ICompletionProposalMap.containsKey(completionProposal.getDisplayString())) {
displayString2ICompletionProposalMap.put(completionProposal.getDisplayString(), completionProposal);
- if (model != null) {
-
- // filter by prefix
- // TODO: this works only if we have access to the corresponding grammarelement
- if (completionProposal instanceof XtextCompletionProposal) {
-
- XtextCompletionProposal xtextCompletionProposal = (XtextCompletionProposal) completionProposal;
-
- AbstractElement abstractElement = null;
-
- if (xtextCompletionProposal.getAbstractElement() instanceof Keyword ||
- xtextCompletionProposal.getAbstractElement() instanceof CrossReference) {
- abstractElement = GrammarUtil.containingAssignment(xtextCompletionProposal.getAbstractElement());
- }
-
- if (null==abstractElement) {
- abstractElement = xtextCompletionProposal.getAbstractElement();
- }
-
- CompositeNode rootNode = NodeUtil.getRootNode(model);
-
- AbstractNode lastCompleteNode = ParseTreeUtil.getLastCompleteNodeByOffset(rootNode, offset);
-
- LeafNode currentLeafNode = ParseTreeUtil.getCurrentNodeByOffset(rootNode, offset);
-
- EObject grammarElement = GrammarUtil.containingAssignment(currentLeafNode.getGrammarElement());
-
- if (null==grammarElement) {
- grammarElement = currentLeafNode.getGrammarElement();
- }
-
- boolean atTheEndOfTheLastCompleteNode = currentLeafNode == lastCompleteNode;
-
- 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 (atTheEndOfTheLastCompleteNode && abstractElement.equals(grammarElement)) {
- candidateToCompare = true;
- } else if (!atTheEndOfTheLastCompleteNode ) {
- candidateToCompare = true;
- }
-
- if ( candidateToCompare && (!"".equals(prefix.trim()) && !completionProposal.getDisplayString().toUpperCase().startsWith(prefix.toUpperCase()))) {
- if (logger.isDebugEnabled()) {
- logger.debug("filter completionProposal '" + completionProposal + "'");
- }
- iterator.remove();
- }
+ // filter by prefix
+ if (isFiltered(model, prefix, completionProposal)) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("filter completionProposal '" + completionProposal + "'");
}
+ iterator.remove();
}
-
}
else {
if (logger.isDebugEnabled()) {
logger.debug("filter duplicate completionProposal '" + completionProposal + "'");
}
-
iterator.remove();
}
}
+ Collections.sort(completionProposalList, PROPOSAL_COMPARATOR);
+
return completionProposalList;
}
/**
- * @param text
- * to apply
+ * The default behaviour of this method delegates to {@link XtextCompletionProposal#matches(String)} to
+ * test if the given prefix string matches or not.
+ *
+ * @param model the last semtantically complete object
+ * @param prefix
+ * @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 boolean isFiltered(EObject model, String prefix, ICompletionProposal completionProposal) {
+
+ if (completionProposal instanceof XtextCompletionProposal) {
+
+ XtextCompletionProposal xtextCompletionProposal = (XtextCompletionProposal) completionProposal;
+
+ return !xtextCompletionProposal.matches(prefix);
+ }
+
+ return false;
+ }
+
+ /**
+ * @param text to apply
* @return the provided string with the first letter capitalized
*/
protected final String firstLetterCapitalized(String text) {
return Strings.toFirstUpper(text);
}
+
+ /**
+ *
+ * Simple {@link Comparator} implementation to compare
+ * <code>ICompletionProposal</code> by disaply strings'.
+ *
+ */
+ private static final class ProposalComparator implements Comparator<ICompletionProposal> {
+ public int compare(ICompletionProposal o1, ICompletionProposal o2) {
+ return o1.getDisplayString().compareTo(o2.getDisplayString());
+ }
+ }
}
diff --git a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/ContentAssistContextAdapter.java b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/ContentAssistContextAdapter.java
new file mode 100644
index 0000000..615ca53
--- /dev/null
+++ b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/ContentAssistContextAdapter.java
@@ -0,0 +1,150 @@
+/*******************************************************************************
+ * Copyright (c) 2008 itemis AG (http://www.itemis.eu) 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.codecompletion;
+
+import org.eclipse.emf.common.notify.impl.AdapterImpl;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.xtext.GrammarUtil;
+import org.eclipse.xtext.ParserRule;
+import org.eclipse.xtext.parsetree.AbstractNode;
+import org.eclipse.xtext.parsetree.CompositeNode;
+
+/**
+ * Represents a custom emf adapter to adapt or extend the behaviour of emf
+ * models used to provide CA related context information.
+ *
+ * @author Michael Clay - Initial contribution and API
+ * @see org.eclipse.emf.common.notify.Adapter
+ */
+public class ContentAssistContextAdapter extends AdapterImpl {
+
+ private CompositeNode rootNode;
+
+ private AbstractNode lastCompleteNode;
+
+ private AbstractNode currentNode;
+
+ private String prefix;
+
+ private int offset;
+
+ public ContentAssistContextAdapter(CompositeNode rootNode, AbstractNode currentNode, AbstractNode lastCompleteNode,
+ int offset, String prefix) {
+ super();
+ this.rootNode = rootNode;
+ this.currentNode = currentNode;
+ this.lastCompleteNode = lastCompleteNode;
+ this.offset = offset;
+ this.prefix = prefix;
+ }
+
+ /**
+ * @return true or false wheter the cursor is at the the last complete node
+ */
+ public boolean isCusorAtEndOfLastCompleteNode() {
+ return lastCompleteNode == currentNode;
+ }
+
+ /**
+ * @return the grammar element of the current node
+ */
+ public EObject getCurrentGrammarElement() {
+
+ EObject grammarElement = GrammarUtil.containingAssignment(currentNode.getGrammarElement());
+
+ if (null == grammarElement) {
+ grammarElement = (currentNode.getGrammarElement() instanceof ParserRule ? ((ParserRule) currentNode
+ .getGrammarElement()).getAlternatives()
+ : currentNode.getGrammarElement());
+ }
+
+ return grammarElement;
+ }
+
+ @Override
+ public boolean isAdapterForType(Object type) {
+ return type == ContentAssistContextAdapter.class;
+ }
+
+ /**
+ * @return the rootNode
+ */
+ public CompositeNode getRootNode() {
+ return rootNode;
+ }
+
+ /**
+ * @param rootNode
+ * the rootNode to set
+ */
+ public void setRootNode(CompositeNode rootNode) {
+ this.rootNode = rootNode;
+ }
+
+ /**
+ * @return the lastCompleteNode
+ */
+ public AbstractNode getLastCompleteNode() {
+ return lastCompleteNode;
+ }
+
+ /**
+ * @param lastCompleteNode
+ * the lastCompleteNode to set
+ */
+ public void setLastCompleteNode(AbstractNode lastCompleteNode) {
+ this.lastCompleteNode = lastCompleteNode;
+ }
+
+ /**
+ * @return the currentNode
+ */
+ public AbstractNode getCurrentNode() {
+ return currentNode;
+ }
+
+ /**
+ * @param currentNode
+ * the currentNode to set
+ */
+ public void setCurrentNode(AbstractNode currentNode) {
+ this.currentNode = currentNode;
+ }
+
+ /**
+ * @return the prefix
+ */
+ public String getPrefix() {
+ return prefix;
+ }
+
+ /**
+ * @param prefix
+ * the prefix to set
+ */
+ public void setPrefix(String prefix) {
+ this.prefix = prefix;
+ }
+
+ /**
+ * @return the offset
+ */
+ public int getOffset() {
+ return offset;
+ }
+
+ /**
+ * @param offset
+ * the offset to set
+ */
+ public void setOffset(int offset) {
+ this.offset = offset;
+ }
+
+}
diff --git a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/DefaultContentAssistProcessor.java b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/DefaultContentAssistProcessor.java
index 8a08ed6..698773c 100644
--- a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/DefaultContentAssistProcessor.java
+++ b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/DefaultContentAssistProcessor.java
@@ -7,8 +7,10 @@ import java.util.Set;
import org.apache.log4j.Logger;
import org.eclipse.core.runtime.Assert;
+import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.ContextInformationValidator;
@@ -23,6 +25,7 @@ import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.GrammarUtil;
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.parser.IParseResult;
@@ -48,14 +51,10 @@ public class DefaultContentAssistProcessor implements IContentAssistProcessor {
@Inject
private IProposalProvider proposalProvider;
+
+ @Inject
+ private ILinkingService linkingService;
- /**
- * @param proposalProvider
- * the proposalProvider to set
- */
- public void setProposalProvider(IProposalProvider proposalProvider) {
- this.proposalProvider = proposalProvider;
- }
/**
* computes the possible grammar elements following the one at the given offset and calls the respective methods on
@@ -65,97 +64,122 @@ public class DefaultContentAssistProcessor implements IContentAssistProcessor {
ICompletionProposal[] completionProposals = null;
- if (proposalProvider != null) {
-
- IDocument document = viewer.getDocument();
+ IDocument document = viewer.getDocument();
- if (document instanceof IXtextDocument) {
+ if (document instanceof IXtextDocument) {
- List<ICompletionProposal> completionProposalList = new ArrayList<ICompletionProposal>();
+ List<ICompletionProposal> completionProposalList = new ArrayList<ICompletionProposal>();
- IXtextDocument xtextDocument = (IXtextDocument) document;
+ IXtextDocument xtextDocument = (IXtextDocument) document;
- CompositeNode rootNode = xtextDocument.readOnly(new UnitOfWork<CompositeNode>() {
- public CompositeNode exec(XtextResource resource) throws Exception {
- IParseResult parseResult = resource.getParseResult();
- Assert.isNotNull(parseResult);
- return parseResult.getRootNode();
- }
- });
-
- Assert.isNotNull(rootNode);
+ CompositeNode rootNode = xtextDocument.readOnly(new UnitOfWork<CompositeNode>() {
+ public CompositeNode exec(XtextResource resource) throws Exception {
+ IParseResult parseResult = resource.getParseResult();
+ Assert.isNotNull(parseResult);
+ return parseResult.getRootNode();
+ }
+ });
- AbstractNode lastCompleteNode = ParseTreeUtil.getLastCompleteNodeByOffset(rootNode, offset);
+ Assert.isNotNull(rootNode);
- LeafNode currentLeafNode = ParseTreeUtil.getCurrentNodeByOffset(rootNode, offset);
+ AbstractNode lastCompleteNode = ParseTreeUtil.getLastCompleteNodeByOffset(rootNode, offset);
- String prefix = calculatePrefix(viewer, offset, currentLeafNode);
+ AbstractNode currentNode = ParseTreeUtil.getCurrentNodeByOffset(rootNode, offset);
- EObject model = lastCompleteNode instanceof AbstractNode ? NodeUtil
- .getNearestSemanticObject((AbstractNode) lastCompleteNode) : lastCompleteNode;
+ String prefix = calculatePrefix(viewer, offset, currentNode);
- Set<AbstractElement> nextValidElementSet = new LinkedHashSet<AbstractElement>();
- /**
- * in case of a crossreference which isnt linked already we evaluate it again and delegate to
- * proposalProvider (again)
- */
- if (lastCompleteNode.getGrammarElement() instanceof CrossReference && !isLinked(lastCompleteNode)) {
- nextValidElementSet.add((AbstractElement) lastCompleteNode.getGrammarElement());
+ EObject model = lastCompleteNode instanceof AbstractNode ? NodeUtil
+ .getNearestSemanticObject((AbstractNode) lastCompleteNode) : lastCompleteNode;
+
+ addOrReplaceCaContextAdapter(model, new ContentAssistContextAdapter(rootNode,currentNode,lastCompleteNode,offset,prefix));
+
+ Set<AbstractElement> nextValidElementSet = new LinkedHashSet<AbstractElement>();
+ /**
+ * in case of a crossreference which isnt linked properly we evaluate or propose it again
+ */
+ if (lastCompleteNode.getGrammarElement() instanceof CrossReference && !isLinked(lastCompleteNode)) {
+ nextValidElementSet.add(getAbstractElement(lastCompleteNode));
+ nextValidElementSet.addAll(ParseTreeUtil.getElementSetValidFromOffset(rootNode, lastCompleteNode,
+ offset));
+ }
+ /**
+ * 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)
+ */
+ else if (lastCompleteNode == currentNode) {
+
+ Assignment containingAssignment = GrammarUtil
+ .containingAssignment(lastCompleteNode.getGrammarElement());
+
+ if (lastCompleteNode.getGrammarElement() instanceof RuleCall && containingAssignment != null) {
+ nextValidElementSet.add(containingAssignment);
nextValidElementSet.addAll(ParseTreeUtil.getElementSetValidFromOffset(rootNode, lastCompleteNode,
offset));
}
- /**
- * 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)
- */
- else if (currentLeafNode == lastCompleteNode) {
- Assignment containingAssignment = GrammarUtil.containingAssignment(lastCompleteNode.getGrammarElement());
-
- if (lastCompleteNode.getGrammarElement() instanceof RuleCall && containingAssignment!=null) {
- nextValidElementSet.add(containingAssignment);
- nextValidElementSet.addAll(ParseTreeUtil.getElementSetValidFromOffset(rootNode, lastCompleteNode, offset));
- } else {
- nextValidElementSet = ParseTreeUtil.getElementSetValidFromOffset(rootNode, lastCompleteNode, offset);
- nextValidElementSet.add((AbstractElement) lastCompleteNode.getGrammarElement());
- }
- }
else {
- nextValidElementSet = ParseTreeUtil
- .getElementSetValidFromOffset(rootNode, lastCompleteNode, offset);
+ nextValidElementSet = ParseTreeUtil.getElementSetValidFromOffset(rootNode, lastCompleteNode, offset);
+ nextValidElementSet.add(getAbstractElement(lastCompleteNode));
}
+ }
+ else {
+ nextValidElementSet = ParseTreeUtil.getElementSetValidFromOffset(rootNode, lastCompleteNode, offset);
+ }
- ProposalProviderInvokerSwitch proposalProviderInvokerSwitch = new ProposalProviderInvokerSwitch(model,
- document, offset, prefix, proposalProvider);
+ ProposalProviderInvokerSwitch proposalProviderInvokerSwitch = new ProposalProviderInvokerSwitch(model,
+ document, offset, prefix, proposalProvider);
- for (List<EObject> resolvedElementOrRuleList : new ProposalCandidateResolverSwitch(nextValidElementSet)) {
+ for (List<EObject> resolvedElementOrRuleList : new ProposalCandidateResolverSwitch(nextValidElementSet)) {
- List<ICompletionProposal> collectedCompletionProposalList = proposalProviderInvokerSwitch
- .collectCompletionProposalList(resolvedElementOrRuleList);
+ List<ICompletionProposal> collectedCompletionProposalList = proposalProviderInvokerSwitch
+ .collectCompletionProposalList(resolvedElementOrRuleList);
- completionProposalList.addAll(collectedCompletionProposalList);
- }
- if (completionProposalList != null) {
- List<? extends ICompletionProposal> processedCompletionProposalList = proposalProvider
- .sortAndFilter(completionProposalList, model, prefix, document, offset);
- completionProposals = processedCompletionProposalList.toArray(new ICompletionProposal[] {});
- }
+ completionProposalList.addAll(collectedCompletionProposalList);
+ }
+
+ if (completionProposalList != null) {
+ List<? extends ICompletionProposal> processedCompletionProposalList = proposalProvider.sortAndFilter(
+ completionProposalList, model, prefix, document, offset);
+ completionProposals = processedCompletionProposalList.toArray(
+ new ICompletionProposal[processedCompletionProposalList.size()]);
}
+
}
return completionProposals;
}
- protected String calculatePrefix(ITextViewer viewer, final int offset, LeafNode currentLeafNode) {
+
+ public char[] getCompletionProposalAutoActivationCharacters() {
+ return null;
+ }
+
+ public String getErrorMessage() {
+ return null;
+ }
+
+ public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
+ return null;
+ }
- if (currentLeafNode == null)
+ public char[] getContextInformationAutoActivationCharacters() {
+ return null;
+ }
+
+ public IContextInformationValidator getContextInformationValidator() {
+ return new ContextInformationValidator(this);
+ }
+
+ protected String calculatePrefix(ITextViewer viewer, final int offset, AbstractNode abstractNode) {
+
+ if (abstractNode == null)
return "";
String prefix = "";
StyledText textWidget = viewer.getTextWidget();
if (textWidget.getCharCount() > 0) {
int boundedOffset = Math.min(offset, textWidget.getCharCount()) - 1;
- if (currentLeafNode.getTotalOffset() <= boundedOffset)
- prefix = textWidget.getText(currentLeafNode.getTotalOffset(), boundedOffset);
+ if (abstractNode.getTotalOffset() <= boundedOffset)
+ prefix = textWidget.getText(abstractNode.getTotalOffset(), boundedOffset);
}
// if cursor is behind a complete keyword, accept any input => empty
@@ -163,36 +187,29 @@ public class DefaultContentAssistProcessor implements IContentAssistProcessor {
// 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
- if (currentLeafNode.getGrammarElement() instanceof Keyword && currentLeafNode.getText().equals(prefix)) {
+ if (abstractNode.getGrammarElement() instanceof Keyword && (abstractNode instanceof LeafNode && ((LeafNode)abstractNode).getText().equals(prefix))) {
prefix = "";
}
return prefix;
}
- public char[] getCompletionProposalAutoActivationCharacters() {
- return null;
- }
-
- public String getErrorMessage() {
- return null;
- }
+
+ private void addOrReplaceCaContextAdapter(EObject model, ContentAssistContextAdapter contentAssistContextAdapter) {
+
+ if (model != null) {
- public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
- return null;
- }
+ Adapter existingAdapter = EcoreUtil.getAdapter(model.eAdapters(), ContentAssistContextAdapter.class);
- public char[] getContextInformationAutoActivationCharacters() {
- return null;
- }
+ if (existingAdapter != null) {
+ model.eAdapters().remove(existingAdapter);
+ }
- public IContextInformationValidator getContextInformationValidator() {
- return new ContextInformationValidator(this);
+ model.eAdapters().add(contentAssistContextAdapter);
+ }
}
- @Inject
- private ILinkingService linkingService;
-
+
private boolean isLinked(AbstractNode lastCompleteNode) {
EObject semanticModel = NodeUtil.getNearestSemanticObject(lastCompleteNode);
CrossReference crossReference = (CrossReference) lastCompleteNode.getGrammarElement();
@@ -208,5 +225,10 @@ public class DefaultContentAssistProcessor implements IContentAssistProcessor {
return !linkCandidates.isEmpty() && referencedObjects.containsAll(linkCandidates);
}
}
+
+ private AbstractElement getAbstractElement(AbstractNode lastCompleteNode) {
+ return (AbstractElement) (lastCompleteNode.getGrammarElement() instanceof ParserRule ?
+ ((ParserRule)lastCompleteNode.getGrammarElement()).getAlternatives(): lastCompleteNode.getGrammarElement());
+ }
}
diff --git a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/XtextCompletionProposal.java b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/XtextCompletionProposal.java
index 58dc0d1..1e3228a 100644
--- a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/XtextCompletionProposal.java
+++ b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/XtextCompletionProposal.java
@@ -3,6 +3,7 @@ package org.eclipse.xtext.ui.common.editor.codecompletion;
import org.apache.log4j.Logger;
import org.eclipse.core.runtime.Assert;
import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentEvent;
@@ -20,11 +21,10 @@ import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.Assignment;
import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.GrammarUtil;
+import org.eclipse.xtext.Keyword;
import org.eclipse.xtext.RuleCall;
-import org.eclipse.xtext.parsetree.CompositeNode;
+import org.eclipse.xtext.parsetree.AbstractNode;
import org.eclipse.xtext.parsetree.LeafNode;
-import org.eclipse.xtext.parsetree.NodeUtil;
-import org.eclipse.xtext.parsetree.ParseTreeUtil;
/**
* Default Xtext implementation of interface <code>ICompletionProposal</code>.
@@ -54,19 +54,13 @@ public class XtextCompletionProposal implements ICompletionProposal,
* @param element
* the element for which this CompletionProposal is created for
* @param model the last semtantically complete object
- * @param text
- * the text value to be replaced/inserted
- * @param label
- * the label to be displayed
- * @param description
- * some additional description for the tooltip
- * @param imageFilePath
- * the relative path of the image file, relative to the root of
+ * @param text the text value to be replaced/inserted
+ * @param label the label to be displayed
+ * @param description some additional description for the tooltip
+ * @param imageFilePath the relative path of the image file, relative to the root of
* the plug-in; the path must be legal
- * @param pluginId
- * the id of the plug-in containing the image file;
- * @param offset
- * the offset of the text
+ * @param pluginId the id of the plug-in containing the image file;
+ * @param offset the offset of the text
*/
public XtextCompletionProposal(AbstractElement element,EObject model,String text, StyledString label,
String description, String imageFilePath, String pluginIdentifier,
@@ -217,47 +211,38 @@ public class XtextCompletionProposal implements ICompletionProposal,
IDocument document = viewer.getDocument();
- int offsetToApply = this.offset;
-
if (model != null) {
+ ContentAssistContextAdapter contentAssistContextAdapter = getContextAdapater();
- CompositeNode parserNode = NodeUtil.getRootNode(model);
-
- LeafNode currentLeafNode=ParseTreeUtil.getCurrentNodeByOffset(parserNode, offset);
-
- boolean isCursorAtTheEndOfTheLastElement = offset == (currentLeafNode.getTotalOffset() + currentLeafNode
- .getTotalLength());
+ AbstractNode abstractNode = contentAssistContextAdapter.getCurrentNode();
- if ((currentLeafNode.isHidden() && !"".equals(currentLeafNode.getText().trim()))
- || isCursorAtTheEndOfTheLastElement) {
+ if (abstractNode instanceof LeafNode) {
+
+ LeafNode currentLeafNode = (LeafNode) abstractNode;
+
if (getDisplayString().toUpperCase().startsWith(currentLeafNode.getText().toUpperCase())) {
- offsetToApply-=currentLeafNode.getText().trim().length();
- }
- }
-
- if (!currentLeafNode.isHidden() &&
- isCursorAtTheEndOfTheLastElement &&
- offsetToApply==offset) {
+ setText(getText().substring(this.offset - currentLeafNode.getTotalOffset()));
+ } else if (contentAssistContextAdapter.isCusorAtEndOfLastCompleteNode()) {
- if (currentLeafNode.getGrammarElement() instanceof CrossReference
- && abstractElement instanceof CrossReference) {
- setText(" " + getText());
+ if (currentLeafNode.getGrammarElement() instanceof CrossReference
+ && abstractElement instanceof CrossReference) {
+ setText(" " + getText());
+ }
+ else if (currentLeafNode.getGrammarElement() instanceof RuleCall
+ && currentLeafNode.getGrammarElement().eContainer() instanceof Assignment
+ && abstractElement instanceof Assignment) {
+ setText(" " + getText());
+ }
+ else if (!GrammarUtil.containingParserRule(abstractElement).equals(
+ GrammarUtil.containingParserRule(currentLeafNode.getGrammarElement()))) {
+ setText(" " + getText());
+ }
}
- else if (currentLeafNode.getGrammarElement() instanceof RuleCall
- && currentLeafNode.getGrammarElement().eContainer() instanceof Assignment
- && abstractElement instanceof Assignment) {
- setText(" " + getText());
- }
- else if (!GrammarUtil.containingParserRule(abstractElement).equals(GrammarUtil.containingParserRule(currentLeafNode.getGrammarElement()))) {
- setText(" " + getText());
- }
-
- }
+ }
}
- document.replace(offsetToApply, offset != offsetToApply ? offset
- - offsetToApply : 0, getText());
+ document.replace(this.offset, document.getLength()<(this.offset+getText().length()) ? 0:getText().length() , getText());
} catch (BadLocationException e) {
logger.error(e);
@@ -325,6 +310,50 @@ public class XtextCompletionProposal implements ICompletionProposal,
return new Point(offset + this.text.length(), 0);
}
+ /**
+ *
+ * @param prefix to match
+ * @return true or false whether the given prefix matches the text of this completion proposal
+ */
+ public boolean matches(String prefix) {
+
+ boolean matches = true;
+
+ if (model != null) {
+
+ AbstractElement abstractElement = null;
+
+ if (getAbstractElement() instanceof Keyword ||
+ getAbstractElement() instanceof CrossReference) {
+ abstractElement = GrammarUtil.containingAssignment(getAbstractElement());
+ }
+
+ if (null==abstractElement) {
+ abstractElement = getAbstractElement();
+ }
+
+ ContentAssistContextAdapter contentAssistContextAdapter = getContextAdapater();
+
+ 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 (contentAssistContextAdapter.isCusorAtEndOfLastCompleteNode() &&
+ abstractElement.equals(contentAssistContextAdapter.getCurrentGrammarElement())) {
+ candidateToCompare = true;
+ } else if (!contentAssistContextAdapter.isCusorAtEndOfLastCompleteNode() ) {
+ candidateToCompare = true;
+ }
+
+ if ( candidateToCompare && (!"".equals(prefix.trim()) &&
+ !getDisplayString().toUpperCase().trim().startsWith(prefix.toUpperCase().trim()))) {
+ matches = false;
+ }
+ }
+
+
+ return matches;
+ }
+
@Override
public String toString() {
return "XtextCompletionPoposal[text='"+getText()+"']";
@@ -344,4 +373,10 @@ public class XtextCompletionProposal implements ICompletionProposal,
// }
this.image = newImage;
}
+
+ private ContentAssistContextAdapter getContextAdapater() {
+ ContentAssistContextAdapter contentAssistContextAdapter = (ContentAssistContextAdapter)
+ EcoreUtil.getAdapter(model.eAdapters(), ContentAssistContextAdapter.class);
+ return contentAssistContextAdapter;
+ }
}
diff --git a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/parsetree/ParseTreeUtilTest.java b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/parsetree/ParseTreeUtilTest.java
index 73875c1..cf75fe1 100644
--- a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/parsetree/ParseTreeUtilTest.java
+++ b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/parsetree/ParseTreeUtilTest.java
@@ -61,27 +61,27 @@ public class ParseTreeUtilTest extends AbstractGeneratorTest {
String text = "spielplatz 1 \"junit\" { kin ";
CompositeNode rootNode = getRootNode(text);
- LeafNode currentNodeByOffset = ParseTreeUtil.getCurrentNodeByOffset(rootNode, 26);
+ LeafNode currentNodeByOffset = (LeafNode) ParseTreeUtil.getCurrentNodeByOffset(rootNode, 26);
assertEquals("expect leafnode with text 'kin'", currentNodeByOffset.getText(), "kin");
text = "spielplatz 1 \"junit\" { kind (";
rootNode = getRootNode(text);
- currentNodeByOffset = ParseTreeUtil.getCurrentNodeByOffset(rootNode, text.length());
+ currentNodeByOffset = (LeafNode) ParseTreeUtil.getCurrentNodeByOffset(rootNode, text.length());
assertEquals("expect leafnode with text '('", currentNodeByOffset.getText(), "(");
text = "spielplatz 1 \"junit\" { kind ( ";
rootNode = getRootNode(text);
- currentNodeByOffset = ParseTreeUtil.getCurrentNodeByOffset(rootNode, text.length());
+ currentNodeByOffset = (LeafNode) ParseTreeUtil.getCurrentNodeByOffset(rootNode, text.length());
assertEquals("expect leafnode with WS text '__'", currentNodeByOffset.getText(), " ");
text = "spielplatz 1 \"junit\" { kind (";
rootNode = getRootNode(text);
- currentNodeByOffset = ParseTreeUtil.getCurrentNodeByOffset(rootNode, text.length() - 1);
- assertEquals("expect leafnode with WS text '_'", currentNodeByOffset.getText(), " ");
+ currentNodeByOffset = (LeafNode) ParseTreeUtil.getCurrentNodeByOffset(rootNode, text.length() - 1);
+ assertEquals("expect leafnode with '('", currentNodeByOffset.getText(), "(");
text = "spielplatz 1 \"junit\" { kind (";
rootNode = getRootNode(text);
- currentNodeByOffset = ParseTreeUtil.getCurrentNodeByOffset(rootNode, 0);
+ currentNodeByOffset = (LeafNode) ParseTreeUtil.getCurrentNodeByOffset(rootNode, 0);
assertEquals("expect leafnode with WS text 'spielplatz'", currentNodeByOffset.getText(), "spielplatz");
}
diff --git a/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/editor/outline/OutlineViewTest.java b/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/editor/outline/OutlineViewTest.java
index 99a955a..ababcdc 100644
--- a/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/editor/outline/OutlineViewTest.java
+++ b/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/editor/outline/OutlineViewTest.java
@@ -28,6 +28,7 @@ import org.eclipse.ui.views.contentoutline.ContentOutline;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.eclipse.xtext.EcoreUtil2;
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.ParseTreeUtil;
@@ -177,7 +178,7 @@ public class OutlineViewTest extends AbstractEditorTest {
assertSynchronized(editor, 2, 45, 0);
}
- protected LeafNode getCurrentEditorNode() {
+ protected AbstractNode getCurrentEditorNode() {
XtextDocument document = (XtextDocument) editor.getDocument();
ITextSelection selection = (ITextSelection) editor.getSelectionProvider().getSelection();
@@ -195,8 +196,7 @@ public class OutlineViewTest extends AbstractEditorTest {
});
Assert.isNotNull(rootNode);
- LeafNode currentNodeByOffset = ParseTreeUtil.getCurrentNodeByOffset(rootNode, offset);
- return currentNodeByOffset;
+ return ParseTreeUtil.getCurrentNodeByOffset(rootNode, offset);
}
protected void assertSynchronized(XtextEditor editor, int elementIndex, int offset, int length) {
@@ -239,9 +239,12 @@ public class OutlineViewTest extends AbstractEditorTest {
EObject objInEditor = contents.get(elementIndex);
// just debugging purposes
- LeafNode currentEditorNode = getCurrentEditorNode();
- System.out.println("Selection [" + offset + ";" + length + "] yields node text ["
- + currentEditorNode.getText() + "]");
+ AbstractNode currentEditorNode = getCurrentEditorNode();
+ if (currentEditorNode instanceof LeafNode) {
+ System.out.println("Selection [" + offset + ";" + length + "] yields node text ["
+ + ((LeafNode)currentEditorNode).getText() + "]");
+ }
+
// obtain selected model element in outline
EObject objInOutline = resource.getEObject(uri.fragment());