| author | mclay | 2008-11-09 16:18:16 (EST) |
|---|---|---|
| committer | sefftinge | 2008-11-09 16:18:16 (EST) |
| commit | 5e99a1ca7f3c64102c0c83ac25df7914b375302f (patch) (side-by-side diff) | |
| tree | 4cf9ab218f142ceb90a6062b88f7d601f61a0dca | |
| parent | 0dcacba2b3c50df06b552d01fdc6314c698c72c6 (diff) | |
| download | org.eclipse.xtext-5e99a1ca7f3c64102c0c83ac25df7914b375302f.zip org.eclipse.xtext-5e99a1ca7f3c64102c0c83ac25df7914b375302f.tar.gz org.eclipse.xtext-5e99a1ca7f3c64102c0c83ac25df7914b375302f.tar.bz2 | |
fix: some NPE's
6 files changed, 122 insertions, 98 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 ec42692..609579e 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 @@ -24,8 +24,10 @@ import org.eclipse.xtext.ParserRule; import org.eclipse.xtext.RuleCall; import org.eclipse.xtext.crossref.ILinkingService; 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.service.Inject; import org.eclipse.xtext.util.Pair; @@ -82,9 +84,10 @@ public abstract class AbstractProposalProvider implements IProposalProvider { return Collections.emptyList(); } + /* * (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) + * @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, @@ -173,10 +176,8 @@ public abstract class AbstractProposalProvider implements IProposalProvider { List<ICompletionProposal> completionProposalList = new ArrayList<ICompletionProposal>(); if (linkingService != null) { - EObject semanticModel = model instanceof AbstractNode ? NodeUtil - .getNearestSemanticObject((AbstractNode) model) : model; List<Pair<String, URI>> candidates = linkingService - .getLinkCandidates(semanticModel, crossReference, prefix); + .getLinkCandidates(model, crossReference, prefix); for (Pair<String, URI> candidate : candidates) { completionProposalList.add(createCompletionProposal(crossReference, model, candidate.getFirstElement(), offset)); @@ -201,85 +202,60 @@ public abstract class AbstractProposalProvider implements IProposalProvider { * behavior. Gets called after all completion proposals have been collected. * * The default behavior of this implementation is to sort duplicates and to - * trim matching <code>ICompletionProposal#displayString</code> with - * matching prefix values. - * - * @param completionProposalList - * matching {@link ICompletionProposal} to sort and filter - * @param model - * - the most specific model element under the cursor. - * @param prefix - * - the prefix under the cursor or null if there is no prefix - * @param offset - * @param document - * @return the sorted and filtered <code>ICompletionProposal</code> list. + * trim matching <code>ICompletionProposal#displayString</code> with matching prefix values. * + * @see #sortAndFilter(List, EObject, String, IDocument, int, AbstractNode, LeafNode) */ protected List<? extends ICompletionProposal> doSortAndFilter( - List<? extends ICompletionProposal> completionProposalList, EObject model, String prefix, - IDocument document, int offset) { - - if (model instanceof LeafNode) { - - LeafNode leafNode = (LeafNode) model; + List<? extends ICompletionProposal> completionProposalList, EObject model, String prefix,IDocument document, int offset) { - Map<String, ICompletionProposal> displayString2ICompletionProposalMap = new HashMap<String, ICompletionProposal>(); + Map<String, ICompletionProposal> displayString2ICompletionProposalMap = new HashMap<String, ICompletionProposal>(); + + for (Iterator<? extends ICompletionProposal> iterator = completionProposalList.iterator(); iterator.hasNext();) { - for (Iterator<? extends ICompletionProposal> iterator = completionProposalList.iterator(); iterator - .hasNext();) { + ICompletionProposal completionProposal = iterator.next(); + + // filter duplicate displayString + if (!displayString2ICompletionProposalMap.containsKey(completionProposal.getDisplayString())) { - ICompletionProposal completionProposal = iterator.next(); + displayString2ICompletionProposalMap.put(completionProposal.getDisplayString(), completionProposal); + + if (model != null) { + + CompositeNode parserNode = NodeUtil.getRootNode(model); + + LeafNode currentLeafNode=ParseTreeUtil.getCurrentNodeByOffset(parserNode, offset); - // filter duplicate displayString - if (!displayString2ICompletionProposalMap.containsKey(completionProposal.getDisplayString())) { - - displayString2ICompletionProposalMap.put(completionProposal.getDisplayString(), completionProposal); - - boolean cursorIsAtTheEndOfTheLastElement = offset == (leafNode.getOffset() + leafNode.getLength()); - /** - * 1. filter and trim proposal's matching the prefix of the - * current leafNode (e.g. leafNode 'kin' for keyword 'kind' - * will be trimmed to 'd' , displayString stays the same) - * <p/> - * 2. special case for crossref: if the cursor is at the end - * of the previous element kind>|< we want to apply the - * filter only to instances of the previous grammarElement - * in order to always show all matches for 'right-to-left' - * backtracking use-cases - */ - if ((leafNode.isHidden() || cursorIsAtTheEndOfTheLastElement) - && !"".equals(leafNode.getText().trim()) - && completionProposal instanceof XtextCompletionProposal) { + boolean isCursorAtTheEndOfTheLastElement = offset == (currentLeafNode.getOffset() + currentLeafNode.getLength()); + + if (isCursorAtTheEndOfTheLastElement && completionProposal instanceof XtextCompletionProposal) { XtextCompletionProposal xtextCompletionProposal = (XtextCompletionProposal) completionProposal; AbstractElement abstractElement = xtextCompletionProposal.getAbstractElement(); - EObject grammarElement = leafNode.getGrammarElement(); - // at the end of the last element we want to filter only - // the CompletionProposal for the same grammar element - if (completionProposal.getDisplayString().startsWith(leafNode.getText())) { - xtextCompletionProposal.setText(xtextCompletionProposal.getText().substring( - leafNode.getText().length())); - } - else if ((cursorIsAtTheEndOfTheLastElement && abstractElement.equals(grammarElement)) - || !cursorIsAtTheEndOfTheLastElement) { + EObject grammarElement = currentLeafNode.getGrammarElement(); + // at the end of the last element we want to filter only the CompletionProposal for the same grammar element + if (((isCursorAtTheEndOfTheLastElement && abstractElement.equals(grammarElement)) + || !isCursorAtTheEndOfTheLastElement) && !completionProposal.getDisplayString().startsWith(currentLeafNode.getText())) { if (logger.isDebugEnabled()) { logger.debug("filter completionProposal '" + completionProposal + "'"); } iterator.remove(); } - } + } } - else { - if (logger.isDebugEnabled()) { - logger.debug("filter duplicate completionProposal '" + completionProposal + "'"); - } - - iterator.remove(); + + } + else { + if (logger.isDebugEnabled()) { + logger.debug("filter duplicate completionProposal '" + completionProposal + "'"); } + + iterator.remove(); } } + return completionProposalList; } 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 3a31dc3..5f7ba5e 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 @@ -82,37 +82,48 @@ public class DefaultContentAssistProcessor implements IContentAssistProcessor { String prefix = null==currentLeafNode ? "" : currentLeafNode.getText(); + EObject model = lastCompleteNode instanceof AbstractNode ? NodeUtil + .getNearestSemanticObject((AbstractNode) lastCompleteNode) : lastCompleteNode; + Set<AbstractElement> nextValidElementSet = new LinkedHashSet<AbstractElement>(); /** - * in the case of a non linked crossreference we delegate to proposalProvider (again) + * 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()); + nextValidElementSet.addAll(ParseTreeUtil.getElementSetValidFromOffset(rootNode,lastCompleteNode, offset)); } /** - * in the case we are at the end of the completed previous element add for 'right-to-left' backtracking cases + * 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) { + nextValidElementSet = ParseTreeUtil.getElementSetValidFromOffset(rootNode,lastCompleteNode, offset); nextValidElementSet.add((AbstractElement) lastCompleteNode.getGrammarElement()); + } else { + nextValidElementSet = ParseTreeUtil.getElementSetValidFromOffset(rootNode,lastCompleteNode, offset); } - nextValidElementSet.addAll(ParseTreeUtil.getElementSetValidFromOffset(rootNode,lastCompleteNode, offset)); - ProposalProviderInvokerSwitch proposalProviderInvokerSwitch = new ProposalProviderInvokerSwitch( - lastCompleteNode, document, offset, prefix, proposalProvider); + model, document, offset, prefix, proposalProvider); - for (List<EObject> resolvedElementOrRuleList : new ProposalCandidateResolverSwitch(nextValidElementSet)) { + + try { + for (List<EObject> resolvedElementOrRuleList : new ProposalCandidateResolverSwitch( + nextValidElementSet)) { - List<ICompletionProposal> collectedCompletionProposalList = - proposalProviderInvokerSwitch.collectCompletionProposalList(resolvedElementOrRuleList); - - completionProposalList.addAll(collectedCompletionProposalList); - } + List<ICompletionProposal> collectedCompletionProposalList = proposalProviderInvokerSwitch + .collectCompletionProposalList(resolvedElementOrRuleList); - if (completionProposalList != null) { - List<? extends ICompletionProposal> sortAndFilter = proposalProvider.sortAndFilter(completionProposalList,currentLeafNode,prefix,document,offset); - completionProposals = sortAndFilter.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[] {}); + } + } + catch (Exception e) { + e.printStackTrace(); } } } diff --git a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/IProposalProvider.java b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/IProposalProvider.java index 88db9a5..c4d35f9 100644 --- a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/IProposalProvider.java +++ b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/IProposalProvider.java @@ -71,6 +71,9 @@ public interface IProposalProvider { * This method is invoked by the framework after all possible completions have been collected. * @param completionProposalList matching {@link ICompletionProposal} to sort and filter * @param model - the most specific model element under the cursor. + * @param prefix - the prefix under the cursor or null if there is no prefix + * @param document - the IDocument instance used by the editor + * @param offset - an offset within the document for which completions should be computed * @return the sorted and filtered <code>ICompletionProposal</code> list. * */ diff --git a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/ProposalCandidateResolverSwitch.java b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/ProposalCandidateResolverSwitch.java index b3e31ea..d309adf 100644 --- a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/ProposalCandidateResolverSwitch.java +++ b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/ProposalCandidateResolverSwitch.java @@ -27,8 +27,9 @@ import org.eclipse.xtext.RuleCall; import org.eclipse.xtext.util.XtextSwitch; /** - * Represents a <b>Switch</b> for the Xtext model's inheritance hierarchy to resolve - * potential completion proposal candidate's. + * Represents a <b>Switch</b> for the Xtext model's inheritance hierarchy to + * resolve (or flatten) 'container' level elements like <code>Group</code> or + * <code>Alternatives</code> to gather potential completion proposal candidates. * * It supports the call {@link #doSwitch(EObject) doSwitch(object)} instead of * typical <code>if (x instanceof y)</code> code blocks. diff --git a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/ProposalProviderInvokerSwitch.java b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/ProposalProviderInvokerSwitch.java index b12e918..ad32773 100644 --- a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/ProposalProviderInvokerSwitch.java +++ b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/ProposalProviderInvokerSwitch.java @@ -28,8 +28,6 @@ import org.eclipse.xtext.Keyword; import org.eclipse.xtext.ParserRule; import org.eclipse.xtext.RuleCall; import org.eclipse.xtext.TypeRef; -import org.eclipse.xtext.parsetree.AbstractNode; -import org.eclipse.xtext.parsetree.CompositeNode; import org.eclipse.xtext.util.XtextSwitch; /** @@ -53,7 +51,7 @@ public class ProposalProviderInvokerSwitch extends XtextSwitch<List<ICompletionP private final IDocument document; - private final AbstractNode currentLeafNode; + private final EObject model; private final String prefix; @@ -63,8 +61,7 @@ public class ProposalProviderInvokerSwitch extends XtextSwitch<List<ICompletionP /** * - * @param currentLeafNode - * the last node in the document + * @param model the most specific model element under the cursor. * @param document * the document itself * @param offset @@ -75,10 +72,10 @@ public class ProposalProviderInvokerSwitch extends XtextSwitch<List<ICompletionP * @param proposalProvider * the proposalProvider to callback and invoke */ - public ProposalProviderInvokerSwitch(AbstractNode currentLeafNode, IDocument document, int offset, String prefix, + public ProposalProviderInvokerSwitch(EObject model, IDocument document, int offset, String prefix, IProposalProvider proposalProvider) { super(); - this.currentLeafNode = currentLeafNode; + this.model = model; this.document = document; this.offset = offset; this.prefix = prefix; @@ -112,12 +109,9 @@ public class ProposalProviderInvokerSwitch extends XtextSwitch<List<ICompletionP public List<ICompletionProposal> caseAssignment(Assignment assignment) { ParserRule parserRule = GrammarUtil.containingParserRule(assignment); - EObject model = null == ((CompositeNode) currentLeafNode.eContainer()).getElement() ? currentLeafNode - .eContainer() : ((CompositeNode) currentLeafNode.eContainer()).getElement(); - Method method = findMethod(proposalProvider.getClass(), "complete" + firstLetterCapitalized(parserRule.getName()) + firstLetterCapitalized(assignment.getFeature()), - Assignment.class, model.getClass(), String.class, document.getClass(), int.class); + Assignment.class, model==null? EObject.class : model.getClass(), String.class, document.getClass(), int.class); Collection<? extends ICompletionProposal> assignmentProposalList = null == method ? null : invokeMethod(method, proposalProvider, assignment, model, prefix, document, offset); @@ -131,18 +125,13 @@ public class ProposalProviderInvokerSwitch extends XtextSwitch<List<ICompletionP @Override public List<ICompletionProposal> caseKeyword(Keyword keyword) { - completionProposalList.addAll(proposalProvider.completeKeyword(keyword, currentLeafNode, prefix, document, - offset)); + completionProposalList.addAll(proposalProvider.completeKeyword(keyword, model, prefix, document,offset)); return null; } @Override public List<ICompletionProposal> caseRuleCall(RuleCall ruleCall) { - EObject model = null == ((CompositeNode) currentLeafNode.eContainer()).getElement() ? currentLeafNode - .eContainer() : ((CompositeNode) currentLeafNode.eContainer()).getElement(); - - List<? extends ICompletionProposal> ruleCallProposalList = this.proposalProvider.completeRuleCall(ruleCall, model, prefix, document, offset); @@ -158,7 +147,7 @@ public class ProposalProviderInvokerSwitch extends XtextSwitch<List<ICompletionP Method method = findMethod(proposalProvider.getClass(), "complete" + firstLetterCapitalized(typeRef.getAlias()) + firstLetterCapitalized(typeRef.getName()), - RuleCall.class, model.getClass(), String.class, document.getClass(), int.class); + RuleCall.class, model==null? EObject.class : model.getClass(), String.class, document.getClass(), int.class); Collection<? extends ICompletionProposal> proposalList = null == method ? null : invokeMethod(method, proposalProvider, ruleCall, model, prefix, document, offset); 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 ffeef6d..5567aaa 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 @@ -17,6 +17,14 @@ import org.eclipse.jface.viewers.StyledString; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.xtext.AbstractElement; +import org.eclipse.xtext.Assignment; +import org.eclipse.xtext.CrossReference; +import org.eclipse.xtext.GrammarUtil; +import org.eclipse.xtext.RuleCall; +import org.eclipse.xtext.parsetree.CompositeNode; +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>. @@ -206,10 +214,46 @@ public class XtextCompletionProposal implements ICompletionProposal, int offset) { try { + IDocument document = viewer.getDocument(); + + if (model != null) { + CompositeNode parserNode = NodeUtil.getRootNode(model); + + LeafNode currentLeafNode=ParseTreeUtil.getCurrentNodeByOffset(parserNode, offset); + + boolean isCursorAtTheEndOfTheLastElement = offset == (currentLeafNode.getOffset() + currentLeafNode + .getLength()); + + if ((currentLeafNode.isHidden() && !"".equals(currentLeafNode.getText().trim())) + || isCursorAtTheEndOfTheLastElement) { + if (getDisplayString().startsWith(currentLeafNode.getText())) { + setText(getText().substring(currentLeafNode.getText().length())); + } + } + + if (!currentLeafNode.isHidden() && + isCursorAtTheEndOfTheLastElement && + getDisplayString().equalsIgnoreCase(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()); + } + + } + } + document.replace(this.offset, offset != this.offset ? offset - - this.offset : 0, this.text); + - this.offset : 0, getText()); } catch (BadLocationException e) { logger.error(e); |

