| author | mclay | 2008-10-27 16:27:00 (EDT) |
|---|---|---|
| committer | sefftinge | 2008-10-27 16:27:00 (EDT) |
| commit | f52ea33709b087828a8e5b276ff40d7d47c1266c (patch) (side-by-side diff) | |
| tree | 8dabe327d51f8ace73446f01a9b975a08f592f65 | |
| parent | 900f70e80b70705eff710322a6c460d3df8cab2d (diff) | |
| download | org.eclipse.xtext-f52ea33709b087828a8e5b276ff40d7d47c1266c.zip org.eclipse.xtext-f52ea33709b087828a8e5b276ff40d7d47c1266c.tar.gz org.eclipse.xtext-f52ea33709b087828a8e5b276ff40d7d47c1266c.tar.bz2 | |
add: keep working on bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=250383 (Implementation of org.eclipse.xtext.ui.common.editor.codecompletion.DefaultContentAssistProcessor)
3 files changed, 414 insertions, 171 deletions
diff --git a/plugins/org.eclipse.xtext.common.ui/src/org/eclipse/xtext/ui/service/impl/BuiltInProposalsProvider.java b/plugins/org.eclipse.xtext.common.ui/src/org/eclipse/xtext/ui/service/impl/BuiltInProposalsProvider.java index 14143d2..0e109e6 100644 --- a/plugins/org.eclipse.xtext.common.ui/src/org/eclipse/xtext/ui/service/impl/BuiltInProposalsProvider.java +++ b/plugins/org.eclipse.xtext.common.ui/src/org/eclipse/xtext/ui/service/impl/BuiltInProposalsProvider.java @@ -40,7 +40,6 @@ import org.eclipse.xtext.parsetree.AbstractNode; import org.eclipse.xtext.parsetree.LeafNode; import org.eclipse.xtext.parsetree.ParseTreeUtil; import org.eclipse.xtext.ui.editor.model.IEditorModel; -import org.eclipse.xtext.ui.editor.model.XtextCompletionProposal; import org.eclipse.xtext.ui.util.GrammarConstants; /** @@ -287,22 +286,22 @@ public class BuiltInProposalsProvider extends AbstractProposalsProvider protected void processProposal(Proposal proposal, LeafNode currentLeafNode, int offset, List<ICompletionProposal> completionProposalList) { // filter proposals - if (currentLeafNode != null - && !"".equals(currentLeafNode.getText().trim()) - && (currentLeafNode.isHidden())) { - if (proposal.getText().startsWith(currentLeafNode.getText())) { - proposal.setText(proposal.getText().replaceFirst( - currentLeafNode.getText(), "")); - completionProposalList.add(new XtextCompletionProposal(proposal - .getText(), proposal.getLabel(), proposal - .getDescription(), proposal.getImage(), - this.namespaceIdentifier, offset)); - } - } else { - completionProposalList.add(new XtextCompletionProposal(proposal - .getText(), proposal.getLabel(), proposal.getDescription(), - proposal.getImage(), this.namespaceIdentifier, offset)); - } +// if (currentLeafNode != null +// && !"".equals(currentLeafNode.getText().trim()) +// && (currentLeafNode.isHidden())) { +// if (proposal.getText().startsWith(currentLeafNode.getText())) { +// proposal.setText(proposal.getText().replaceFirst( +// currentLeafNode.getText(), "")); +// completionProposalList.add(new XtextCompletionProposal(proposal +// .getText(), proposal.getLabel(), proposal +// .getDescription(), proposal.getImage(), +// this.namespaceIdentifier, offset)); +// } +// } else { +// completionProposalList.add(new XtextCompletionProposal(proposal +// .getText(), proposal.getLabel(), proposal.getDescription(), +// proposal.getImage(), this.namespaceIdentifier, offset)); +// } } protected final List<Proposal> toProposalList(AbstractNode rootNode, 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 5e5dc74..f47ab7b 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 @@ -5,9 +5,11 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; import org.eclipse.core.runtime.Assert; @@ -43,14 +45,16 @@ import org.eclipse.xtext.ui.core.editor.model.UnitOfWork; import org.eclipse.xtext.ui.core.editor.model.XtextDocument; /** + * @author Michael Clay - Initial contribution and API * @author Dennis Hübner - Initial contribution and API - * */ public class DefaultContentAssistProcessor implements IContentAssistProcessor { @Inject private IProposalProvider proposalProvider; + private final Map<String, Method> methodLookupMap = new HashMap<String, Method>(); + /** * computes the possible grammar elements following the one at the given * offset and calls the respective methods on the proposal provider. @@ -70,9 +74,9 @@ public class DefaultContentAssistProcessor implements IContentAssistProcessor { }); Assert.isNotNull(rootNode); - // last COMPLETE node element (node with associated grammar element) + // last COMPLETE node element with associated grammar element AbstractNode lastCompleteNode = ParseTreeUtil.getLastCompleteNodeByOffset(rootNode, offset); - // node at the CURRENT cursor position (node with or without grammar element) + // node at CURRENT cursor pos. with or without grammar element LeafNode currentLeafNode = (LeafNode) ParseTreeUtil.getCurrentNodeByOffset(rootNode, offset); // get associated grammar element AbstractElement grammarElement = ParseTreeUtil.getGrammarElementFromNode(lastCompleteNode); @@ -85,41 +89,10 @@ public class DefaultContentAssistProcessor implements IContentAssistProcessor { .iterator(); iterator.hasNext();) { AbstractElement nextElement = iterator.next(); - List<EObject> resolvedElementOrRuleList = resolveElement(nextElement); - - for (Iterator<EObject> elementOrRuleIterator = resolvedElementOrRuleList.iterator(); elementOrRuleIterator - .hasNext();) { - EObject abstractElement = elementOrRuleIterator.next(); - - if (abstractElement instanceof Keyword) { - completionProposalList.addAll(proposalProvider.completeKeyword((Keyword) abstractElement, - currentLeafNode, prefix, xtextDocument, offset)); - } - else if (abstractElement instanceof Assignment) { - - Assignment assignment = (Assignment) abstractElement; - - ParserRule lexerRule = GrammarUtil.containingParserRule(assignment); - - Method method = findMethod(proposalProvider.getClass(), "complete" - + firstLetterCapitalized(lexerRule.getName()) - + firstLetterCapitalized(assignment.getFeature()), Assignment.class, EObject.class, - String.class, IDocument.class, int.class); - - Collection<? extends ICompletionProposal> assignmentProposalList = (Collection<? extends ICompletionProposal>) invokeMethod(method, proposalProvider, assignment, currentLeafNode, prefix, xtextDocument, - offset); - - completionProposalList.addAll(assignmentProposalList); - } - else if (abstractElement instanceof LexerRule) { - } - else if (abstractElement instanceof RuleCall) { - } - else if (abstractElement instanceof CrossReference) { - } - else if (abstractElement instanceof Action) { - } - } + List<EObject> resolvedElementOrRuleList = resolveElement(nextElement, true); + + collectCompletionProposalList(resolvedElementOrRuleList, completionProposalList, xtextDocument, + currentLeafNode, prefix, offset); } if (completionProposalList != null) { @@ -132,6 +105,155 @@ public class DefaultContentAssistProcessor implements IContentAssistProcessor { return null; } + private void collectCompletionProposalList(List<EObject> resolvedElementOrRuleList, + List<ICompletionProposal> completionProposalList, XtextDocument xtextDocument, LeafNode currentLeafNode, + String prefix, final int offset) { + for (Iterator<EObject> elementOrRuleIterator = resolvedElementOrRuleList.iterator(); elementOrRuleIterator + .hasNext();) { + EObject abstractElement = elementOrRuleIterator.next(); + + if (abstractElement instanceof Keyword) { + completionProposalList.addAll(proposalProvider.completeKeyword((Keyword) abstractElement, + currentLeafNode, prefix, xtextDocument, offset)); + } + else if (abstractElement instanceof Assignment) { + + Assignment assignment = (Assignment) abstractElement; + + ParserRule lexerRule = GrammarUtil.containingParserRule(assignment); + + Method method = findMethod(proposalProvider.getClass(), + "complete" + firstLetterCapitalized(lexerRule.getName()) + + firstLetterCapitalized(assignment.getFeature()), Assignment.class, EObject.class, + String.class, IDocument.class, int.class); + + Collection<ICompletionProposal> assignmentProposalList = invokeMethod(method, proposalProvider, + assignment, currentLeafNode, prefix, xtextDocument, offset); + + completionProposalList.addAll(assignmentProposalList); + } + else if (abstractElement instanceof RuleCall) { + List<ICompletionProposal> proposalList = this.proposalProvider.completeRuleCall( + (RuleCall) abstractElement, currentLeafNode, prefix, xtextDocument); + if (null == proposalList || proposalList.isEmpty()) { + collectCompletionProposalList( + resolveElement((AbstractElement) abstractElement.eContainer(), false), + completionProposalList, xtextDocument, currentLeafNode, prefix, offset); + } + } + else if (abstractElement instanceof LexerRule) { + } + else if (abstractElement instanceof CrossReference) { + } + else if (abstractElement instanceof Action) { + } + } + } + + protected final List<EObject> resolveElement(AbstractElement abstractElement, boolean resolveRuleCall) { + + List<EObject> elementList = new ArrayList<EObject>(); + + if (abstractElement instanceof Alternatives) { + for (AbstractElement alternativeElement : ((Alternatives) abstractElement).getGroups()) { + elementList.addAll(resolveElement(alternativeElement, resolveRuleCall)); + } + } + else if (abstractElement instanceof Group) { + boolean includeNext = true; + for (Iterator<AbstractElement> iterator = ((Group) abstractElement).getAbstractTokens().iterator(); iterator + .hasNext() + && includeNext;) { + AbstractElement groupElement = iterator.next(); + elementList.addAll(resolveElement(groupElement, resolveRuleCall)); + includeNext = GrammarUtil.isOptionalCardinality(groupElement); + } + + } + else if (abstractElement instanceof Assignment) { + + Assignment assignment = (Assignment) abstractElement; + + if (assignment.getTerminal() instanceof RuleCall && resolveRuleCall) { + elementList.addAll(resolveElement(assignment.getTerminal(), resolveRuleCall)); + } + else if (assignment.getTerminal() instanceof Alternatives) { + elementList.addAll(resolveElement(assignment.getTerminal(), resolveRuleCall)); + } + else { + elementList.add(assignment); + } + + } + else if (abstractElement instanceof RuleCall) { + + AbstractRule abstractRule = GrammarUtil.calledRule((RuleCall) abstractElement); + + if (abstractRule instanceof ParserRule) { + elementList.addAll(resolveElement(((ParserRule) abstractRule).getAlternatives(), resolveRuleCall)); + } + else { + elementList.add(abstractElement); + } + } + else { + elementList.add(abstractElement); + } + return elementList; + } + + public char[] getCompletionProposalAutoActivationCharacters() { + return null; + } + + public String getErrorMessage() { + return null; + } + + public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { + return null; + } + + public char[] getContextInformationAutoActivationCharacters() { + return null; + } + + public IContextInformationValidator getContextInformationValidator() { + return new ContextInformationValidator(this); + } + + protected void handleReflectionException(Exception ex) { + if (ex instanceof NoSuchMethodException) { + throw new IllegalStateException("Method not found: " + ex.getMessage()); + } + if (ex instanceof IllegalAccessException) { + throw new IllegalStateException("Could not access method: " + ex.getMessage()); + } + if (ex instanceof InvocationTargetException) { + rethrowRuntimeException(((InvocationTargetException) ex).getTargetException()); + } + if (ex instanceof RuntimeException) { + throw (RuntimeException) ex; + } + handleUnexpectedException(ex); + } + + private final void rethrowRuntimeException(Throwable ex) { + if (ex instanceof RuntimeException) { + throw (RuntimeException) ex; + } + if (ex instanceof Error) { + throw (Error) ex; + } + handleUnexpectedException(ex); + } + + protected void handleUnexpectedException(Throwable ex) { + IllegalStateException isex = new IllegalStateException("Unexpected exception thrown"); + isex.initCause(ex); + throw isex; + } + protected final Set<AbstractElement> calculatePossibleElementSet(AbstractNode contextNode, AbstractElement grammarElement) { @@ -237,116 +359,6 @@ public class DefaultContentAssistProcessor implements IContentAssistProcessor { return elementSet; } - protected final List<EObject> resolveElement(AbstractElement abstractElement) { - - List<EObject> elementList = new ArrayList<EObject>(); - - if (abstractElement instanceof Alternatives) { - for (AbstractElement alternativeElement : ((Alternatives) abstractElement).getGroups()) { - elementList.addAll(resolveElement(alternativeElement)); - } - } - else if (abstractElement instanceof Assignment) { - - Assignment assignment = (Assignment) abstractElement; - - if (assignment.getTerminal() instanceof RuleCall) { - - AbstractRule abstractRule = GrammarUtil.calledRule((RuleCall) assignment.getTerminal()); - - if (abstractRule instanceof ParserRule) { - elementList.addAll(resolveElement(assignment.getTerminal())); - } - else { - elementList.add(assignment); - } - } - else { - elementList.add(assignment); - } - } - else if (abstractElement instanceof RuleCall) { - AbstractRule abstractRule = GrammarUtil.calledRule((RuleCall) abstractElement); - - if (null == abstractRule) { - elementList.add(abstractElement); - } - else if (abstractRule instanceof LexerRule) { - elementList.add(abstractRule); - } - else { - return resolveElement(((ParserRule) abstractRule).getAlternatives()); - } - } - else if (abstractElement instanceof Group) { - boolean includeNext = true; - for (Iterator<AbstractElement> iterator = ((Group) abstractElement).getAbstractTokens().iterator(); iterator - .hasNext() - && includeNext;) { - AbstractElement groupElement = iterator.next(); - elementList.addAll(resolveElement(groupElement)); - includeNext = GrammarUtil.isOptionalCardinality(groupElement); - } - - } - else { - elementList.add(abstractElement); - } - return elementList; - } - - public char[] getCompletionProposalAutoActivationCharacters() { - return null; - } - - public String getErrorMessage() { - return null; - } - - public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { - return null; - } - - public char[] getContextInformationAutoActivationCharacters() { - return null; - } - - public IContextInformationValidator getContextInformationValidator() { - return new ContextInformationValidator(this); - } - - protected void handleReflectionException(Exception ex) { - if (ex instanceof NoSuchMethodException) { - throw new IllegalStateException("Method not found: " + ex.getMessage()); - } - if (ex instanceof IllegalAccessException) { - throw new IllegalStateException("Could not access method: " + ex.getMessage()); - } - if (ex instanceof InvocationTargetException) { - rethrowRuntimeException(((InvocationTargetException) ex).getTargetException()); - } - if (ex instanceof RuntimeException) { - throw (RuntimeException) ex; - } - handleUnexpectedException(ex); - } - - private final void rethrowRuntimeException(Throwable ex) { - if (ex instanceof RuntimeException) { - throw (RuntimeException) ex; - } - if (ex instanceof Error) { - throw (Error) ex; - } - handleUnexpectedException(ex); - } - - protected void handleUnexpectedException(Throwable ex) { - IllegalStateException isex = new IllegalStateException("Unexpected exception thrown"); - isex.initCause(ex); - throw isex; - } - private final String firstLetterCapitalized(String name) { return name.substring(0, 1).toUpperCase() + name.substring(1); } @@ -354,24 +366,27 @@ public class DefaultContentAssistProcessor implements IContentAssistProcessor { private final Method findMethod(Class<?> clazz, String name, Class<?>... paramTypes) { Assert.isNotNull(clazz, "Class must not be null"); Assert.isNotNull(name, "Method name must not be null"); + Method result = methodLookupMap.get(name); Class<?> searchType = clazz; - while (!Object.class.equals(searchType) && searchType != null) { + while (!Object.class.equals(searchType) && searchType != null && null == result) { Method[] methods = (searchType.isInterface() ? searchType.getMethods() : searchType.getDeclaredMethods()); - for (int i = 0; i < methods.length; i++) { + for (int i = 0; i < methods.length && null == result; i++) { Method method = methods[i]; if (name.equals(method.getName()) && (paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) { - return method; + result = method; + methodLookupMap.put(name, method); } } searchType = searchType.getSuperclass(); } - return null; + return result; } - private final Object invokeMethod(Method method, Object target, Object... args) { + @SuppressWarnings("unchecked") + private final Collection<ICompletionProposal> invokeMethod(Method method, Object target, Object... args) { try { - return method.invoke(target, args); + return (Collection<ICompletionProposal>) method.invoke(target, args); } catch (Exception ex) { handleReflectionException(ex); 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 new file mode 100644 index 0000000..f2fee38 --- a/dev/null +++ b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/codecompletion/XtextCompletionProposal.java @@ -0,0 +1,229 @@ +package org.eclipse.xtext.ui.common.editor.codecompletion; + +import org.apache.log4j.Logger; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.DocumentEvent; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.contentassist.ContextInformation; +import org.eclipse.jface.text.contentassist.ICompletionProposal; +import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; +import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; +import org.eclipse.jface.text.contentassist.IContextInformation; +import org.eclipse.jface.viewers.StyledString; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; + +/** + * Default Xtext implementation of interface <code>ICompletionProposal</code>. + * + * @author Dennis Hübner - Initial contribution and API + * @author Michael Clay + * @see org.eclipse.jface.text.contentassist.ICompletionProposal + * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2 + * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension6 + */ +public class XtextCompletionProposal implements ICompletionProposal, + ICompletionProposalExtension2, ICompletionProposalExtension6 { + + private Logger logger = Logger.getLogger(XtextCompletionProposal.class); + + private String text; + private String description; + private Image image; + private final StyledString label; + private final int offset; + private String pluginIdentifier; + + /** + * @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 + */ + public XtextCompletionProposal(String text, StyledString label, + String description, String imageFilePath, String pluginIdentifier, + int offset) { + Assert.isNotNull(text, "parameter 'text' must not be null"); + Assert.isNotNull(pluginIdentifier, + "pluginIdentifier 'text' must not be null"); + this.text = text; + this.description = description; + this.offset = offset; + this.pluginIdentifier = pluginIdentifier; + + if (label != null) { + this.label = label; + } else { + this.label = new StyledString(this.text); + } + if (imageFilePath != null) { + initializeImage(imageFilePath); + } + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.jface.text.contentassist.ICompletionProposal#apply(org.eclipse + * .jface.text.IDocument) + */ + public void apply(IDocument document) { + + } + + /* + * (non-Javadoc) + * + * @seeorg.eclipse.jface.text.contentassist.ICompletionProposal# + * getAdditionalProposalInfo() + */ + public String getAdditionalProposalInfo() { + return this.description == null ? null : this.description + + " Additional"; + } + + /* + * (non-Javadoc) + * + * @seeorg.eclipse.jface.text.contentassist.ICompletionProposal# + * getContextInformation() + */ + public IContextInformation getContextInformation() { + if (this.description != null) + return new ContextInformation(getImage(), this.text, + this.description); + return null; + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.jface.text.contentassist.ICompletionProposal#getDisplayString + * () + */ + public String getDisplayString() { + return this.label.getString(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getImage() + */ + public Image getImage() { + return this.image; + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#apply + * (org.eclipse.jface.text.ITextViewer, char, int, int) + */ + public void apply(ITextViewer viewer, char trigger, int stateMask, + int offset) { + + try { + IDocument document = viewer.getDocument(); + + document.replace(this.offset, offset != this.offset ? offset + - this.offset : 0, this.text); + + } catch (BadLocationException e) { + logger.error(e); + } + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#selected + * (org.eclipse.jface.text.ITextViewer, boolean) + */ + public void selected(ITextViewer viewer, boolean smartToggle) { + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#unselected + * (org.eclipse.jface.text.ITextViewer) + */ + public void unselected(ITextViewer viewer) { + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#validate + * (org.eclipse.jface.text.IDocument, int, + * org.eclipse.jface.text.DocumentEvent) + */ + public boolean validate(IDocument document, int offset, DocumentEvent event) { + boolean startsWith = false; + try { + String prefix = document.get(this.offset, offset - this.offset); + startsWith = getDisplayString().toLowerCase().startsWith( + prefix.toLowerCase()); + } catch (BadLocationException e) { + logger.error(e); + } + return startsWith; + } + + /* + * (non-Javadoc) + * + * @seeorg.eclipse.jface.text.contentassist.ICompletionProposalExtension6# + * getStyledDisplayString() + */ + public StyledString getStyledDisplayString() { + return this.label; + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.jface.text.contentassist.ICompletionProposal#getSelection + * (org.eclipse.jface.text.IDocument) + */ + public Point getSelection(IDocument document) { + return new Point(offset + this.text.length(), 0); + } + + private void initializeImage(String imageName) { + Image newImage = JFaceResources.getImage(imageName); +// if (newImage == null) { +// ImageDescriptor imageDescriptorFromPlugin = Activator +// .imageDescriptorFromPlugin(this.pluginIdentifier, imageName); +// if (imageDescriptorFromPlugin != null) { +// JFaceResources.getImageRegistry().put(imageName, +// imageDescriptorFromPlugin); +// newImage = JFaceResources.getImage(imageName); +// } +// } + this.image = newImage; + } +} |

