*fix
diff --git a/plugins/org.eclipse.dltk.ruby.core/src/org/eclipse/dltk/ruby/internal/core/codeassist/RubyCompletionEngine.java b/plugins/org.eclipse.dltk.ruby.core/src/org/eclipse/dltk/ruby/internal/core/codeassist/RubyCompletionEngine.java
index c2a501e..0470e98 100644
--- a/plugins/org.eclipse.dltk.ruby.core/src/org/eclipse/dltk/ruby/internal/core/codeassist/RubyCompletionEngine.java
+++ b/plugins/org.eclipse.dltk.ruby.core/src/org/eclipse/dltk/ruby/internal/core/codeassist/RubyCompletionEngine.java
@@ -1,878 +1,873 @@
-/*******************************************************************************
- * Copyright (c) 2005, 2007 IBM Corporation 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.dltk.ruby.internal.core.codeassist;
-
-import java.text.Collator;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-
-import org.eclipse.core.resources.ResourcesPlugin;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.jobs.Job;
-import org.eclipse.dltk.ast.ASTNode;
-import org.eclipse.dltk.ast.Modifiers;
-import org.eclipse.dltk.ast.declarations.ModuleDeclaration;
-import org.eclipse.dltk.ast.expressions.CallExpression;
-import org.eclipse.dltk.ast.parser.ISourceParser;
-import org.eclipse.dltk.ast.references.ConstantReference;
-import org.eclipse.dltk.ast.references.SimpleReference;
-import org.eclipse.dltk.codeassist.IAssistParser;
-import org.eclipse.dltk.codeassist.ScriptCompletionEngine;
-import org.eclipse.dltk.compiler.env.ISourceModule;
-import org.eclipse.dltk.compiler.problem.DefaultProblem;
-import org.eclipse.dltk.core.CompletionProposal;
-import org.eclipse.dltk.core.DLTKLanguageManager;
-import org.eclipse.dltk.core.IField;
-import org.eclipse.dltk.core.IMethod;
-import org.eclipse.dltk.core.IModelElement;
-import org.eclipse.dltk.core.IType;
-import org.eclipse.dltk.core.ModelException;
-import org.eclipse.dltk.core.mixin.IMixinElement;
-import org.eclipse.dltk.core.mixin.MixinModel;
-import org.eclipse.dltk.evaluation.types.AmbiguousType;
-import org.eclipse.dltk.evaluation.types.IClassType;
-import org.eclipse.dltk.internal.core.ModelElement;
-import org.eclipse.dltk.ruby.ast.RubyBlock;
-import org.eclipse.dltk.ruby.ast.RubyColonExpression;
-import org.eclipse.dltk.ruby.ast.RubyDAssgnExpression;
-import org.eclipse.dltk.ruby.ast.RubyDVarExpression;
-import org.eclipse.dltk.ruby.core.RubyNature;
-import org.eclipse.dltk.ruby.core.RubyPlugin;
-import org.eclipse.dltk.ruby.core.model.FakeField;
-import org.eclipse.dltk.ruby.core.text.RubyKeyword;
-import org.eclipse.dltk.ruby.core.utils.RubySyntaxUtils;
-import org.eclipse.dltk.ruby.internal.parser.mixin.IRubyMixinElement;
-import org.eclipse.dltk.ruby.internal.parser.mixin.RubyMixinClass;
-import org.eclipse.dltk.ruby.internal.parser.mixin.RubyMixinElementInfo;
-import org.eclipse.dltk.ruby.internal.parser.mixin.RubyMixinMethod;
-import org.eclipse.dltk.ruby.internal.parser.mixin.RubyMixinModel;
-import org.eclipse.dltk.ruby.internal.parser.mixin.RubyMixinVariable;
-import org.eclipse.dltk.ruby.internal.parsers.jruby.ASTUtils;
-import org.eclipse.dltk.ruby.typeinference.IMixinSearchRequestor;
-import org.eclipse.dltk.ruby.typeinference.RubyClassType;
-import org.eclipse.dltk.ruby.typeinference.RubyModelUtils;
-import org.eclipse.dltk.ruby.typeinference.RubyTypeInferencingUtils;
-import org.eclipse.dltk.ti.BasicContext;
-import org.eclipse.dltk.ti.DLTKTypeInferenceEngine;
-import org.eclipse.dltk.ti.goals.ExpressionTypeGoal;
-import org.eclipse.dltk.ti.types.IEvaluatedType;
-import org.jruby.util.collections.WeakHashSet;
-
-public class RubyCompletionEngine extends ScriptCompletionEngine {
-
- private final static int RELEVANCE_FREE_SPACE = 10000000;
-
- private final static int RELEVANCE_KEYWORD = 1000000;
-
- private final static int RELEVANCE_METHODS = 100000;
-
- private final static String[] globalVars = { "$DEBUG", "$$", "$-i",
- "$deferr", "$/", "$'", "$stdout", "$-l", "$-I", "$.", "$KCODE",
- "$binding", "$-w", "$FILENAME", "$defout", "$,", "$`", "$-F", "$*",
- "$LOADED_FEATURES", "$stdin", "$-p", "$:", "$\\", "$=", "$!",
- "$-v", "$>", "$&", "$;", "$SAFE", "$PROGRAM_NAME", "$\"", "$-d",
- "$?", "$-0", "$+", "$@", "$-a", "$VERBOSE", "$stderr", "$~", "$0",
- "$LOAD_PATH", "$<", "$_", "$-K" };
-
- private DLTKTypeInferenceEngine inferencer;
- private ISourceParser parser = null;
- private MixinModel model;
- private HashSet completedNames = new HashSet();
- private WeakHashSet intresting = new WeakHashSet();
-
- private ASTNode completionNode;
-
- private final Comparator modelElementComparator = new Comparator() {
-
- Collator collator = Collator.getInstance(Locale.ENGLISH);
-
- public int compare(Object arg0, Object arg1) {
- if (arg0 instanceof IModelElement && arg1 instanceof IModelElement) {
- IModelElement me1 = (IModelElement) arg1;
- IModelElement me2 = (IModelElement) arg0;
- int r = -collator.compare(me1.getElementName(), me2
- .getElementName());
- if (r == 0) {
- // prefer elements from current module
- boolean cur1 = me1.getAncestor(IModelElement.SOURCE_MODULE)
- .equals(currentModule);
- boolean cur2 = me2.getAncestor(IModelElement.SOURCE_MODULE)
- .equals(currentModule);
- if (cur1 == cur2) {
- return 0;
- } else
- return cur1 ? -1 : 1;
- } else
- return r;
- }
- return 0;
- }
-
- /*
- * private int calcVisibility(IMethod method1) throws ModelException {
- * return (method1.getFlags() & Modifiers.AccPrivate) +
- * (method1.getFlags() & Modifiers.AccProtected) + (method1.getFlags() &
- * Modifiers.AccPublic); }
- */
-
- };
-
- private ISourceModule currentModule;;
-
- public RubyCompletionEngine() {
- this.inferencer = new DLTKTypeInferenceEngine();
- this.model = RubyMixinModel.getRawInstance();
- try {
- this.parser = DLTKLanguageManager
- .getSourceParser(RubyNature.NATURE_ID);
- } catch (CoreException e) {
- throw new RuntimeException(
- "Failed to initialize RubyCompletionEngine", e);
- }
- }
-
- protected int getEndOfEmptyToken() {
- return 0;
- }
-
- protected String processMethodName(IMethod method, String token) {
- return null;
- }
-
- protected String processTypeName(IType method, String token) {
- return null;
- }
-
- public IAssistParser getParser() {
- return null;
- }
-
- private boolean afterColons(String content, int position) {
- if (position < 2)
- return false;
- if (content.charAt(position - 1) == ':'
- && content.charAt(position - 2) == ':')
- return true;
- return false;
- }
-
- private boolean afterDollar(String content, int position) {
- if (position < 1)
- return false;
- if (content.charAt(position - 1) == '$')
- return true;
- return false;
- }
-
- private boolean afterAt(String content, int position) {
- if (position < 1)
- return false;
- if (content.charAt(position - 1) == '@')
- return true;
- return false;
- }
-
- private boolean afterAt2(String content, int position) {
- if (position < 2)
- return false;
- if (content.charAt(position - 1) == '@'
- && content.charAt(position - 2) == '@')
- return true;
- return false;
- }
-
- private String getWordStarting(String content, int position, int maxLen) {
- int original = position;
- if (position <= 0)
- return "";
- if (position >= content.length())
- position = content.length();
- position--;
- int len = 0;
- while (position >= 0
- && len < maxLen
- && RubySyntaxUtils.isLessStrictIdentifierCharacter(content
- .charAt(position))) {
- position--;
- }
- if (position + 1 > original)
- return "";
- if ((position >= 0 && Character.isWhitespace(content.charAt(position)))
- || position == -1)
- return content.substring(position + 1, original);
- return null;
- }
-
- public void complete(ISourceModule module, int position, int i) {
- this.currentModule = module;
- if (Job.getJobManager().find(ResourcesPlugin.FAMILY_AUTO_BUILD).length > 0) { // `FIXIT,
- // make
- // more
- // correct
- // awaiting
- // for
- // building
- this.requestor.completionFailure(new DefaultProblem(null,
- "Please wait until building is ready...", 0, null,
- IStatus.WARNING, startPosition, endPosition, -1));
- return;
- }
- if (!RubyPlugin.initialized) {
- this.requestor.completionFailure(new DefaultProblem(null,
- "Please wait while DLTK Ruby being initialized...", 0,
- null, IStatus.WARNING, startPosition, endPosition, -1));
- return;
- }
-
- completedNames.clear();
- this.actualCompletionPosition = position;
- this.requestor.beginReporting();
- org.eclipse.dltk.core.ISourceModule modelModule = (org.eclipse.dltk.core.ISourceModule) module;
- try {
- String content = module.getSourceContents();
-
- String wordStarting = getWordStarting(content, position, 10);
-
- if (wordStarting != null) {
- this.setSourceRange(position - wordStarting.length(), position);
- String[] keywords = RubyKeyword.findByPrefix(wordStarting);
- for (int j = 0; j < keywords.length; j++) {
- reportKeyword(keywords[j]);
- }
- }
-
- ModuleDeclaration moduleDeclaration = parser.parse(module
- .getFileName(), content.toCharArray(), null);
-
- if (afterDollar(content, position)) {
- completeGlobalVar((org.eclipse.dltk.core.ISourceModule) module,
- moduleDeclaration, "$", position);
- } else if (afterAt2(content, position)) {
- completeSimpleRef((org.eclipse.dltk.core.ISourceModule) module,
- moduleDeclaration, "@@", position);
- } else if (afterAt(content, position)) {
- completeSimpleRef((org.eclipse.dltk.core.ISourceModule) module,
- moduleDeclaration, "@", position);
- } else if (afterColons(content, position)) {
-
- ASTNode node = ASTUtils.findMaximalNodeEndingAt(
- moduleDeclaration, position - 2);
- this.setSourceRange(position, position);
- if (node != null) {
- BasicContext basicContext = new BasicContext(modelModule,
- moduleDeclaration);
- ExpressionTypeGoal goal = new ExpressionTypeGoal(
- basicContext, node);
- IEvaluatedType type = inferencer.evaluateType(goal, 3000);
- reportSubElements(modelModule, type, "");
- } else {
- completeConstant(modelModule, moduleDeclaration, "",
- position, true);
- }
- } else {
- ASTNode minimalNode = ASTUtils.findMinimalNode(
- moduleDeclaration, position, position);
- if (minimalNode != null) {
- this.completionNode = minimalNode;
- if (minimalNode instanceof CallExpression) {
- completeCall(modelModule, moduleDeclaration,
- (CallExpression) minimalNode, position);
- } else if (minimalNode instanceof ConstantReference) {
- completeConstant(modelModule, moduleDeclaration,
- (ConstantReference) minimalNode, position);
- } else if (minimalNode instanceof RubyColonExpression) {
- completeColonExpression(modelModule, moduleDeclaration,
- (RubyColonExpression) minimalNode, position);
- } else if (minimalNode instanceof SimpleReference) {
- completeSimpleRef(modelModule, moduleDeclaration,
- ((SimpleReference) minimalNode).getName(),
- position);
- } else if (minimalNode instanceof RubyDVarExpression) {
- completeSimpleRef(modelModule, moduleDeclaration,
- ((RubyDVarExpression) minimalNode).getName(),
- position);
- } else { // worst case
- if (wordStarting == null || wordStarting.length() == 0) {
- int rel = RELEVANCE_FREE_SPACE;
- try {
- IModelElement[] children = modelModule
- .getChildren();
- if (children != null)
- for (int j = 0; j < children.length; j++) {
- if (children[j] instanceof IField)
- reportField((IField) children[j],
- rel);
- if (children[j] instanceof IMethod) {
- IMethod method = (IMethod) children[j];
- if ((method.getFlags() & Modifiers.AccStatic) == 0)
- reportMethod(method, rel);
- }
- if (children[j] instanceof IType
- && !children[j]
- .getElementName()
- .trim()
- .startsWith("<<"))
- reportType((IType) children[j], rel);
- }
- } catch (ModelException e) {
- e.printStackTrace();
- }
- }
- }
- }
-
- }
-
- } finally {
- this.requestor.endReporting();
- }
- }
-
- private class CompletionMixinMethodRequestor implements
- IMixinSearchRequestor {
- private String lastParent = null;
- private List group = new ArrayList();
- private int relevance = 0;
- private final RubyMixinClass klass;
-
- public CompletionMixinMethodRequestor(RubyMixinClass klass) {
- super();
- this.klass = klass;
- }
-
- public void acceptResult(IRubyMixinElement element) {
- if (element instanceof RubyMixinMethod) {
- RubyMixinMethod method = (RubyMixinMethod) element;
- String parent = method.getSelfType().getKey();
- if (lastParent == null || !lastParent.equals(parent)) {
- this.flush();
- lastParent = parent;
- }
- group.add(method);
- }
- }
-
- public void flush() {
- if (group.size() > 0) {
- RubyMixinMethod[] mixinMethods = (RubyMixinMethod[]) group
- .toArray(new RubyMixinMethod[group.size()]);
- List allSourceMethods = RubyModelUtils.getAllSourceMethods(
- mixinMethods, klass);
- IMethod[] methods = (IMethod[]) allSourceMethods
- .toArray(new IMethod[allSourceMethods.size()]);
- Arrays.sort(methods, modelElementComparator);
- for (int j = 0; j < methods.length; j++) {
- reportMethod(methods[j], RELEVANCE_METHODS + relevance);
- }
- group.clear();
- }
- relevance--;
- }
-
- }
-
- private void completeClassMethods(
- org.eclipse.dltk.core.ISourceModule modelModule,
- ModuleDeclaration moduleDeclaration, RubyMixinClass rubyClass,
- String prefix) {
- CompletionMixinMethodRequestor mixinSearchRequestor = new CompletionMixinMethodRequestor(
- rubyClass);
- rubyClass.findMethods(prefix, true, mixinSearchRequestor);
- mixinSearchRequestor.flush();
- }
-
- private void completeClassMethods(
- org.eclipse.dltk.core.ISourceModule modelModule,
- ModuleDeclaration moduleDeclaration, IEvaluatedType type,
- String prefix) {
- if (type instanceof RubyClassType) {
- RubyClassType rubyClassType = (RubyClassType) type;
- RubyMixinClass rubyClass = RubyMixinModel.getInstance()
- .createRubyClass(rubyClassType);
- if (rubyClass != null) {
- completeClassMethods(modelModule, moduleDeclaration, rubyClass,
- prefix);
- }
-
- } else if (type instanceof AmbiguousType) {
- AmbiguousType type2 = (AmbiguousType) type;
- IEvaluatedType[] possibleTypes = type2.getPossibleTypes();
- for (int i = 0; i < possibleTypes.length; i++) {
- completeClassMethods(modelModule, moduleDeclaration,
- possibleTypes[i], prefix);
- }
- }
- }
-
- private void completeClassMethods(
- org.eclipse.dltk.core.ISourceModule modelModule,
- ModuleDeclaration moduleDeclaration, ASTNode receiver,
- String pattern) {
- ExpressionTypeGoal goal = new ExpressionTypeGoal(new BasicContext(
- modelModule, moduleDeclaration), receiver);
- IEvaluatedType type = inferencer.evaluateType(goal, 2000);
- completeClassMethods(modelModule, moduleDeclaration, type, pattern);
- }
-
- private void completeGlobalVar(org.eclipse.dltk.core.ISourceModule module,
- ModuleDeclaration moduleDeclaration, String prefix, int position) {
- int relevance = 424242;
- this.setSourceRange(position - prefix.length(), position);
-
- IMixinElement[] elements = RubyMixinModel.getRawInstance().find(
- prefix + "*");
-
- // String[] findKeys = RubyMixinModel.getRawInstance().findKeys(
- // prefix + "*");
- for (int i = 0; i < elements.length; i++) {
- IRubyMixinElement rubyElement = RubyMixinModel.getInstance()
- .createRubyElement(elements[i]);
- if (rubyElement instanceof RubyMixinVariable) {
- RubyMixinVariable variable = (RubyMixinVariable) rubyElement;
- IField[] sourceFields = variable.getSourceFields();
- for (int j = 0; j < sourceFields.length; j++) {
- if (sourceFields[j] != null) {
- reportField(sourceFields[j], relevance--);
- break;
- }
- }
- }
- }
-
- for (int i = 0; i < globalVars.length; i++) {
- if (globalVars[i].startsWith(prefix))
- reportField(new FakeField((ModelElement) module, globalVars[i],
- 0, 0), relevance--);
- }
- }
-
- private void completeSimpleRef(org.eclipse.dltk.core.ISourceModule module,
- ModuleDeclaration moduleDeclaration, String prefix, int position) {
- int relevance = 424242;
-
- this.setSourceRange(position - prefix.length(), position);
- ASTNode[] wayToNode = ASTUtils.restoreWayToNode(moduleDeclaration,
- this.completionNode);
- for (int i = wayToNode.length - 1; i > 0; i--) {
- if (wayToNode[i] instanceof RubyBlock) {
- RubyBlock rubyBlock = (RubyBlock) wayToNode[i];
- Set vars = rubyBlock.getVars();
- for (Iterator iterator = vars.iterator(); iterator.hasNext();) {
- ASTNode n = (ASTNode) iterator.next();
- if (n instanceof RubyDAssgnExpression) {
- RubyDAssgnExpression rd = (RubyDAssgnExpression) n;
- if (rd.getName().startsWith(prefix)) {
- reportField(new FakeField((ModelElement) module, rd
- .getName(), 0, 0), relevance--);
- }
- }
- }
- }
- }
-
- if (prefix.startsWith("$")) { // globals
- completeGlobalVar(module, moduleDeclaration, prefix, position);
- } else { // class & instance & locals
- IField[] fields = RubyModelUtils.findFields(module,
- moduleDeclaration, prefix, position);
- for (int i = 0; i < fields.length; i++) {
- reportField(fields[i], relevance--);
- }
- }
-
- }
-
- private void reportSubElements(org.eclipse.dltk.core.ISourceModule module,
- IEvaluatedType type, String prefix) {
- int relevance = 424242;
-
- ArrayList types = new ArrayList();
- ArrayList methods = new ArrayList();
- ArrayList fields = new ArrayList();
-
- if (type instanceof RubyClassType) {
- RubyClassType rubyClassType = (RubyClassType) type;
- IMixinElement mixinElement = model.get(rubyClassType.getModelKey());
- if (mixinElement != null) {
- IMixinElement[] children = mixinElement.getChildren();
- for (int i = 0; i < children.length; i++) {
- Object[] infos = children[i].getAllObjects();
- for (int j = 0; j < infos.length; j++) {
- RubyMixinElementInfo obj = (RubyMixinElementInfo) infos[j];
- if (obj.getObject() == null)
- continue;
- if (obj.getKind() == RubyMixinElementInfo.K_CLASS
- || obj.getKind() == RubyMixinElementInfo.K_MODULE) {
- IType type2 = (IType) obj.getObject();
- if (type2 != null
- && type2.getElementName()
- .startsWith(prefix)) {
- // reportType(type2, relevance--);
- types.add(type2);
- }
- } else if (obj.getKind() == RubyMixinElementInfo.K_METHOD) {
- IMethod method2 = (IMethod) obj.getObject();
- if (method2 != null
- && method2.getElementName().startsWith(
- prefix)) {
- // reportMethod(method2, relevance--);
- methods.add(method2);
- }
- }
- if (obj.getKind() == RubyMixinElementInfo.K_VARIABLE) {
- IField fff = (IField) obj.getObject();
- if (fff != null
- && fff.getElementName().startsWith(prefix)) {
- // reportField(fff, relevance--);
- fields.add(fff);
- }
- }
- break;
- }
-
- }
- }
- } else {
- // never should be here
- }
-
- Collections.sort(fields, modelElementComparator);
- for (Iterator iterator = fields.iterator(); iterator.hasNext();) {
- IField t = (IField) iterator.next();
- reportField(t, relevance--);
- }
-
- Collections.sort(types, modelElementComparator);
- for (Iterator iterator = types.iterator(); iterator.hasNext();) {
- IType t = (IType) iterator.next();
- reportType(t, relevance--);
- }
-
- Collections.sort(methods, modelElementComparator);
- for (Iterator iterator = methods.iterator(); iterator.hasNext();) {
- IMethod t = (IMethod) iterator.next();
- reportMethod(t, relevance--);
- }
-
- }
-
- private void completeColonExpression(
- org.eclipse.dltk.core.ISourceModule module,
- ModuleDeclaration moduleDeclaration, RubyColonExpression node,
- int position) {
- String content;
- try {
- content = module.getSource();
- } catch (ModelException e) {
- return;
- }
- int pos = (node.getLeft() != null) ? (node.getLeft().sourceEnd() + 2)
- : (node.sourceStart());
- String starting = null;
- try {
- starting = content.substring(pos, position).trim();
- } catch (IndexOutOfBoundsException e) {
- e.printStackTrace();
- return;
- }
-
- if (starting.startsWith("::")) {
- this.setSourceRange(position - starting.length() + 2, position);
- completeConstant(module, moduleDeclaration, starting.substring(2),
- position, true);
- return;
- }
-
- this.setSourceRange(position - starting.length(), position);
-
- ExpressionTypeGoal goal = new ExpressionTypeGoal(new BasicContext(
- module, moduleDeclaration), node.getLeft());
- IEvaluatedType type = inferencer.evaluateType(goal, 3000);
- reportSubElements(module, type, starting);
- }
-
- private void completeConstant(org.eclipse.dltk.core.ISourceModule module,
- ModuleDeclaration moduleDeclaration, String prefix, int position,
- boolean topLevelOnly) {
-
- if (!topLevelOnly) {
- IMixinElement[] modelStaticScopes = RubyTypeInferencingUtils
- .getModelStaticScopes(model, moduleDeclaration, position);
- for (int i = modelStaticScopes.length - 1; i >= 0; i--) {
- IMixinElement scope = modelStaticScopes[i];
- if (scope == null)
- continue;
- reportSubElements(module, new RubyClassType(scope.getKey()),
- prefix);
- }
- }
-
- int relevance = 4242;
-
- // try {
- if (prefix.length() > 0) {
- String varkey = "Object" + MixinModel.SEPARATOR + prefix;
- RubyMixinModel rubyModel = RubyMixinModel.getInstance();
- String[] keys2 = rubyModel.getRawModel().findKeys(varkey + "*");
- for (int i = 0; i < keys2.length; i++) {
- IRubyMixinElement element = rubyModel
- .createRubyElement(keys2[i]);
- if (element instanceof RubyMixinVariable) {
- RubyMixinVariable variable = (RubyMixinVariable) element;
- IField[] sourceFields = variable.getSourceFields();
- for (int j = 0; j < sourceFields.length; j++) {
- if (sourceFields[j] != null) {
- reportField(sourceFields[j], relevance--);
- break;
- }
- }
- }
- }
- }
- // } catch (ModelException e) {
- // e.printStackTrace();
- // }
-
- HashSet names = new HashSet();
- IType[] allTypes = RubyTypeInferencingUtils.getAllTypes(module, prefix);
- for (int i = 0; i < allTypes.length; i++) {
- String elementName = allTypes[i].getElementName();
- if (names.contains(elementName))
- continue;
- names.add(elementName);
- reportType(allTypes[i], relevance--);
- }
-
- }
-
- private void completeConstant(org.eclipse.dltk.core.ISourceModule module,
- ModuleDeclaration moduleDeclaration, ConstantReference node,
- int position) {
- String content;
- try {
- content = module.getSource();
- } catch (ModelException e) {
- return;
- }
-
- String prefix = content.substring(node.sourceStart(), position);
- this.setSourceRange(position - prefix.length(), position);
- completeConstant(module, moduleDeclaration, prefix, position, false);
- }
-
- private void completeCall(org.eclipse.dltk.core.ISourceModule module,
- ModuleDeclaration moduleDeclaration, CallExpression node,
- int position) {
- ASTNode receiver = node.getReceiver();
-
- String content;
- try {
- content = module.getSource();
- } catch (ModelException e) {
- return;
- }
-
- int pos = (receiver != null) ? (receiver.sourceEnd() + 1) : (node
- .sourceStart());
-
- for (int t = 0; t < 2; t++) { // correct not more 2 chars
- if (pos < position
- && !RubySyntaxUtils.isStrictIdentifierCharacter(content
- .charAt(pos))) // for (...).name and Foo::name
- // calls
- pos++;
- }
-
- String starting = content.substring(pos, position).trim();
-
- if (receiver == null)
- completeSimpleRef(module, moduleDeclaration, starting, position);
-
- this.setSourceRange(position - starting.length(), position);
-
- if (starting.startsWith("__")) {
- String[] keywords = RubyKeyword.findByPrefix("__");
- for (int j = 0; j < keywords.length; j++) {
- reportKeyword(keywords[j]);
- }
- }
-
- if (receiver != null) {
- completeClassMethods(module, moduleDeclaration, receiver, starting);
- } else {
- IClassType self = RubyTypeInferencingUtils.determineSelfClass(
- module, moduleDeclaration, position);
- completeClassMethods(module, moduleDeclaration, self, starting);
- }
-
- }
-
- protected String processFieldName(IField field, String token) {
- return field.getElementName();
- }
-
- private void reportMethod(IMethod method, int rel) {
- this.intresting.add(method);
- String elementName = method.getElementName();
- if (completedNames.contains(elementName)) {
- return;
- }
- completedNames.add(elementName);
- if (elementName.indexOf('.') != -1) {
- elementName = elementName.substring(elementName.indexOf('.') + 1);
- }
- char[] name = elementName.toCharArray();
- char[] compl = name;
-
- int relevance = rel;
-
- // accept result
- noProposal = false;
- if (!requestor.isIgnored(CompletionProposal.METHOD_DECLARATION)) {
- CompletionProposal proposal = createProposal(
- CompletionProposal.METHOD_DECLARATION,
- actualCompletionPosition);
-
- String[] params = null;
- try {
- params = method.getParameters();
- } catch (ModelException e) {
- e.printStackTrace();
- }
-
- if (params != null && params.length > 0) {
- char[][] args = new char[params.length][];
- for (int i = 0; i < params.length; ++i) {
- args[i] = params[i].toCharArray();
- }
- proposal.setParameterNames(args);
- }
-
- proposal.setModelElement(method);
- proposal.setName(name);
- proposal.setCompletion(compl);
- try {
- proposal.setFlags(method.getFlags());
- } catch (ModelException e) {
- RubyPlugin.log(e);
- }
- proposal.setReplaceRange(this.startPosition - this.offset,
- this.endPosition - this.offset);
- proposal.setRelevance(relevance);
- this.requestor.accept(proposal);
- if (DEBUG) {
- this.printDebug(proposal);
- }
- }
-
- }
-
- private void reportType(IType type, int rel) {
- this.intresting.add(type);
- String elementName = type.getElementName();
- if (completedNames.contains(elementName)) {
- return;
- }
- completedNames.add(elementName);
- char[] name = elementName.toCharArray();
- if (name.length == 0)
- return;
-
- int relevance = rel;
-
- // accept result
- noProposal = false;
- if (!requestor.isIgnored(CompletionProposal.TYPE_REF)) {
- CompletionProposal proposal = createProposal(
- CompletionProposal.TYPE_REF, actualCompletionPosition);
-
- proposal.setModelElement(type);
- proposal.setName(name);
- proposal.setCompletion(elementName.toCharArray());
- // proposal.setFlags(Flags.AccDefault);
- try {
- proposal.setFlags(type.getFlags());
- } catch (ModelException e) {
- }
- proposal.setReplaceRange(this.startPosition - this.offset,
- this.endPosition - this.offset);
- proposal.setRelevance(relevance);
- this.requestor.accept(proposal);
- if (DEBUG) {
- this.printDebug(proposal);
- }
- }
-
- }
-
- private void reportField(IField field, int rel) {
- this.intresting.add(field);
- String elementName = field.getElementName();
- if (completedNames.contains(elementName)) {
- return;
- }
- completedNames.add(elementName);
- char[] name = elementName.toCharArray();
- if (name.length == 0)
- return;
-
- int relevance = rel;
-
- // accept result
- noProposal = false;
- if (!requestor.isIgnored(CompletionProposal.FIELD_REF)) {
- CompletionProposal proposal = createProposal(
- CompletionProposal.FIELD_REF, actualCompletionPosition);
-
- proposal.setModelElement(field);
- proposal.setName(name);
- proposal.setCompletion(elementName.toCharArray());
- // proposal.setFlags(Flags.AccDefault);
- proposal.setReplaceRange(this.startPosition - this.offset,
- this.endPosition - this.offset);
- proposal.setRelevance(relevance);
- this.requestor.accept(proposal);
- if (DEBUG) {
- this.printDebug(proposal);
- }
- }
-
- }
-
- private void reportKeyword(String name) {
- // accept result
- noProposal = false;
- if (!requestor.isIgnored(CompletionProposal.FIELD_REF)) {
- CompletionProposal proposal = createProposal(
- CompletionProposal.KEYWORD, actualCompletionPosition);
-
- proposal.setName(name.toCharArray());
- proposal.setCompletion(name.toCharArray());
- // proposal.setFlags(Flags.AccDefault);
- proposal.setReplaceRange(this.startPosition - this.offset,
- this.endPosition - this.offset);
- proposal.setRelevance(RELEVANCE_KEYWORD);
- this.requestor.accept(proposal);
- if (DEBUG) {
- this.printDebug(proposal);
- }
- }
-
- }
-
+/*******************************************************************************
+ * Copyright (c) 2005, 2007 IBM Corporation 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.dltk.ruby.internal.core.codeassist;
+
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.dltk.ast.ASTNode;
+import org.eclipse.dltk.ast.Modifiers;
+import org.eclipse.dltk.ast.declarations.ModuleDeclaration;
+import org.eclipse.dltk.ast.expressions.CallExpression;
+import org.eclipse.dltk.ast.parser.ISourceParser;
+import org.eclipse.dltk.ast.references.ConstantReference;
+import org.eclipse.dltk.ast.references.SimpleReference;
+import org.eclipse.dltk.codeassist.IAssistParser;
+import org.eclipse.dltk.codeassist.ScriptCompletionEngine;
+import org.eclipse.dltk.compiler.env.ISourceModule;
+import org.eclipse.dltk.compiler.problem.DefaultProblem;
+import org.eclipse.dltk.core.CompletionProposal;
+import org.eclipse.dltk.core.DLTKLanguageManager;
+import org.eclipse.dltk.core.IField;
+import org.eclipse.dltk.core.IMethod;
+import org.eclipse.dltk.core.IModelElement;
+import org.eclipse.dltk.core.IType;
+import org.eclipse.dltk.core.ModelException;
+import org.eclipse.dltk.core.mixin.IMixinElement;
+import org.eclipse.dltk.core.mixin.MixinModel;
+import org.eclipse.dltk.evaluation.types.AmbiguousType;
+import org.eclipse.dltk.evaluation.types.IClassType;
+import org.eclipse.dltk.internal.core.ModelElement;
+import org.eclipse.dltk.ruby.ast.RubyBlock;
+import org.eclipse.dltk.ruby.ast.RubyColonExpression;
+import org.eclipse.dltk.ruby.ast.RubyDAssgnExpression;
+import org.eclipse.dltk.ruby.ast.RubyDVarExpression;
+import org.eclipse.dltk.ruby.core.RubyNature;
+import org.eclipse.dltk.ruby.core.RubyPlugin;
+import org.eclipse.dltk.ruby.core.model.FakeField;
+import org.eclipse.dltk.ruby.core.text.RubyKeyword;
+import org.eclipse.dltk.ruby.core.utils.RubySyntaxUtils;
+import org.eclipse.dltk.ruby.internal.parser.mixin.IRubyMixinElement;
+import org.eclipse.dltk.ruby.internal.parser.mixin.RubyMixinClass;
+import org.eclipse.dltk.ruby.internal.parser.mixin.RubyMixinElementInfo;
+import org.eclipse.dltk.ruby.internal.parser.mixin.RubyMixinMethod;
+import org.eclipse.dltk.ruby.internal.parser.mixin.RubyMixinModel;
+import org.eclipse.dltk.ruby.internal.parser.mixin.RubyMixinVariable;
+import org.eclipse.dltk.ruby.internal.parsers.jruby.ASTUtils;
+import org.eclipse.dltk.ruby.typeinference.IMixinSearchRequestor;
+import org.eclipse.dltk.ruby.typeinference.RubyClassType;
+import org.eclipse.dltk.ruby.typeinference.RubyModelUtils;
+import org.eclipse.dltk.ruby.typeinference.RubyTypeInferencingUtils;
+import org.eclipse.dltk.ti.BasicContext;
+import org.eclipse.dltk.ti.DLTKTypeInferenceEngine;
+import org.eclipse.dltk.ti.goals.ExpressionTypeGoal;
+import org.eclipse.dltk.ti.types.IEvaluatedType;
+import org.jruby.util.collections.WeakHashSet;
+
+public class RubyCompletionEngine extends ScriptCompletionEngine {
+
+ private final static int RELEVANCE_FREE_SPACE = 10000000;
+
+ private final static int RELEVANCE_KEYWORD = 1000000;
+
+ private final static int RELEVANCE_METHODS = 100000;
+
+ private final static String[] globalVars = { "$DEBUG", "$$", "$-i",
+ "$deferr", "$/", "$'", "$stdout", "$-l", "$-I", "$.", "$KCODE",
+ "$binding", "$-w", "$FILENAME", "$defout", "$,", "$`", "$-F", "$*",
+ "$LOADED_FEATURES", "$stdin", "$-p", "$:", "$\\", "$=", "$!",
+ "$-v", "$>", "$&", "$;", "$SAFE", "$PROGRAM_NAME", "$\"", "$-d",
+ "$?", "$-0", "$+", "$@", "$-a", "$VERBOSE", "$stderr", "$~", "$0",
+ "$LOAD_PATH", "$<", "$_", "$-K" };
+
+ private DLTKTypeInferenceEngine inferencer;
+ private ISourceParser parser = null;
+ private MixinModel model;
+ private HashSet completedNames = new HashSet();
+ private WeakHashSet intresting = new WeakHashSet();
+
+ private ASTNode completionNode;
+
+ private final Comparator modelElementComparator = new Comparator() {
+
+ Collator collator = Collator.getInstance(Locale.ENGLISH);
+
+ public int compare(Object arg0, Object arg1) {
+ if (arg0 instanceof IModelElement && arg1 instanceof IModelElement) {
+ IModelElement me1 = (IModelElement) arg1;
+ IModelElement me2 = (IModelElement) arg0;
+ int r = -collator.compare(me1.getElementName(), me2
+ .getElementName());
+ if (r == 0) {
+ // prefer elements from current module
+ boolean cur1 = me1.getAncestor(IModelElement.SOURCE_MODULE)
+ .equals(currentModule);
+ boolean cur2 = me2.getAncestor(IModelElement.SOURCE_MODULE)
+ .equals(currentModule);
+ if (cur1 == cur2) {
+ return 0;
+ } else
+ return cur1 ? -1 : 1;
+ } else
+ return r;
+ }
+ return 0;
+ }
+
+ /*
+ * private int calcVisibility(IMethod method1) throws ModelException {
+ * return (method1.getFlags() & Modifiers.AccPrivate) +
+ * (method1.getFlags() & Modifiers.AccProtected) + (method1.getFlags() &
+ * Modifiers.AccPublic); }
+ */
+
+ };
+
+ private ISourceModule currentModule;;
+
+ public RubyCompletionEngine() {
+ this.inferencer = new DLTKTypeInferenceEngine();
+ this.model = RubyMixinModel.getRawInstance();
+ try {
+ this.parser = DLTKLanguageManager
+ .getSourceParser(RubyNature.NATURE_ID);
+ } catch (CoreException e) {
+ throw new RuntimeException(
+ "Failed to initialize RubyCompletionEngine", e);
+ }
+ }
+
+ protected int getEndOfEmptyToken() {
+ return 0;
+ }
+
+ protected String processMethodName(IMethod method, String token) {
+ return null;
+ }
+
+ protected String processTypeName(IType method, String token) {
+ return null;
+ }
+
+ public IAssistParser getParser() {
+ return null;
+ }
+
+ private boolean afterColons(String content, int position) {
+ if (position < 2)
+ return false;
+ if (content.charAt(position - 1) == ':'
+ && content.charAt(position - 2) == ':')
+ return true;
+ return false;
+ }
+
+ private boolean afterDollar(String content, int position) {
+ if (position < 1)
+ return false;
+ if (content.charAt(position - 1) == '$')
+ return true;
+ return false;
+ }
+
+ private boolean afterAt(String content, int position) {
+ if (position < 1)
+ return false;
+ if (content.charAt(position - 1) == '@')
+ return true;
+ return false;
+ }
+
+ private boolean afterAt2(String content, int position) {
+ if (position < 2)
+ return false;
+ if (content.charAt(position - 1) == '@'
+ && content.charAt(position - 2) == '@')
+ return true;
+ return false;
+ }
+
+ private String getWordStarting(String content, int position, int maxLen) {
+ int original = position;
+ if (position <= 0)
+ return "";
+ if (position >= content.length())
+ position = content.length();
+ position--;
+ int len = 0;
+ while (position >= 0
+ && len < maxLen
+ && RubySyntaxUtils.isLessStrictIdentifierCharacter(content
+ .charAt(position))) {
+ position--;
+ }
+ if (position + 1 > original)
+ return "";
+ if ((position >= 0 && Character.isWhitespace(content.charAt(position)))
+ || position == -1)
+ return content.substring(position + 1, original);
+ return null;
+ }
+
+ public void complete(ISourceModule module, int position, int i) {
+ this.currentModule = module;
+ if (Job.getJobManager().find(ResourcesPlugin.FAMILY_AUTO_BUILD).length > 0) {
+ // FIXIT, make more correct awaiting for building
+ this.requestor.completionFailure(new DefaultProblem(null,
+ "Please wait until building is ready...", 0, null,
+ IStatus.WARNING, startPosition, endPosition, -1));
+ return;
+ }
+ if (!RubyPlugin.initialized) {
+ this.requestor.completionFailure(new DefaultProblem(null,
+ "Please wait while DLTK Ruby being initialized...", 0,
+ null, IStatus.WARNING, startPosition, endPosition, -1));
+ return;
+ }
+
+ completedNames.clear();
+ this.actualCompletionPosition = position;
+ this.requestor.beginReporting();
+ org.eclipse.dltk.core.ISourceModule modelModule = (org.eclipse.dltk.core.ISourceModule) module;
+ try {
+ String content = module.getSourceContents();
+
+ String wordStarting = getWordStarting(content, position, 10);
+
+ if (wordStarting != null) {
+ this.setSourceRange(position - wordStarting.length(), position);
+ String[] keywords = RubyKeyword.findByPrefix(wordStarting);
+ for (int j = 0; j < keywords.length; j++) {
+ reportKeyword(keywords[j]);
+ }
+ }
+
+ ModuleDeclaration moduleDeclaration = parser.parse(module
+ .getFileName(), content.toCharArray(), null);
+
+ if (afterDollar(content, position)) {
+ completeGlobalVar((org.eclipse.dltk.core.ISourceModule) module,
+ moduleDeclaration, "$", position);
+ } else if (afterAt2(content, position)) {
+ completeSimpleRef((org.eclipse.dltk.core.ISourceModule) module,
+ moduleDeclaration, "@@", position);
+ } else if (afterAt(content, position)) {
+ completeSimpleRef((org.eclipse.dltk.core.ISourceModule) module,
+ moduleDeclaration, "@", position);
+ } else if (afterColons(content, position)) {
+
+ ASTNode node = ASTUtils.findMaximalNodeEndingAt(
+ moduleDeclaration, position - 2);
+ this.setSourceRange(position, position);
+ if (node != null) {
+ BasicContext basicContext = new BasicContext(modelModule,
+ moduleDeclaration);
+ ExpressionTypeGoal goal = new ExpressionTypeGoal(
+ basicContext, node);
+ IEvaluatedType type = inferencer.evaluateType(goal, 3000);
+ reportSubElements(modelModule, type, "");
+ } else {
+ completeConstant(modelModule, moduleDeclaration, "",
+ position, true);
+ }
+ } else {
+ ASTNode minimalNode = ASTUtils.findMinimalNode(
+ moduleDeclaration, position, position);
+ if (minimalNode != null) {
+ this.completionNode = minimalNode;
+ if (minimalNode instanceof CallExpression) {
+ completeCall(modelModule, moduleDeclaration,
+ (CallExpression) minimalNode, position);
+ } else if (minimalNode instanceof ConstantReference) {
+ completeConstant(modelModule, moduleDeclaration,
+ (ConstantReference) minimalNode, position);
+ } else if (minimalNode instanceof RubyColonExpression) {
+ completeColonExpression(modelModule, moduleDeclaration,
+ (RubyColonExpression) minimalNode, position);
+ } else if (minimalNode instanceof SimpleReference) {
+ completeSimpleRef(modelModule, moduleDeclaration,
+ ((SimpleReference) minimalNode).getName(),
+ position);
+ } else if (minimalNode instanceof RubyDVarExpression) {
+ completeSimpleRef(modelModule, moduleDeclaration,
+ ((RubyDVarExpression) minimalNode).getName(),
+ position);
+ } else { // worst case
+ if (wordStarting == null || wordStarting.length() == 0) {
+ int rel = RELEVANCE_FREE_SPACE;
+ try {
+ IModelElement[] children = modelModule
+ .getChildren();
+ if (children != null)
+ for (int j = 0; j < children.length; j++) {
+ if (children[j] instanceof IField)
+ reportField((IField) children[j],
+ rel);
+ if (children[j] instanceof IMethod) {
+ IMethod method = (IMethod) children[j];
+ if ((method.getFlags() & Modifiers.AccStatic) == 0)
+ reportMethod(method, rel);
+ }
+ if (children[j] instanceof IType
+ && !children[j]
+ .getElementName()
+ .trim()
+ .startsWith("<<"))
+ reportType((IType) children[j], rel);
+ }
+ } catch (ModelException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ }
+
+ } finally {
+ this.requestor.endReporting();
+ }
+ }
+
+ private class CompletionMixinMethodRequestor implements
+ IMixinSearchRequestor {
+ private String lastParent = null;
+ private List group = new ArrayList();
+ private int relevance = 0;
+ private final RubyMixinClass klass;
+
+ public CompletionMixinMethodRequestor(RubyMixinClass klass) {
+ super();
+ this.klass = klass;
+ }
+
+ public void acceptResult(IRubyMixinElement element) {
+ if (element instanceof RubyMixinMethod) {
+ RubyMixinMethod method = (RubyMixinMethod) element;
+ String parent = method.getSelfType().getKey();
+ if (lastParent == null || !lastParent.equals(parent)) {
+ this.flush();
+ lastParent = parent;
+ }
+ group.add(method);
+ }
+ }
+
+ public void flush() {
+ if (group.size() > 0) {
+ RubyMixinMethod[] mixinMethods = (RubyMixinMethod[]) group
+ .toArray(new RubyMixinMethod[group.size()]);
+ List allSourceMethods = RubyModelUtils.getAllSourceMethods(
+ mixinMethods, klass);
+ IMethod[] methods = (IMethod[]) allSourceMethods
+ .toArray(new IMethod[allSourceMethods.size()]);
+ Arrays.sort(methods, modelElementComparator);
+ for (int j = 0; j < methods.length; j++) {
+ reportMethod(methods[j], RELEVANCE_METHODS + relevance);
+ }
+ group.clear();
+ }
+ relevance--;
+ }
+
+ }
+
+ private void completeClassMethods(
+ org.eclipse.dltk.core.ISourceModule modelModule,
+ ModuleDeclaration moduleDeclaration, RubyMixinClass rubyClass,
+ String prefix) {
+ CompletionMixinMethodRequestor mixinSearchRequestor = new CompletionMixinMethodRequestor(
+ rubyClass);
+ rubyClass.findMethods(prefix, true, mixinSearchRequestor);
+ mixinSearchRequestor.flush();
+ }
+
+ private void completeClassMethods(
+ org.eclipse.dltk.core.ISourceModule modelModule,
+ ModuleDeclaration moduleDeclaration, IEvaluatedType type,
+ String prefix) {
+ if (type instanceof RubyClassType) {
+ RubyClassType rubyClassType = (RubyClassType) type;
+ RubyMixinClass rubyClass = RubyMixinModel.getInstance()
+ .createRubyClass(rubyClassType);
+ if (rubyClass != null) {
+ completeClassMethods(modelModule, moduleDeclaration, rubyClass,
+ prefix);
+ }
+
+ } else if (type instanceof AmbiguousType) {
+ AmbiguousType type2 = (AmbiguousType) type;
+ IEvaluatedType[] possibleTypes = type2.getPossibleTypes();
+ for (int i = 0; i < possibleTypes.length; i++) {
+ completeClassMethods(modelModule, moduleDeclaration,
+ possibleTypes[i], prefix);
+ }
+ }
+ }
+
+ private void completeClassMethods(
+ org.eclipse.dltk.core.ISourceModule modelModule,
+ ModuleDeclaration moduleDeclaration, ASTNode receiver,
+ String pattern) {
+ ExpressionTypeGoal goal = new ExpressionTypeGoal(new BasicContext(
+ modelModule, moduleDeclaration), receiver);
+ IEvaluatedType type = inferencer.evaluateType(goal, 2000);
+ completeClassMethods(modelModule, moduleDeclaration, type, pattern);
+ }
+
+ private void completeGlobalVar(org.eclipse.dltk.core.ISourceModule module,
+ ModuleDeclaration moduleDeclaration, String prefix, int position) {
+ int relevance = 424242;
+ this.setSourceRange(position - prefix.length(), position);
+
+ IMixinElement[] elements = RubyMixinModel.getRawInstance().find(
+ prefix + "*");
+
+ // String[] findKeys = RubyMixinModel.getRawInstance().findKeys(
+ // prefix + "*");
+ for (int i = 0; i < elements.length; i++) {
+ IRubyMixinElement rubyElement = RubyMixinModel.getInstance()
+ .createRubyElement(elements[i]);
+ if (rubyElement instanceof RubyMixinVariable) {
+ RubyMixinVariable variable = (RubyMixinVariable) rubyElement;
+ IField[] sourceFields = variable.getSourceFields();
+ for (int j = 0; j < sourceFields.length; j++) {
+ if (sourceFields[j] != null) {
+ reportField(sourceFields[j], relevance--);
+ break;
+ }
+ }
+ }
+ }
+
+ for (int i = 0; i < globalVars.length; i++) {
+ if (globalVars[i].startsWith(prefix))
+ reportField(new FakeField((ModelElement) module, globalVars[i],
+ 0, 0), relevance--);
+ }
+ }
+
+ private void completeSimpleRef(org.eclipse.dltk.core.ISourceModule module,
+ ModuleDeclaration moduleDeclaration, String prefix, int position) {
+ int relevance = 424242;
+
+ this.setSourceRange(position - prefix.length(), position);
+ ASTNode[] wayToNode = ASTUtils.restoreWayToNode(moduleDeclaration,
+ this.completionNode);
+ for (int i = wayToNode.length - 1; i > 0; i--) {
+ if (wayToNode[i] instanceof RubyBlock) {
+ RubyBlock rubyBlock = (RubyBlock) wayToNode[i];
+ Set vars = rubyBlock.getVars();
+ for (Iterator iterator = vars.iterator(); iterator.hasNext();) {
+ ASTNode n = (ASTNode) iterator.next();
+ if (n instanceof RubyDAssgnExpression) {
+ RubyDAssgnExpression rd = (RubyDAssgnExpression) n;
+ if (rd.getName().startsWith(prefix)) {
+ reportField(new FakeField((ModelElement) module, rd
+ .getName(), 0, 0), relevance--);
+ }
+ }
+ }
+ }
+ }
+
+ if (prefix.startsWith("$")) { // globals
+ completeGlobalVar(module, moduleDeclaration, prefix, position);
+ } else { // class & instance & locals
+ IField[] fields = RubyModelUtils.findFields(module,
+ moduleDeclaration, prefix, position);
+ for (int i = 0; i < fields.length; i++) {
+ reportField(fields[i], relevance--);
+ }
+ }
+
+ }
+
+ private void reportSubElements(org.eclipse.dltk.core.ISourceModule module,
+ IEvaluatedType type, String prefix) {
+ int relevance = 424242;
+
+ ArrayList types = new ArrayList();
+ ArrayList methods = new ArrayList();
+ ArrayList fields = new ArrayList();
+
+ if (type instanceof RubyClassType) {
+ RubyClassType rubyClassType = (RubyClassType) type;
+ IMixinElement mixinElement = model.get(rubyClassType.getModelKey());
+ if (mixinElement != null) {
+ IMixinElement[] children = mixinElement.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ Object[] infos = children[i].getAllObjects();
+ for (int j = 0; j < infos.length; j++) {
+ RubyMixinElementInfo obj = (RubyMixinElementInfo) infos[j];
+ if (obj.getObject() == null)
+ continue;
+ if (obj.getKind() == RubyMixinElementInfo.K_CLASS
+ || obj.getKind() == RubyMixinElementInfo.K_MODULE) {
+ IType type2 = (IType) obj.getObject();
+ if (type2 != null
+ && type2.getElementName()
+ .startsWith(prefix)) {
+ // reportType(type2, relevance--);
+ types.add(type2);
+ }
+ } else if (obj.getKind() == RubyMixinElementInfo.K_METHOD) {
+ IMethod method2 = (IMethod) obj.getObject();
+ if (method2 != null
+ && method2.getElementName().startsWith(
+ prefix)) {
+ // reportMethod(method2, relevance--);
+ methods.add(method2);
+ }
+ }
+ if (obj.getKind() == RubyMixinElementInfo.K_VARIABLE) {
+ IField fff = (IField) obj.getObject();
+ if (fff != null
+ && fff.getElementName().startsWith(prefix)) {
+ // reportField(fff, relevance--);
+ fields.add(fff);
+ }
+ }
+ break;
+ }
+
+ }
+ }
+ } else {
+ // never should be here
+ }
+
+ Collections.sort(fields, modelElementComparator);
+ for (Iterator iterator = fields.iterator(); iterator.hasNext();) {
+ IField t = (IField) iterator.next();
+ reportField(t, relevance--);
+ }
+
+ Collections.sort(types, modelElementComparator);
+ for (Iterator iterator = types.iterator(); iterator.hasNext();) {
+ IType t = (IType) iterator.next();
+ reportType(t, relevance--);
+ }
+
+ Collections.sort(methods, modelElementComparator);
+ for (Iterator iterator = methods.iterator(); iterator.hasNext();) {
+ IMethod t = (IMethod) iterator.next();
+ reportMethod(t, relevance--);
+ }
+
+ }
+
+ private void completeColonExpression(
+ org.eclipse.dltk.core.ISourceModule module,
+ ModuleDeclaration moduleDeclaration, RubyColonExpression node,
+ int position) {
+ String content;
+ try {
+ content = module.getSource();
+ } catch (ModelException e) {
+ return;
+ }
+ int pos = (node.getLeft() != null) ? (node.getLeft().sourceEnd() + 2)
+ : (node.sourceStart());
+ String starting = null;
+ try {
+ starting = content.substring(pos, position).trim();
+ } catch (IndexOutOfBoundsException e) {
+ e.printStackTrace();
+ return;
+ }
+
+ if (starting.startsWith("::")) {
+ this.setSourceRange(position - starting.length() + 2, position);
+ completeConstant(module, moduleDeclaration, starting.substring(2),
+ position, true);
+ return;
+ }
+
+ this.setSourceRange(position - starting.length(), position);
+
+ ExpressionTypeGoal goal = new ExpressionTypeGoal(new BasicContext(
+ module, moduleDeclaration), node.getLeft());
+ IEvaluatedType type = inferencer.evaluateType(goal, 3000);
+ reportSubElements(module, type, starting);
+ }
+
+ private void completeConstant(org.eclipse.dltk.core.ISourceModule module,
+ ModuleDeclaration moduleDeclaration, String prefix, int position,
+ boolean topLevelOnly) {
+
+ if (!topLevelOnly) {
+ IMixinElement[] modelStaticScopes = RubyTypeInferencingUtils
+ .getModelStaticScopes(model, moduleDeclaration, position);
+ for (int i = modelStaticScopes.length - 1; i >= 0; i--) {
+ IMixinElement scope = modelStaticScopes[i];
+ if (scope == null)
+ continue;
+ reportSubElements(module, new RubyClassType(scope.getKey()),
+ prefix);
+ }
+ }
+
+ int relevance = 4242;
+
+ // try {
+ if (prefix.length() > 0) {
+ String varkey = "Object" + MixinModel.SEPARATOR + prefix;
+ RubyMixinModel rubyModel = RubyMixinModel.getInstance();
+ String[] keys2 = rubyModel.getRawModel().findKeys(varkey + "*");
+ for (int i = 0; i < keys2.length; i++) {
+ IRubyMixinElement element = rubyModel
+ .createRubyElement(keys2[i]);
+ if (element instanceof RubyMixinVariable) {
+ RubyMixinVariable variable = (RubyMixinVariable) element;
+ IField[] sourceFields = variable.getSourceFields();
+ for (int j = 0; j < sourceFields.length; j++) {
+ if (sourceFields[j] != null) {
+ reportField(sourceFields[j], relevance--);
+ break;
+ }
+ }
+ }
+ }
+ }
+ // } catch (ModelException e) {
+ // e.printStackTrace();
+ // }
+
+ HashSet names = new HashSet();
+ IType[] allTypes = RubyTypeInferencingUtils.getAllTypes(module, prefix);
+ for (int i = 0; i < allTypes.length; i++) {
+ String elementName = allTypes[i].getElementName();
+ if (names.contains(elementName))
+ continue;
+ names.add(elementName);
+ reportType(allTypes[i], relevance--);
+ }
+
+ }
+
+ private void completeConstant(org.eclipse.dltk.core.ISourceModule module,
+ ModuleDeclaration moduleDeclaration, ConstantReference node,
+ int position) {
+ String content;
+ try {
+ content = module.getSource();
+ } catch (ModelException e) {
+ return;
+ }
+
+ String prefix = content.substring(node.sourceStart(), position);
+ this.setSourceRange(position - prefix.length(), position);
+ completeConstant(module, moduleDeclaration, prefix, position, false);
+ }
+
+ private void completeCall(org.eclipse.dltk.core.ISourceModule module,
+ ModuleDeclaration moduleDeclaration, CallExpression node,
+ int position) {
+ ASTNode receiver = node.getReceiver();
+
+ String content;
+ try {
+ content = module.getSource();
+ } catch (ModelException e) {
+ return;
+ }
+
+ int pos = (receiver != null) ? (receiver.sourceEnd() + 1) : (node
+ .sourceStart());
+
+ for (int t = 0; t < 2; t++) { // correct not more 2 chars
+ if (pos < position
+ && !RubySyntaxUtils.isStrictIdentifierCharacter(content
+ .charAt(pos))) // for (...).name and Foo::name
+ // calls
+ pos++;
+ }
+
+ String starting = content.substring(pos, position).trim();
+
+ if (receiver == null)
+ completeSimpleRef(module, moduleDeclaration, starting, position);
+
+ this.setSourceRange(position - starting.length(), position);
+
+ if (starting.startsWith("__")) {
+ String[] keywords = RubyKeyword.findByPrefix("__");
+ for (int j = 0; j < keywords.length; j++) {
+ reportKeyword(keywords[j]);
+ }
+ }
+
+ if (receiver != null) {
+ completeClassMethods(module, moduleDeclaration, receiver, starting);
+ } else {
+ IClassType self = RubyTypeInferencingUtils.determineSelfClass(
+ module, moduleDeclaration, position);
+ completeClassMethods(module, moduleDeclaration, self, starting);
+ }
+
+ }
+
+ protected String processFieldName(IField field, String token) {
+ return field.getElementName();
+ }
+
+ private void reportMethod(IMethod method, int rel) {
+ this.intresting.add(method);
+ String elementName = method.getElementName();
+ if (completedNames.contains(elementName)) {
+ return;
+ }
+ completedNames.add(elementName);
+ if (elementName.indexOf('.') != -1) {
+ elementName = elementName.substring(elementName.indexOf('.') + 1);
+ }
+ char[] name = elementName.toCharArray();
+ char[] compl = name;
+
+ int relevance = rel;
+
+ // accept result
+ noProposal = false;
+ if (!requestor.isIgnored(CompletionProposal.METHOD_DECLARATION)) {
+ CompletionProposal proposal = createProposal(
+ CompletionProposal.METHOD_DECLARATION,
+ actualCompletionPosition);
+
+ String[] params = null;
+ try {
+ params = method.getParameters();
+ } catch (ModelException e) {
+ e.printStackTrace();
+ }
+
+ if (params != null && params.length > 0) {
+ char[][] args = new char[params.length][];
+ for (int i = 0; i < params.length; ++i) {
+ args[i] = params[i].toCharArray();
+ }
+ proposal.setParameterNames(args);
+ }
+
+ proposal.setModelElement(method);
+ proposal.setName(name);
+ proposal.setCompletion(compl);
+ try {
+ proposal.setFlags(method.getFlags());
+ } catch (ModelException e) {
+ RubyPlugin.log(e);
+ }
+ proposal.setReplaceRange(this.startPosition - this.offset,
+ this.endPosition - this.offset);
+ proposal.setRelevance(relevance);
+ this.requestor.accept(proposal);
+ if (DEBUG) {
+ this.printDebug(proposal);
+ }
+ }
+
+ }
+
+ private void reportType(IType type, int rel) {
+ this.intresting.add(type);
+ String elementName = type.getElementName();
+ if (completedNames.contains(elementName)) {
+ return;
+ }
+ completedNames.add(elementName);
+ char[] name = elementName.toCharArray();
+ if (name.length == 0)
+ return;
+
+ int relevance = rel;
+
+ // accept result
+ noProposal = false;
+ if (!requestor.isIgnored(CompletionProposal.TYPE_REF)) {
+ CompletionProposal proposal = createProposal(
+ CompletionProposal.TYPE_REF, actualCompletionPosition);
+
+ proposal.setModelElement(type);
+ proposal.setName(name);
+ proposal.setCompletion(elementName.toCharArray());
+ // proposal.setFlags(Flags.AccDefault);
+ try {
+ proposal.setFlags(type.getFlags());
+ } catch (ModelException e) {
+ }
+ proposal.setReplaceRange(this.startPosition - this.offset,
+ this.endPosition - this.offset);
+ proposal.setRelevance(relevance);
+ this.requestor.accept(proposal);
+ if (DEBUG) {
+ this.printDebug(proposal);
+ }
+ }
+
+ }
+
+ private void reportField(IField field, int rel) {
+ this.intresting.add(field);
+ String elementName = field.getElementName();
+ if (completedNames.contains(elementName)) {
+ return;
+ }
+ completedNames.add(elementName);
+ char[] name = elementName.toCharArray();
+ if (name.length == 0)
+ return;
+
+ int relevance = rel;
+
+ // accept result
+ noProposal = false;
+ if (!requestor.isIgnored(CompletionProposal.FIELD_REF)) {
+ CompletionProposal proposal = createProposal(
+ CompletionProposal.FIELD_REF, actualCompletionPosition);
+
+ proposal.setModelElement(field);
+ proposal.setName(name);
+ proposal.setCompletion(elementName.toCharArray());
+ // proposal.setFlags(Flags.AccDefault);
+ proposal.setReplaceRange(this.startPosition - this.offset,
+ this.endPosition - this.offset);
+ proposal.setRelevance(relevance);
+ this.requestor.accept(proposal);
+ if (DEBUG) {
+ this.printDebug(proposal);
+ }
+ }
+
+ }
+
+ private void reportKeyword(String name) {
+ // accept result
+ noProposal = false;
+ if (!requestor.isIgnored(CompletionProposal.FIELD_REF)) {
+ CompletionProposal proposal = createProposal(
+ CompletionProposal.KEYWORD, actualCompletionPosition);
+
+ proposal.setName(name.toCharArray());
+ proposal.setCompletion(name.toCharArray());
+ // proposal.setFlags(Flags.AccDefault);
+ proposal.setReplaceRange(this.startPosition - this.offset,
+ this.endPosition - this.offset);
+ proposal.setRelevance(RELEVANCE_KEYWORD);
+ this.requestor.accept(proposal);
+ if (DEBUG) {
+ this.printDebug(proposal);
+ }
+ }
+
+ }
+
}
\ No newline at end of file