package org.eclipse.dltk.xotcl.internal.core; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.eclipse.dltk.ast.ASTNode; import org.eclipse.dltk.ast.ASTVisitor; import org.eclipse.dltk.ast.declarations.FieldDeclaration; import org.eclipse.dltk.ast.declarations.TypeDeclaration; import org.eclipse.dltk.ast.expressions.Expression; import org.eclipse.dltk.ast.references.SimpleReference; import org.eclipse.dltk.ast.statements.Statement; import org.eclipse.dltk.codeassist.complete.CompletionNodeFound; import org.eclipse.dltk.core.CompletionProposal; import org.eclipse.dltk.core.CompletionRequestor; import org.eclipse.dltk.core.DLTKCore; 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.IMixinRequestor; import org.eclipse.dltk.tcl.ast.TclStatement; import org.eclipse.dltk.tcl.core.TclParseUtil; import org.eclipse.dltk.tcl.core.extensions.ICompletionExtension; import org.eclipse.dltk.tcl.internal.core.codeassist.FieldNameProvider; import org.eclipse.dltk.tcl.internal.core.codeassist.TclCompletionEngine; import org.eclipse.dltk.tcl.internal.core.codeassist.TclResolver; import org.eclipse.dltk.tcl.internal.core.codeassist.completion.CompletionOnKeywordArgumentOrFunctionArgument; import org.eclipse.dltk.tcl.internal.core.codeassist.completion.CompletionOnKeywordOrFunction; import org.eclipse.dltk.tcl.internal.core.codeassist.completion.CompletionOnVariable; import org.eclipse.dltk.tcl.internal.core.codeassist.completion.TclCompletionParser; import org.eclipse.dltk.xotcl.core.IXOTclModifiers; import org.eclipse.dltk.xotcl.core.XOTclParseUtil; import org.eclipse.dltk.xotcl.core.ast.xotcl.XOTclExInstanceVariable; import org.eclipse.dltk.xotcl.core.ast.xotcl.XOTclInstanceVariable; import org.eclipse.dltk.xotcl.core.ast.xotcl.XOTclMethodCallStatement; import org.eclipse.dltk.xotcl.core.ast.xotcl.XOTclProcCallStatement; import org.eclipse.dltk.xotcl.internal.core.search.mixin.model.XOTclClass; import org.eclipse.dltk.xotcl.internal.core.search.mixin.model.XOTclClassInstance; import org.eclipse.dltk.xotcl.internal.core.search.mixin.model.XOTclInstProc; import org.eclipse.dltk.xotcl.internal.core.search.mixin.model.XOTclProc; public class XOTclCompletionExtension implements ICompletionExtension { private CompletionRequestor requestor; public boolean visit(Expression s, TclCompletionParser parser, int position) { List exprs = new ArrayList(); if (s instanceof XOTclMethodCallStatement) { XOTclMethodCallStatement pcs = (XOTclMethodCallStatement) s; exprs.add(pcs.getInstNameRef()); exprs.add(pcs.getCallName()); if (pcs.getArgs() != null) { exprs.addAll(pcs.getArgs().getChilds()); } processArgumentCompletion(s, exprs, parser, position); } return false; } public boolean visit(Statement s, TclCompletionParser parser, int position) { List exprs = new ArrayList(); if (s instanceof XOTclProcCallStatement) { XOTclProcCallStatement pcs = (XOTclProcCallStatement) s; exprs.add(pcs.getInstNameRef()); exprs.add(pcs.getCallName()); if (pcs.getArguments() != null) { exprs.addAll(pcs.getArguments().getChilds()); } processArgumentCompletion(s, exprs, parser, position); } return false; } private void processArgumentCompletion(Statement s, List exprs, TclCompletionParser parser, int position) { TclStatement statement = new TclStatement(exprs); statement.setStart(s.sourceStart()); statement.setEnd(s.sourceEnd()); ASTNode completionNode = null; for (int i = 0; i < exprs.size(); ++i) { ASTNode n = (ASTNode) exprs.get(i); if (n.sourceStart() <= position && n.sourceEnd() >= position) { completionNode = n; } } String token = ""; if (completionNode != null && completionNode instanceof SimpleReference) { token = ((SimpleReference) completionNode).getName(); } String[] keywords; if (token == null) { keywords = parser.checkKeywords("", TclCompletionParser.MODULE); } else { keywords = parser.checkKeywords(token, TclCompletionParser.MODULE); } if (completionNode != null) { ASTNode nde = new CompletionOnKeywordArgumentOrFunctionArgument( token, completionNode, statement, keywords); parser.setAssistNodeParent(TclParseUtil.getPrevParent(parser .getModule(), s)); throw new CompletionNodeFound(nde, null); // ((TypeDeclaration)inNode).scope } else { ASTNode nde = new CompletionOnKeywordArgumentOrFunctionArgument( token, statement, keywords, position); parser.setAssistNodeParent(TclParseUtil.getPrevParent(parser .getModule(), s)); throw new CompletionNodeFound(nde, null); // ((TypeDeclaration)inNode).scope } } public void completeOnKeywordOrFunction(CompletionOnKeywordOrFunction key, ASTNode astNodeParent, TclCompletionEngine engine) { if (!engine.getRequestor().isIgnored(CompletionProposal.TYPE_REF)) { Set methodNames = new HashSet(); char[] token = key.getToken(); token = engine.removeLastColonFromToken(token); findLocalXOTclClasses(token, methodNames, astNodeParent, engine); // findLocalClasses findXOTclClasses(token, methodNames, engine); } if (!engine.getRequestor().isIgnored(CompletionProposal.FIELD_REF)) { Set methodNames = new HashSet(); char[] token = key.getToken(); token = engine.removeLastColonFromToken(token); // findLocalClassInstances findLocalXOTclClassInstances(token, methodNames, astNodeParent, engine); findXOTclClassInstances(token, methodNames, engine); } } private void findLocalXOTclClasses(char[] token, Set methodNames, ASTNode astNodeParent, TclCompletionEngine engine) { ASTNode parent = TclParseUtil.getScopeParent(engine.getAssistParser() .getModule(), astNodeParent); // We need to process all xotcl classes. Set classes = new HashSet(); findXOTclClassessIn(parent, classes, engine); engine.removeSameFrom(methodNames, classes, new String(token)); engine.findTypes(token, true, engine.toList(classes)); methodNames.addAll(classes); } private void findLocalXOTclClassInstances(char[] token, Set methodNames, ASTNode astNodeParent, TclCompletionEngine engine) { ASTNode parent = TclParseUtil.getScopeParent(engine.getAssistParser() .getModule(), astNodeParent); // We need to process all xotcl classes. Set classes = new HashSet(); findXOTclClassInstancesIn(parent, classes, engine); final String tok = new String(token); engine.removeSameFrom(methodNames, classes, tok); engine.findFields(token, true, engine.toList(classes), new FieldNameProvider(tok)); methodNames.addAll(classes); // Also use not fully qualified names // String elementFQN = TclParseUtil.getElementFQN(parent, "::", // parser.getModule()); } private void findXOTclClassessIn(ASTNode parent, Set classes, final TclCompletionEngine engine) { // List statements = TclASTUtil.getStatements(parent); final List result = new ArrayList(); final TclResolver resolver = new TclResolver(engine.getSourceModule(), engine.getAssistParser().getModule(), null); try { engine.getAssistParser().getModule().traverse(new ASTVisitor() { // for (Iterator iterator = statements.iterator(); // iterator.hasNext();) { // ASTNode nde = (ASTNode) iterator.next(); public boolean visit(TypeDeclaration type) { if ((type.getModifiers() & IXOTclModifiers.AccXOTcl) != 0 && type.sourceStart() < engine .getActualCompletionPosition()) { // we need to find model element for selected type. resolver.searchAddElementsTo(engine.getAssistParser() .getModule().getStatements(), type, engine .getSourceModule(), result); } return true; } }); } catch (Exception e) { if (DLTKCore.DEBUG) { e.printStackTrace(); } } classes.addAll(result); } private void findXOTclClassInstancesIn(ASTNode parent, Set classes, final TclCompletionEngine engine) { // List statements = TclASTUtil.getStatements(parent); final List result = new ArrayList(); final TclResolver resolver = new TclResolver(engine.getSourceModule(), engine.getAssistParser().getModule(), null); try { parent.traverse(new ASTVisitor() { public boolean visit(Statement st) { if (st instanceof XOTclInstanceVariable || st instanceof XOTclExInstanceVariable) { // we need to find model element for selected type. resolver.searchAddElementsTo(engine.getAssistParser() .getModule().getStatements(), st, engine .getSourceModule(), result); } return true; } }); } catch (Exception e) { if (DLTKCore.DEBUG) { e.printStackTrace(); } } classes.addAll(result); } private void findXOTclClassInstances(char[] token, Set methodNames, TclCompletionEngine engine) { // IDLTKSearchScope scope = SearchEngine.createWorkspaceScope(toolkit); String to_ = new String(token); String to = to_; if (to.startsWith("::")) { to = to.substring(2); } if (to.length() == 0) { return; } Set elements = new HashSet(); elements.addAll(methodNames); findClassesInstanceFromMixin(elements, to + "*", engine); engine.removeSameFrom(methodNames, elements, to); engine.findFields(token, true, engine.toList(elements), new FieldNameProvider(to_)); methodNames.addAll(elements); } private void completionForInstanceVariableMethods(FieldDeclaration var, char[] token, Set methodNames, TclCompletionEngine engine) { String keyPrefix = null; if (var instanceof XOTclInstanceVariable) { XOTclInstanceVariable ivar = (XOTclInstanceVariable) var; TypeDeclaration declaringType = ivar.getDeclaringType(); keyPrefix = TclParseUtil.getElementFQN(declaringType, IMixinRequestor.MIXIN_NAME_SEPARATOR, engine .getAssistParser().getModule()); if (keyPrefix.startsWith(IMixinRequestor.MIXIN_NAME_SEPARATOR)) { keyPrefix = keyPrefix.substring(1); } } else if (var instanceof XOTclExInstanceVariable) { XOTclExInstanceVariable ivar = (XOTclExInstanceVariable) var; String className = ivar.getDeclaringClassParameter().getClassName(); if (className.startsWith("::")) { className = className.substring(2); } keyPrefix = className.replaceAll("::", IMixinRequestor.MIXIN_NAME_SEPARATOR); } Set methods = new HashSet(); findClassesFromMixin(methods, keyPrefix, engine); Set result = new HashSet(); // replace class name with methods. while (methods.size() > 0) { IModelElement e = (IModelElement) methods.iterator().next(); methods.remove(e); if (e instanceof IType) { IType type = (IType) e; try { IMethod[] ms = type.getMethods(); result.addAll(Arrays.asList(ms)); } catch (ModelException e1) { if (DLTKCore.DEBUG) { e1.printStackTrace(); } } // Add super types information. try { String[] superClasses = type.getSuperClasses(); if (superClasses != null) { for (int i = 0; i < superClasses.length; i++) { String key = superClasses[i]; if (key.startsWith("::")) { key = key.substring(2); } if (key.length() > 0) { findClassesFromMixin(methods, key, engine); } } } } catch (ModelException e1) { if (DLTKCore.DEBUG) { e1.printStackTrace(); } } } else if (e instanceof IMethod) { result.add(e); } } findMethodsShortName(token, result, methodNames, engine); // We need to handle supers addKeywords(token, XOTclKeywords.XOTclCommandClassArgs, methodNames, engine); addKeywords(token, XOTclKeywords.XOTclCommandObjectArgs, methodNames, engine); } // protected boolean methodCanBeAdded(ASTNode nde, TclCompletionEngine // engine) { // if (nde instanceof XOTclMethodDeclaration) { // return false; // } // return super.methodCanBeAdded(nde); // } private void completeClassMethods(String name, char[] cs, Set methodNames, TclCompletionEngine engine) { Set completions = new HashSet(); if (name.startsWith("::")) { name = name.substring(2); } findClassesFromMixin(completions, name, engine); if (completions.size() >= 1) { // We found class with such name, so // this is method completion. Set methods = new HashSet(); methods.addAll(methodNames); findProcsFromMixin(methods, name + "::*", engine); methods.removeAll(methodNames); findMethodsShortName(cs, methods, methodNames, engine); // We also need to add Object and Class methods // We need to add superclass methods addKeywords(cs, XOTclKeywords.XOTclCommandClassArgs, methodNames, engine); addKeywords(cs, XOTclKeywords.XOTclCommandObjectArgs, methodNames, engine); } } private void addKeywords(char[] cs, String[] keywords, Set methodNames, TclCompletionEngine engine) { List k = new ArrayList(); String token = new String(cs); for (int i = 0; i < keywords.length; ++i) { String kkw = keywords[i]; if (kkw.startsWith(token)) { if (!methodNames.contains(kkw)) { k.add(kkw); methodNames.add(kkw); } } } String kw[] = (String[]) k.toArray(new String[k.size()]); engine.findKeywords(cs, kw, true); } private void findMethodsShortName(char[] cs, Set methods, Set allMethods, TclCompletionEngine engine) { List methodsList = engine.toList(methods); List methodNames = new ArrayList(); List remove = new ArrayList(); for (Iterator iterator = methodsList.iterator(); iterator.hasNext();) { IMethod method = (IMethod) iterator.next(); String methodName = method.getElementName(); if (!methodNames.contains(methodName)) { methodNames.add(methodName); allMethods.add(methodName); } else { remove.add(method); } } for (Iterator iterator = remove.iterator(); iterator.hasNext();) { Object object = (Object) iterator.next(); methodsList.remove(object); } engine.findMethods(cs, true, methodsList, methodNames); } protected void findProcsFromMixin(final Set methods, String tok, TclCompletionEngine engine) { engine.findMixinTclElement(methods, tok, XOTclProc.class); } protected void findInstProcsFromMixin(final Set methods, String tok, TclCompletionEngine engine) { engine.findMixinTclElement(methods, tok, XOTclInstProc.class); } private void findXOTclClasses(char[] token, Set methodNames, TclCompletionEngine engine) { // IDLTKSearchScope scope = SearchEngine.createWorkspaceScope(toolkit); String to_ = new String(token); String to = to_; if (to.startsWith("::")) { to = to.substring(2); } if (to.length() == 0) { return; } Set methods = new HashSet(); methods.addAll(methodNames); findClassesFromMixin(methods, to + "*", engine); methods.removeAll(methodNames); engine.removeSameFrom(methodNames, methods, to_); engine.findTypes(token, true, engine.toList(methods)); } protected void findClassesFromMixin(final Set completions, String tok, TclCompletionEngine engine) { engine.findMixinTclElement(completions, tok, XOTclClass.class); } protected void findClassesInstanceFromMixin(final Set completions, String tok, TclCompletionEngine engine) { engine.findMixinTclElement(completions, tok, XOTclClassInstance.class); } private FieldDeclaration searchFieldFromMixin(String name, TclCompletionEngine engine) { return null; } public void completeOnKeywordArgumentsOne(String name, char[] token, CompletionOnKeywordArgumentOrFunctionArgument compl, Set methodNames, TclStatement st, TclCompletionEngine engine) { // Check for class and its methods. completeClassMethods(name, token, methodNames, engine); // Lets find instance with specified name. FieldDeclaration var = XOTclParseUtil .findXOTclInstanceVariableDeclarationFrom(engine .getAssistParser().getModule(), TclParseUtil .getScopeParent(engine.getAssistParser().getModule(), st), name); if (var == null) { var = searchFieldFromMixin(name, engine); } if (var != null) { completionForInstanceVariableMethods(var, token, methodNames, engine); } } public void setRequestor(CompletionRequestor requestor) { this.requestor = requestor; } public void completeOnVariables(CompletionOnVariable astNode, TclCompletionEngine engine) { } public boolean modelFilter(Set completions, IModelElement modelElement) { return true; } }