|
From: <caw...@us...> - 2006-09-22 00:00:26
|
Revision: 1616
http://svn.sourceforge.net/rubyeclipse/?rev=1616&view=rev
Author: cawilliams
Date: 2006-09-21 17:00:16 -0700 (Thu, 21 Sep 2006)
Log Message:
-----------
move all the completion code into the CompletionEngine as a single spot for the code. Properly create images for all proposal types (not just methods)
Modified Paths:
--------------
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/codeassist/CompletionEngine.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/ruby/RubyCompletionProcessor.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/ruby/RubyScriptCompletion.java
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/codeassist/CompletionEngine.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/codeassist/CompletionEngine.java 2006-09-21 22:30:10 UTC (rev 1615)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/codeassist/CompletionEngine.java 2006-09-22 00:00:16 UTC (rev 1616)
@@ -1,19 +1,50 @@
package org.rubypeople.rdt.internal.codeassist;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
+import java.util.Set;
+import org.jruby.ast.ClassNode;
+import org.jruby.ast.ClassVarAsgnNode;
+import org.jruby.ast.ClassVarDeclNode;
+import org.jruby.ast.ClassVarNode;
+import org.jruby.ast.Colon2Node;
+import org.jruby.ast.ConstNode;
+import org.jruby.ast.DefnNode;
+import org.jruby.ast.DefsNode;
+import org.jruby.ast.InstAsgnNode;
+import org.jruby.ast.InstVarNode;
+import org.jruby.ast.ModuleNode;
+import org.jruby.ast.Node;
+import org.jruby.ast.ScopeNode;
+import org.jruby.lexer.yacc.SyntaxException;
import org.rubypeople.rdt.core.CompletionProposal;
import org.rubypeople.rdt.core.CompletionRequestor;
import org.rubypeople.rdt.core.Flags;
import org.rubypeople.rdt.core.IMethod;
+import org.rubypeople.rdt.core.IParent;
+import org.rubypeople.rdt.core.IRubyElement;
import org.rubypeople.rdt.core.IRubyProject;
import org.rubypeople.rdt.core.IRubyScript;
import org.rubypeople.rdt.core.IType;
import org.rubypeople.rdt.core.RubyModelException;
+import org.rubypeople.rdt.internal.core.RubyElement;
+import org.rubypeople.rdt.internal.core.RubyScript;
+import org.rubypeople.rdt.internal.core.RubyType;
+import org.rubypeople.rdt.internal.core.parser.RubyParser;
import org.rubypeople.rdt.internal.ti.DefaultTypeInferrer;
import org.rubypeople.rdt.internal.ti.ITypeGuess;
import org.rubypeople.rdt.internal.ti.ITypeInferrer;
+import org.rubypeople.rdt.internal.ti.util.AttributeLocator;
+import org.rubypeople.rdt.internal.ti.util.ClosestSpanningNodeLocator;
+import org.rubypeople.rdt.internal.ti.util.INodeAcceptor;
+import org.rubypeople.rdt.internal.ti.util.ScopedNodeLocator;
public class CompletionEngine {
private CompletionRequestor requestor;
@@ -34,6 +65,7 @@
// if we hit a period, use character before period as offset for
// inferrer
// if we hit a space, use character after space?
+ // TODO We need to handle other bad syntax like invoking compeltion right after an @
for (int i = offset; i >= 0; i--) {
char curChar = (char) source.charAt(i);
if (curChar == '.') {
@@ -53,7 +85,6 @@
break;
}
}
- System.out.println((char) source.charAt(offset));
List<ITypeGuess> guesses = inferrer.infer(source.toString(), offset);
// TODO Grab the project and all referred projects!
@@ -63,22 +94,24 @@
for (Iterator iter = guesses.iterator(); iter.hasNext();) {
ITypeGuess guess = (ITypeGuess) iter.next();
IType type = completer.findType(guess.getType());
- suggestMethods(requestor, replaceStart, completer, guess, type);
+ suggestMethods(replaceStart, completer, guess, type);
}
+ // FIXME Do we need to call this at all if we know it's a method call we're trying to complete?
+ getDocumentsRubyElementsInScope(script, source.toString(), offset, replaceStart);
+ this.requestor.endReporting();
}
- private void suggestMethods(CompletionRequestor requestor,
- int replaceStart, RubyElementRequestor completer, ITypeGuess guess,
+ private void suggestMethods(int replaceStart, RubyElementRequestor completer, ITypeGuess guess,
IType type) throws RubyModelException {
if (type == null)
return;
- suggestMethods(requestor, replaceStart, guess.getConfidence(), type);
+ suggestMethods(replaceStart, guess.getConfidence(), type);
// Now grab methods from all the included modules
String[] modules = type.getIncludedModuleNames();
for (int x = 0; x < modules.length; x++) {
IType tmpType = completer.findType(modules[x]);
- suggestMethods(requestor, replaceStart, guess.getConfidence(),
+ suggestMethods(replaceStart, guess.getConfidence(),
tmpType);
}
String superClass = type.getSuperclassName();
@@ -87,11 +120,10 @@
&& superClass.equals("Object"))
return;
IType parentClass = completer.findType(superClass);
- suggestMethods(requestor, replaceStart, completer, guess, parentClass);
+ suggestMethods(replaceStart, completer, guess, parentClass);
}
- private void suggestMethods(CompletionRequestor requestor,
- int replaceStart, int confidence, IType type)
+ private void suggestMethods(int replaceStart, int confidence, IType type)
throws RubyModelException {
if (type == null)
return;
@@ -125,5 +157,338 @@
requestor.accept(proposal);
}
}
+
+ /**
+ * Gets all the distinct elements in the current RubyScript
+ * @param offset
+ * @param replaceStart
+ *
+ * @return a List of the names of all the elements in the current RubyScript
+ */
+ private void getDocumentsRubyElementsInScope(IRubyScript script, String source, int offset, int replaceStart) {
+ try {
+ // Get all references projects
+ List<IRubyProject> projects = new ArrayList<IRubyProject>();
+ projects.add(script.getRubyProject());
+ projects.addAll(script.getRubyProject().getReferencedProjects());
+
+ // FIXME Try to stop all the multiple re-parsing of the source! Can we parse once and pass the root node around?
+ // Parse
+ Node rootNode = (new RubyParser()).parse(source);
+ if ( rootNode == null ) { return; }
+ // Find the enclosing method to get locals and args
+ Node enclosingMethodNode = ClosestSpanningNodeLocator.Instance().findClosestSpanner(rootNode, offset, new INodeAcceptor() {
+ public boolean doesAccept(Node node) {
+ return ( node instanceof DefnNode || node instanceof DefsNode );
+ }
+ });
+
+ // Add local vars and arguments
+ if ( enclosingMethodNode != null ) {
+ ScopeNode scopeNode = null;
+ if ( enclosingMethodNode instanceof DefnNode ) { scopeNode = (ScopeNode)((DefnNode)enclosingMethodNode).getBodyNode(); }
+ if ( enclosingMethodNode instanceof DefsNode ) { scopeNode = (ScopeNode)((DefsNode)enclosingMethodNode).getBodyNode(); }
+ if ( scopeNode != null && scopeNode.getLocalNames().length > 0 ) {
+ List locals = Arrays.asList (scopeNode.getLocalNames());
+ for (Iterator iter = locals.iterator(); iter.hasNext();) {
+ String local = (String) iter.next();
+ CompletionProposal proposal = new CompletionProposal(
+ CompletionProposal.LOCAL_VARIABLE_REF, local, 100);
+ // TODO Handle replacement start index correctly
+ proposal.setReplaceRange(replaceStart, replaceStart + local.length());
+ requestor.accept(proposal);
+ }
+ }
+ }
+
+ // Find the enclosing type (class or module) to get instance and classvars from
+ Node enclosingTypeNode = ClosestSpanningNodeLocator.Instance().findClosestSpanner(rootNode, offset, new INodeAcceptor() {
+ public boolean doesAccept(Node node) {
+ return ( node instanceof ClassNode || node instanceof ModuleNode );
+ }
+ });
+
+ // Add members from enclosing type
+ if ( enclosingTypeNode != null ) {
+ getMembersAvailableInsideType( enclosingTypeNode, script, replaceStart );
+ }
+
+ // Add all globals, classes, and modules
+ for (Iterator iter = projects.iterator(); iter.hasNext();) {
+ IRubyProject nextProject = (IRubyProject)(iter.next());
+ getElementsOfType( nextProject, new int[] { IRubyElement.GLOBAL }, replaceStart);
+ addClassesAndModulesInProject( nextProject, replaceStart );
+ }
+ } catch ( RubyModelException rme ) {
+ System.out.println("RubyModelException in CompletionEngine::getElementsInScope()");
+ rme.printStackTrace();
+ } catch ( SyntaxException se ) {
+ System.out.println("SyntaxError in CompletionEngine::getElementsInScope()");
+ se.printStackTrace();
+ }
+ }
+
+ private void addClassesAndModulesInProject(IRubyProject project, int replaceStart) {
+ getElementsOfType(project, new int[] { IRubyElement.TYPE }, replaceStart);
+ }
+
+ private void getElementsOfType(IParent element, int[] types, int replaceStart) {
+ try {
+ IRubyElement[] elements = element.getChildren();
+ if (elements == null) return;
+ for (int x = 0; x < elements.length; x++) {
+ IRubyElement child = elements[x];
+ for (int i = 0; i < types.length; i++) {
+ if (child.getElementType() == types[i]) {
+ String name = child.getElementName();
+ CompletionProposal proposal = new CompletionProposal(
+ getCompletionProposalType(child), name, 100);
+ // TODO Handle replacement start index correctly
+ proposal.setReplaceRange(replaceStart, replaceStart + name.length());
+ requestor.accept(proposal);
+ break;
+ }
+ }
+ if (child instanceof IParent)
+ getElementsOfType((IParent) child, types, replaceStart);
+ }
+ } catch (RubyModelException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private int getCompletionProposalType(IRubyElement child) {
+ switch (child.getElementType()) {
+ case IRubyElement.DYNAMIC_VAR:
+ case IRubyElement.LOCAL_VARIABLE:
+ return CompletionProposal.LOCAL_VARIABLE_REF;
+ case IRubyElement.METHOD:
+ return CompletionProposal.METHOD_REF;
+ case IRubyElement.TYPE:
+ return CompletionProposal.TYPE_REF;
+ case IRubyElement.INSTANCE_VAR:
+ case IRubyElement.CLASS_VAR:
+ return CompletionProposal.FIELD_REF;
+ default:
+ return CompletionProposal.KEYWORD;
+ }
+ }
+
+ /**
+ * Gets the members available inside a type node (ModuleNode, ClassNode):
+ * - Instance variables
+ * - Class variables
+ * - Methods
+ *
+ * @param typeNode
+ * @return
+ */
+ private void getMembersAvailableInsideType(Node typeNode, IRubyScript script, int replaceStart) throws RubyModelException {
+ if ( typeNode == null ) { return; }
+
+ // Get type name
+ String typeName = null;
+ if ( typeNode instanceof ClassNode ) { typeName = ((Colon2Node)((ClassNode)typeNode).getCPath()).getName(); }
+ if ( typeNode instanceof ModuleNode ) { typeName = ((Colon2Node)((ModuleNode)typeNode).getCPath()).getName(); }
+ if ( typeName == null ) { return; }
+
+ // XXX rubyType may not be in script, but rather be defined in another script
+// IType rubyType = new RubyType( (RubyElement)script, typeName );
+ //Better method:
+ // Find the named type
+// IType rubyType = findTypeFromAllProjects(typeName, script);
+
+// System.out.println(" -- Located RubyType info.");
+// System.out.println(" -- Superclass: " + rubyType.getSuperclassName() );
+
+// if ( rubyType != null ) {
+// String[] includedModuleNames = rubyType.getIncludedModuleNames();
+// if ( includedModuleNames != null ) {
+// for ( String moduleName : rubyType.getIncludedModuleNames() ) {
+// System.out.println(" -- Includes module: " + moduleName);
+// }
+// }
+// }
+
+
+
+ // Get superclass and add its public members
+ List<Node> superclassNodes = getSuperclassNodes( typeNode, script );
+ for ( Node superclassNode : superclassNodes ) {
+ getMembersAvailableInsideType( superclassNode, script, replaceStart );
+ }
+
+ // Get public members of mixins
+ List<String> mixinNames = getIncludedMixinNames( typeName, script );
+ for ( String mixinName : mixinNames ) {
+ List<Node> mixinDeclarations = getTypeDeclarationNodes( mixinName, script );
+ for ( Node mixinDeclaration : mixinDeclarations ) {
+ getMembersAvailableInsideType( mixinDeclaration, script, replaceStart );
+ }
+ }
+
+ // Get instance and class variables available in the enclosing type
+ List<Node> instanceAndClassVars = ScopedNodeLocator.Instance().findNodesInScope(typeNode, new INodeAcceptor() {
+ public boolean doesAccept(Node node) {
+ return ( node instanceof InstVarNode ||
+ node instanceof InstAsgnNode ||
+ node instanceof ClassVarNode ||
+ node instanceof ClassVarDeclNode ||
+ node instanceof ClassVarAsgnNode );
+ }
+ });
+
+ if ( instanceAndClassVars != null ) {
+ // Get the unique names of instance and class variables
+ for ( Node varNode : instanceAndClassVars ) {
+ String name = getNameReflectively(varNode);
+ if ( name != null ) {
+ CompletionProposal proposal = new CompletionProposal(CompletionProposal.FIELD_REF, name, 100);
+ // TODO Handle replacement start index correctly
+ proposal.setReplaceRange(replaceStart, replaceStart + name.length());
+ requestor.accept(proposal);
+ }
+ }
+ }
+
+ // Get method names defined by DefnNodes and DefsNodes
+ List<Node> methodDefinitions = ScopedNodeLocator.Instance().findNodesInScope(typeNode, new INodeAcceptor() {
+ public boolean doesAccept(Node node) {
+ return ( node instanceof DefnNode ) || ( node instanceof DefsNode );
+ }
+ });
+ for ( Node methodDefinition : methodDefinitions ) {
+ String name = null;
+ if ( methodDefinition instanceof DefnNode ) { name = ((DefnNode)methodDefinition).getName(); }
+ if ( methodDefinition instanceof DefsNode ) { name = ((DefsNode)methodDefinition).getName(); }
+ if (name == null) continue;
+ CompletionProposal proposal = new CompletionProposal(CompletionProposal.METHOD_REF, name, 100);
+ // TODO Handle replacement start index correctly
+ proposal.setReplaceRange(replaceStart, replaceStart + name.length());
+ requestor.accept(proposal);
+ }
+
+ // Get instance and class vars defined by [c]attr_* calls
+ List<String> attrs = AttributeLocator.Instance().findInstanceAttributesInScope(typeNode);
+ for (Iterator iter = attrs.iterator(); iter.hasNext(); ) {
+ String attr = (String) iter.next();
+ CompletionProposal proposal = new CompletionProposal(CompletionProposal.FIELD_REF, attr, 100);
+ // TODO Handle replacement start index correctly
+ proposal.setReplaceRange(replaceStart, replaceStart + attr.length());
+ requestor.accept(proposal);
+ }
+
+ }
+
+ /**
+ * Finds all nodes that declare a type that is a superclass of the specified node. Example:
+ *
+ * """
+ * class Klass;def meth_1;1;end;end
+ * class Klass;def meth_2;2;end;end
+ *
+ * class SubKlass < Klass;end
+ * """
+ *
+ * Issuing getSuperClassNodes() on the ClassNode declaring SubKlass would return two ClassNodes;
+ * one for each definition of Klass.
+ *
+ * @param typeNode Node to find superclass nodes of
+ * @return List of ClassNode or ModuleNode
+ */
+ private List<Node> getSuperclassNodes( Node typeNode, IRubyScript script ) {
+ if ( typeNode instanceof ClassNode ) {
+ Node superNode = ((ClassNode)typeNode).getSuperNode();
+ if ( superNode instanceof ConstNode ) {
+ String superclassName = ((ConstNode)superNode).getName();
+ return getTypeDeclarationNodes( superclassName, script );
+ }
+ }
+ return new ArrayList<Node>();
+ }
+
+ /** Lookup type declaration nodes */
+ private List<Node> getTypeDeclarationNodes( String typeName, IRubyScript script ) {
+ System.out.println("Being asked for the type decl node for " + typeName );
+
+ // Find the named type
+ IType type = findTypeFromAllProjects(typeName, script);
+
+ try {
+ if ( type instanceof RubyType ) {
+
+ // FIXME This feels a little hacky and backwards - RubyType.getSource() and then parse... consider reworking the clients to this method to accept RubyTypes or something similar?
+ // Find source and parse
+ RubyType rubyType = (RubyType)type;
+ String source = rubyType.getSource();
+
+ // FIXME Why does the parser balk on \r chars?
+ source = source.replace('\r', ' ');
+ Node rootNode = (new RubyParser()).parse( source );
+
+ // Bail if the parse fails
+ if ( rootNode == null ) { return new ArrayList(); }
+
+ // Return any type declaration nodes in included source
+ return ScopedNodeLocator.Instance().findNodesInScope(rootNode, new INodeAcceptor() {
+ public boolean doesAccept(Node node) {
+ return ( node instanceof ClassNode ) ||
+ ( node instanceof ModuleNode );
+ }
+ });
+ }
+
+ } catch ( RubyModelException rme ) {
+ rme.printStackTrace();
+ }
+
+ return new ArrayList<Node>(0);
+ }
+
+ private IType findTypeFromAllProjects(String typeName, IRubyScript rootScript) {
+ // Grab the project and all referred projects
+ List<IRubyProject> projects = new LinkedList<IRubyProject>();
+ projects.add(rootScript.getRubyProject());
+ projects.addAll(rootScript.getRubyProject().getReferencedProjects());
+
+ List<IRubyProject> refProjects = rootScript.getRubyProject().getReferencedProjects();
+
+ // Find the named type
+ RubyElementRequestor completer = new RubyElementRequestor(projects.toArray(new IRubyProject[]{}));
+ return completer.findType(typeName);
+ }
+
+ private List<String> getIncludedMixinNames( String typeName, IRubyScript script ) {
+ IType rubyType = new RubyType( (RubyElement)script, typeName );
+
+ try {
+ String[] includedModuleNames = rubyType.getIncludedModuleNames();
+ if ( includedModuleNames != null ) {
+ return Arrays.asList(rubyType.getIncludedModuleNames());
+ } else {
+ return new ArrayList<String>(0);
+ }
+ } catch (RubyModelException e) {
+ return new ArrayList<String>(0);
+ }
+ }
+
+ /**
+ * Gets the name of a node by reflectively invoking "getName()" on it;
+ * helper method just to cut many "instanceof/cast" pairs.
+ * @param node
+ * @return name or null
+ */
+ // TODO Copy/pasted from DefaultOccurrencesFinder, refactor these two methods to a common location.
+ private String getNameReflectively( Node node ) {
+ try {
+ Method getNameMethod = node.getClass().getMethod("getName", new Class[]{});
+ Object name = getNameMethod.invoke( node, new Object[0] );
+ return (String)name;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
}
Modified: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/ruby/RubyCompletionProcessor.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/ruby/RubyCompletionProcessor.java 2006-09-21 22:30:10 UTC (rev 1615)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/ruby/RubyCompletionProcessor.java 2006-09-22 00:00:16 UTC (rev 1616)
@@ -1,16 +1,11 @@
package org.rubypeople.rdt.internal.ui.text.ruby;
-import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
-import java.util.HashSet;
import java.util.Iterator;
-import java.util.LinkedList;
import java.util.List;
-import java.util.Set;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
@@ -28,36 +23,8 @@
import org.eclipse.jface.text.templates.TemplateContextType;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.IEditorPart;
-import org.jruby.ast.ClassNode;
-import org.jruby.ast.ClassVarAsgnNode;
-import org.jruby.ast.ClassVarDeclNode;
-import org.jruby.ast.ClassVarNode;
-import org.jruby.ast.Colon2Node;
-import org.jruby.ast.ConstNode;
-import org.jruby.ast.DefnNode;
-import org.jruby.ast.DefsNode;
-import org.jruby.ast.InstAsgnNode;
-import org.jruby.ast.InstVarNode;
-import org.jruby.ast.ModuleNode;
-import org.jruby.ast.Node;
-import org.jruby.ast.ScopeNode;
-import org.jruby.lexer.yacc.SyntaxException;
-import org.rubypeople.rdt.core.IParent;
-import org.rubypeople.rdt.core.IRubyElement;
-import org.rubypeople.rdt.core.IRubyProject;
import org.rubypeople.rdt.core.IRubyScript;
-import org.rubypeople.rdt.core.IType;
-import org.rubypeople.rdt.core.RubyModelException;
-import org.rubypeople.rdt.internal.codeassist.RubyElementRequestor;
-import org.rubypeople.rdt.internal.core.RubyElement;
-import org.rubypeople.rdt.internal.core.RubyScript;
-import org.rubypeople.rdt.internal.core.RubyType;
-import org.rubypeople.rdt.internal.core.parser.RubyParser;
import org.rubypeople.rdt.internal.corext.template.ruby.RubyContextType;
-import org.rubypeople.rdt.internal.ti.util.AttributeLocator;
-import org.rubypeople.rdt.internal.ti.util.ClosestSpanningNodeLocator;
-import org.rubypeople.rdt.internal.ti.util.INodeAcceptor;
-import org.rubypeople.rdt.internal.ti.util.ScopedNodeLocator;
import org.rubypeople.rdt.internal.ui.RubyPlugin;
import org.rubypeople.rdt.internal.ui.RubyPluginImages;
import org.rubypeople.rdt.internal.ui.text.template.contentassist.RubyTemplateAccess;
@@ -141,8 +108,6 @@
.getSelectionProvider().getSelection();
cursorPosition = selection.getOffset() + selection.getLength();
- ICompletionProposal[] normal = determineRubyElementProposals(viewer,
- documentOffset);
List templates = determineTemplateProposals(viewer, documentOffset);
ICompletionProposal[] templateArray = new ICompletionProposal[templates
.size()];
@@ -150,7 +115,7 @@
for (Iterator iter = templates.iterator(); iter.hasNext(); i++) {
templateArray[i] = (ICompletionProposal) iter.next();
}
- ICompletionProposal[] merged = merge(normal, templateArray);
+ ICompletionProposal[] merged = templateArray;
ICompletionProposal[] keywords = determineKeywordProposals(viewer,
documentOffset);
@@ -188,45 +153,6 @@
return merged;
}
- /**
- * @param viewer
- * @param documentOffset
- * @return
- */
- private ICompletionProposal[] determineRubyElementProposals(
- ITextViewer viewer, int documentOffset) {
- Collection completionProposals = getDocumentsRubyElementsInScope(documentOffset);
- String prefix = getCurrentPrefix(viewer.getDocument().get(),
- documentOffset);
- // following the JDT convention, if there's no text already entered,
- // then don't suggest imported elements
- if (prefix.length() > 0) {
- // FIXME Add elements from required/loaded files!
- }
-
- List possibleProposals = new ArrayList();
- for (Iterator iter = completionProposals.iterator(); iter.hasNext();) {
- String proposal = (String) iter.next();
- if (proposal.startsWith(prefix) && !proposal.equals(prefix)) {
- String message = "{0}";
- IContextInformation info = new ContextInformation(proposal,
- MessageFormat
- .format(message, new Object[] { proposal }));
- possibleProposals
- .add(new CompletionProposal(proposal.substring(prefix
- .length(), proposal.length()), documentOffset,
- 0, proposal.length() - prefix.length(), null,
- proposal, info, MessageFormat.format(
- "Ruby keyword: {0}",
- new Object[] { proposal })));
- }
- }
- ICompletionProposal[] result = new ICompletionProposal[possibleProposals
- .size()];
- possibleProposals.toArray(result);
- return result;
- }
-
/*
* (non-Javadoc)
*
@@ -357,331 +283,7 @@
}
return false;
}
-
- /**
- * Gets all the distinct elements in the current RubyScript
- * @param offset
- *
- * @return a List of the names of all the elements in the current RubyScript
- */
- private Collection getDocumentsRubyElementsInScope(int offset) {
- IRubyScript script = fManager.getWorkingCopy(fEditor.getEditorInput());
- String source = "";
- Collection elements = new ArrayList();
- try {
- // Get the script's source. If possible, get the most recent contents.
- if ( script instanceof RubyScript ) {
- source = new String(((RubyScript)script).getContents());
- } else {
- source = script.getSource();
- }
-
- // FIXME Ugly hacking here to handle where we have invalid syntax by invoking after a period
- StringBuffer sourceBuff = new StringBuffer(source);
- offset--;
- char charAtOffset = (char) sourceBuff.charAt(offset);
- if (charAtOffset == '.') {
- sourceBuff.deleteCharAt(offset);
- offset--;
- }
- source = sourceBuff.toString();
-
- // XXX Combine this code with the code in RubyScript.codeComplete() (into a new CompletionEngine class?)
-
- // Get all references projects
- List<IRubyProject> projects = new ArrayList<IRubyProject>();
- projects.add(script.getRubyProject());
- projects.addAll(script.getRubyProject().getReferencedProjects());
-
- // Parse
- Node rootNode = (new RubyParser()).parse(source);
- if ( rootNode == null ) { return elements; }
-
- // Find the enclosing method to get locals and args
- Node enclosingMethodNode = ClosestSpanningNodeLocator.Instance().findClosestSpanner(rootNode, offset, new INodeAcceptor() {
- public boolean doesAccept(Node node) {
- return ( node instanceof DefnNode || node instanceof DefsNode );
- }
- });
-
- // Add local vars and arguments
- if ( enclosingMethodNode != null ) {
- ScopeNode scopeNode = null;
- if ( enclosingMethodNode instanceof DefnNode ) { scopeNode = (ScopeNode)((DefnNode)enclosingMethodNode).getBodyNode(); }
- if ( enclosingMethodNode instanceof DefsNode ) { scopeNode = (ScopeNode)((DefsNode)enclosingMethodNode).getBodyNode(); }
- if ( scopeNode != null && scopeNode.getLocalNames().length > 0 ) {
- elements.addAll( Arrays.asList (scopeNode.getLocalNames()) );
- }
- }
-
- // Find the enclosing type (class or module) to get instance and classvars from
- Node enclosingTypeNode = ClosestSpanningNodeLocator.Instance().findClosestSpanner(rootNode, offset, new INodeAcceptor() {
- public boolean doesAccept(Node node) {
- return ( node instanceof ClassNode || node instanceof ModuleNode );
- }
- });
-
- // Add members from enclosing type
- if ( enclosingTypeNode != null ) {
- elements.addAll( getMembersAvailableInsideType( enclosingTypeNode, script ) );
- }
-
- // Add all globals, classes, and modules
- for (Iterator iter = projects.iterator(); iter.hasNext();) {
- IRubyProject nextProject = (IRubyProject)(iter.next());
- elements.addAll(getElementsOfType( nextProject, new int[] { IRubyElement.GLOBAL }));
- elements.addAll(addClassesAndModulesInProject( nextProject ));
- }
- } catch ( RubyModelException rme ) {
- System.out.println("RubyModelException in RubyCompletionProcessor::getElementsInScope()");
- rme.printStackTrace();
- // Return empty 'elements'
- } catch ( SyntaxException se ) {
- System.out.println("SyntaxError in RubyCompletionProcessor::getElementsInScope()");
- se.printStackTrace();
- // Return empty 'elements'
- }
- return elements;
- }
-
- private Collection addClassesAndModulesInProject(IRubyProject project) {
- return getElementsOfType(project, new int[] { IRubyElement.TYPE });
- }
-
- private Collection getElementsOfType(IParent element, int[] types) {
- Collection suggestions = new ArrayList();
- try {
- IRubyElement[] elements = element.getChildren();
- if (elements == null)
- return suggestions;
- for (int x = 0; x < elements.length; x++) {
- IRubyElement child = elements[x];
- for (int i = 0; i < types.length; i++) {
- if (child.getElementType() == types[i]) {
- suggestions.add(child.getElementName());
- break;
- }
- }
- if (child instanceof IParent)
- suggestions
- .addAll(getElementsOfType((IParent) child, types));
- }
- } catch (RubyModelException e) {
- e.printStackTrace();
- }
- return suggestions;
- }
-
- /**
- * Gets the memebrs available inside a type node (ModuleNode, ClassNode):
- * - Instance variables
- * - Class variables
- * - Methods
- *
- * @param typeNode
- * @return
- */
- private List<String> getMembersAvailableInsideType(Node typeNode, IRubyScript script) throws RubyModelException {
- List<String> elements = new LinkedList<String>();
- if ( typeNode == null ) { return elements; }
-
- // Get type name
- String typeName = null;
- if ( typeNode instanceof ClassNode ) { typeName = ((Colon2Node)((ClassNode)typeNode).getCPath()).getName(); }
- if ( typeNode instanceof ModuleNode ) { typeName = ((Colon2Node)((ModuleNode)typeNode).getCPath()).getName(); }
- if ( typeName == null ) { return elements; }
-
- // XXX rubyType may not be in script, but rather be defined in another script
-// IType rubyType = new RubyType( (RubyElement)script, typeName );
- //Better method:
- // Find the named type
-// IType rubyType = findTypeFromAllProjects(typeName, script);
-
-// System.out.println(" -- Located RubyType info.");
-// System.out.println(" -- Superclass: " + rubyType.getSuperclassName() );
-
-// if ( rubyType != null ) {
-// String[] includedModuleNames = rubyType.getIncludedModuleNames();
-// if ( includedModuleNames != null ) {
-// for ( String moduleName : rubyType.getIncludedModuleNames() ) {
-// System.out.println(" -- Includes module: " + moduleName);
-// }
-// }
-// }
-
-
-
- // Get superclass and add its public members
- List<Node> superclassNodes = getSuperclassNodes( typeNode, script );
-
- for ( Node superclassNode : superclassNodes ) {
- elements.addAll( getMembersAvailableInsideType( superclassNode, script ) );
- }
-
- // Get public members of mixins
- List<String> mixinNames = getIncludedMixinNames( typeName, script );
- for ( String mixinName : mixinNames ) {
- List<Node> mixinDeclarations = getTypeDeclarationNodes( mixinName, script );
- for ( Node mixinDeclaration : mixinDeclarations ) {
- elements.addAll( getMembersAvailableInsideType( mixinDeclaration, script ) );
- }
- }
-
- // Get instance and class variables available in the enclosing type
- List<Node> instanceAndClassVars = ScopedNodeLocator.Instance().findNodesInScope(typeNode, new INodeAcceptor() {
- public boolean doesAccept(Node node) {
- return ( node instanceof InstVarNode ||
- node instanceof InstAsgnNode ||
- node instanceof ClassVarNode ||
- node instanceof ClassVarDeclNode ||
- node instanceof ClassVarAsgnNode );
- }
- });
-
- if ( instanceAndClassVars != null ) {
- // Get the unique names of instance and class variables
- Set instanceAndClassVarNames = new HashSet(instanceAndClassVars.size());
- for ( Node varNode : instanceAndClassVars ) {
- String name = getNameReflectively(varNode);
- if ( name != null ) {
- instanceAndClassVarNames.add(name);
- }
- }
-
- // Add instance and class variables to matched elements
- elements.addAll( instanceAndClassVarNames );
- }
-
- // Get method names defined by DefnNodes and DefsNodes
- List<Node> methodDefinitions = ScopedNodeLocator.Instance().findNodesInScope(typeNode, new INodeAcceptor() {
- public boolean doesAccept(Node node) {
- return ( node instanceof DefnNode ) || ( node instanceof DefsNode );
- }
- });
- for ( Node methodDefinition : methodDefinitions ) {
- if ( methodDefinition instanceof DefnNode ) { elements.add( ((DefnNode)methodDefinition).getName() ); }
- if ( methodDefinition instanceof DefsNode ) { elements.add( ((DefsNode)methodDefinition).getName() ); }
- }
-
- // Get instance and class vars defined by [c]attr_* calls
- elements.addAll( AttributeLocator.Instance().findInstanceAttributesInScope(typeNode) );
-
- return elements;
- }
-
- /**
- * Finds all nodes that declare a type that is a superclass of the specified node. Example:
- *
- * """
- * class Klass;def meth_1;1;end;end
- * class Klass;def meth_2;2;end;end
- *
- * class SubKlass < Klass;end
- * """
- *
- * Issuing getSuperClassNodes() on the ClassNode declaring SubKlass would return two ClassNodes;
- * one for each definition of Klass.
- *
- * @param typeNode Node to find superclass nodes of
- * @return List of ClassNode or ModuleNode
- */
- private List<Node> getSuperclassNodes( Node typeNode, IRubyScript script ) {
- if ( typeNode instanceof ClassNode ) {
- Node superNode = ((ClassNode)typeNode).getSuperNode();
- if ( superNode instanceof ConstNode ) {
- String superclassName = ((ConstNode)superNode).getName();
- return getTypeDeclarationNodes( superclassName, script );
- }
- }
-
- return new ArrayList<Node>();
- }
-
- private IType findTypeFromAllProjects(String typeName, IRubyScript rootScript) {
- // Grab the project and all referred projects
- List<IRubyProject> projects = new LinkedList<IRubyProject>();
- projects.add(rootScript.getRubyProject());
- projects.addAll(rootScript.getRubyProject().getReferencedProjects());
-
- List<IRubyProject> refProjects = rootScript.getRubyProject().getReferencedProjects();
-
- // Find the named type
- RubyElementRequestor completer = new RubyElementRequestor(projects.toArray(new IRubyProject[]{}));
- return completer.findType(typeName);
- }
-
- /** Lookup type declaration nodes */
- private List<Node> getTypeDeclarationNodes( String typeName, IRubyScript script ) {
- System.out.println("Being asked for the type decl node for " + typeName );
-
- // Find the named type
- IType type = findTypeFromAllProjects(typeName, script);
-
- try {
- if ( type instanceof RubyType ) {
-
- // FIXME This feels a little hacky and backwards - RubyType.getSource() and then parse... consider reworking the clients to this method to accept RubyTypes or something similar?
- // Find source and parse
- RubyType rubyType = (RubyType)type;
- String source = rubyType.getSource();
-
- // FIXME Why does the parser balk on \r chars?
- source = source.replace('\r', ' ');
- Node rootNode = (new RubyParser()).parse( source );
-
- // Bail if the parse fails
- if ( rootNode == null ) { return new ArrayList(); }
-
- // Return any type declaration nodes in included source
- return ScopedNodeLocator.Instance().findNodesInScope(rootNode, new INodeAcceptor() {
- public boolean doesAccept(Node node) {
- return ( node instanceof ClassNode ) ||
- ( node instanceof ModuleNode );
- }
- });
- }
-
- } catch ( RubyModelException rme ) {
- rme.printStackTrace();
- }
-
- return new ArrayList<Node>(0);
- }
-
- private List<String> getIncludedMixinNames( String typeName, IRubyScript script ) {
- IType rubyType = new RubyType( (RubyElement)script, typeName );
-
- try {
- String[] includedModuleNames = rubyType.getIncludedModuleNames();
- if ( includedModuleNames != null ) {
- return Arrays.asList(rubyType.getIncludedModuleNames());
- } else {
- return new ArrayList<String>(0);
- }
- } catch (RubyModelException e) {
- return new ArrayList<String>(0);
- }
- }
-
- /**
- * Gets the name of a node by reflectively invoking "getName()" on it;
- * helper method just to cut many "instanceof/cast" pairs.
- * @param node
- * @return name or null
- */
- // TODO Copy/pasted from DefaultOccurrencesFinder, refactor these two methods to a common location.
- private String getNameReflectively( Node node ) {
- try {
- Method getNameMethod = node.getClass().getMethod("getName", new Class[]{});
- Object name = getNameMethod.invoke( node, new Object[0] );
- return (String)name;
- } catch (Exception e) {
- return null;
- }
- }
-
-
private ICompletionProposal[] determineKeywordProposals(ITextViewer viewer,
int documentOffset) {
initKeywordProposals();
Modified: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/ruby/RubyScriptCompletion.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/ruby/RubyScriptCompletion.java 2006-09-21 22:30:10 UTC (rev 1615)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/ruby/RubyScriptCompletion.java 2006-09-22 00:00:16 UTC (rev 1616)
@@ -206,26 +206,9 @@
// default:
// return null;
// }
- switch (proposal.getKind()) {
- case CompletionProposal.KEYWORD:
- return createKeywordProposal(proposal);
- case CompletionProposal.METHOD_REF:
- case CompletionProposal.METHOD_NAME_REFERENCE:
- return createMethodReferenceProposal(proposal);
- default:
- return createKeywordProposal(proposal);
- }
+ return createProposal(proposal);
}
- private IRubyCompletionProposal createMethodReferenceProposal(CompletionProposal proposal) {
- String completion= proposal.getCompletion();
- int start= proposal.getReplaceStart();
- int length= getLength(proposal);
- String label= proposal.getName();
- int relevance= computeRelevance(proposal);
- Image image = getImage(fLabelProvider.createImageDescriptor(proposal));
- return new RubyCompletionProposal(completion, start, length, image, label, relevance);
- }
/**
* Returns the ruby script that the receiver operates on, or
@@ -250,13 +233,14 @@
return (descriptor == null) ? null : fRegistry.get(descriptor);
}
- private IRubyCompletionProposal createKeywordProposal(CompletionProposal proposal) {
+ private IRubyCompletionProposal createProposal(CompletionProposal proposal) {
String completion= proposal.getCompletion();
int start= proposal.getReplaceStart();
int length= getLength(proposal);
String label= proposal.getName();
int relevance= computeRelevance(proposal);
- return new RubyCompletionProposal(completion, start, length, null, label, relevance);
+ Image image = getImage(fLabelProvider.createImageDescriptor(proposal));
+ return new RubyCompletionProposal(completion, start, length, image, label, relevance);
}
/**
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|