/******************************************************************************* * Copyright (c) 2005, 2017 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.tcl.internal.core.codeassist; import java.util.ArrayList; 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.Argument; import org.eclipse.dltk.ast.declarations.FieldDeclaration; import org.eclipse.dltk.ast.declarations.MethodDeclaration; import org.eclipse.dltk.ast.declarations.ModuleDeclaration; 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.Block; import org.eclipse.dltk.codeassist.AssistParser; import org.eclipse.dltk.codeassist.IAssistParser; import org.eclipse.dltk.codeassist.ScriptSelectionEngine; import org.eclipse.dltk.compiler.env.IModuleSource; import org.eclipse.dltk.core.DLTKCore; import org.eclipse.dltk.core.IDLTKLanguageToolkit; import org.eclipse.dltk.core.IModelElement; import org.eclipse.dltk.core.IParent; import org.eclipse.dltk.core.IScriptProject; import org.eclipse.dltk.core.ModelException; import org.eclipse.dltk.core.mixin.IMixinElement; import org.eclipse.dltk.core.mixin.IMixinRequestor; import org.eclipse.dltk.internal.codeassist.select.SelectionNodeFound; import org.eclipse.dltk.tcl.ast.ITclStatementLookLike; import org.eclipse.dltk.tcl.ast.TclStatement; import org.eclipse.dltk.tcl.core.TclLanguageToolkit; import org.eclipse.dltk.tcl.core.TclParseUtil; import org.eclipse.dltk.tcl.core.extensions.ISelectionExtension; import org.eclipse.dltk.tcl.internal.core.TclExtensionManager; import org.eclipse.dltk.tcl.internal.core.codeassist.TclResolver.IResolveElementParent; import org.eclipse.dltk.tcl.internal.core.codeassist.selection.SelectionOnAST; import org.eclipse.dltk.tcl.internal.core.codeassist.selection.SelectionOnKeywordOrFunction; import org.eclipse.dltk.tcl.internal.core.codeassist.selection.SelectionOnNode; import org.eclipse.dltk.tcl.internal.core.codeassist.selection.SelectionOnVariable; import org.eclipse.dltk.tcl.internal.core.packages.TclBuildPathPackageCollector; import org.eclipse.dltk.tcl.internal.core.search.mixin.TclMixinModel; import org.eclipse.dltk.tcl.internal.core.search.mixin.model.TclField; import org.eclipse.dltk.tcl.internal.core.search.mixin.model.TclNamespaceImport; import org.eclipse.dltk.tcl.internal.core.search.mixin.model.TclProc; import org.eclipse.dltk.tcl.internal.parser.OldTclParserUtils; public class TclSelectionEngine extends ScriptSelectionEngine { public static boolean DEBUG = DLTKCore.DEBUG_SELECTION; protected int actualSelectionStart; protected int actualSelectionEnd; protected List<IModelElement> selectionElements = new ArrayList<>(); protected AssistParser parser = new AssistParser(new TclSelectionParser()); protected org.eclipse.dltk.core.ISourceModule sourceModule; protected IDLTKLanguageToolkit toolkit; protected ISelectionExtension[] extensions; private TclBuildPathPackageCollector packageCollector; public TclSelectionEngine() { this.toolkit = TclLanguageToolkit.getDefault(); this.extensions = TclExtensionManager.getDefault() .getSelectionExtensions(); } @Override public IModelElement[] select(IModuleSource sourceUnit, int selectionSourceStart, int selectionSourceEnd) { sourceModule = (org.eclipse.dltk.core.ISourceModule) sourceUnit .getModelElement(); String content = sourceUnit.getSourceContents(); if (DEBUG) { System.out.print("SELECTION IN "); //$NON-NLS-1$ System.out.print(sourceUnit.getFileName()); System.out.print(" FROM "); //$NON-NLS-1$ System.out.print(selectionSourceStart); System.out.print(" TO "); //$NON-NLS-1$ System.out.println(selectionSourceEnd); System.out.println("SELECTION - Source :"); //$NON-NLS-1$ System.out.println(content); } if (!checkSelection(content, selectionSourceStart, selectionSourceEnd)) { return new IModelElement[0]; } if (DEBUG) { System.out.print("SELECTION - Checked : \""); //$NON-NLS-1$ System.out.print(content.substring(actualSelectionStart, actualSelectionEnd)); System.out.println('"'); } try { ModuleDeclaration parsedUnit = this.parser.parse(sourceUnit); packageCollector = new TclBuildPathPackageCollector(); try { parsedUnit.traverse(packageCollector); } catch (Exception e1) { if (DLTKCore.DEBUG) { e1.printStackTrace(); } } if (parsedUnit != null) { try { parser.parseBlockStatements(parsedUnit, this.actualSelectionStart); if (DEBUG) { System.out.println("COMPLETION - AST :"); //$NON-NLS-1$ System.out.println(parsedUnit.toString()); } } catch (SelectionNodeFound e) { // completionNodeFound = true; if (e.getNode() != null) { if (DEBUG) { System.out.print("COMPLETION - Completion node : "); //$NON-NLS-1$ System.out.println(e.getNode().toString()); if (this.parser.getAssistNodeParent() != null) { System.out.print("COMPLETION - Parent Node : "); //$NON-NLS-1$ System.out.println( this.parser.getAssistNodeParent()); } } // if null then we found a problem in the completion // node select(e.getNode(), this.parser.getAssistNodeParent()); } } } } catch (IndexOutOfBoundsException e) { // work-around internal failure // - 1GEMF6D if (DEBUG) { System.out.println("Exception caught by SelectionEngine:"); //$NON-NLS-1$ e.printStackTrace(System.out); } } return selectionElements .toArray(new IModelElement[selectionElements.size()]); } protected ASTNode parseBlockStatements(TypeDeclaration type, ModuleDeclaration unit, int position) { ASTNode result = parser.parseBlockStatements(type, unit, position); if (result != null) { return result; } if (type instanceof ITclStatementLookLike) { TclStatement statement = ((ITclStatementLookLike) type) .getStatement(); ASTNode inNode = TclParseUtil.getScopeParent(parser.getModule(), type); this.getAssistParser().parseBlockStatements(statement, inNode, position); SelectionOnNode nde = new SelectionOnNode(type); nde.setPosition(position); throw new SelectionNodeFound(nde); } return null; } protected void select(ASTNode astNode, ASTNode astNodeParent) { if (astNode instanceof SelectionOnKeywordOrFunction) { SelectionOnKeywordOrFunction key = (SelectionOnKeywordOrFunction) astNode; // findKeywords(key.getToken(), key.getPossibleKeywords(), // key.canCompleteEmptyToken()); String name = key.getName(); if (name != null) { findLocalFunctions(name, astNodeParent); if (this.selectionElements.size() > 0) { return; } findMethodFromMixin(name); // findMethodFromSearch(name); // if (this.selectionElements.size() > 0) { // return; // } String fqnName = null; if (astNodeParent instanceof TypeDeclaration) { TypeDeclaration t = (TypeDeclaration) astNodeParent; fqnName = t.getEnclosingTypeName() + "::" + t.getName() + "::" + name; } else if (astNodeParent instanceof MethodDeclaration) { MethodDeclaration t = (MethodDeclaration) astNodeParent; fqnName = t.getDeclaringTypeName() + "::" + name; } if (fqnName != null) { if (!fqnName.startsWith("::")) fqnName = "::" + fqnName; findMethodFromMixin(name); // findMethodFromSearch(fqnName); } // Search in imported namespaces String currentNamespace = TclParseUtil.getElementFQN( astNodeParent, "::", getAssistParser().getModule()); Set processed = new HashSet(); selectNamespaceImport(name, currentNamespace, processed); if (!currentNamespace.equals("")) { selectNamespaceImport(name, "", processed); } } for (int i = 0; i < this.extensions.length; i++) { this.extensions[i].selectionOnKeywordOrFunction(key, this); } } else if (astNode instanceof SelectionOnVariable) { SelectionOnVariable completion = (SelectionOnVariable) astNode; findVariables(completion.getName(), astNodeParent, astNode.sourceStart()); } else if (astNode instanceof SelectionOnAST) { ASTNode node = ((SelectionOnAST) astNode).getNode(); for (int i = 0; i < this.extensions.length; i++) { this.extensions[i].selectionOnAST(node, this); } addElementFromASTNode(node); } else if (astNode instanceof SelectionOnNode) { ASTNode node = ((SelectionOnNode) astNode).getNode(); int position = ((SelectionOnNode) astNode).getPosition(); for (int i = 0; i < this.extensions.length; i++) { this.extensions[i].selectionOnNode(node, position, this); } } } private void selectNamespaceImport(String name, String currentNamespace, Set processed) { String pattern = "@" + currentNamespace + "|*"; String[] findKeys = TclMixinModel.getInstance() .getMixin(getScriptProject()).findKeys(pattern); Set keys = new HashSet(); for (int i = 0; i < findKeys.length; i++) { if (keys.add(findKeys[i])) { TclNamespaceImport importSt = TclNamespaceImport .parseKey(findKeys[i]); if (importSt.getNamespace().equals(currentNamespace)) { if (processed.add(importSt.getImportNsName())) { findMethodFromMixinNS( importSt.getImportNsName() + "::" + name, importSt.getImportNsName()); } } } } } protected void findMethodFromMixin(String name) { if (name.startsWith("::")) { if (name.startsWith("::")) { name = name.substring(2); } } String oName = name; if (name.indexOf("::") != -1) { String[] split = TclParseUtil.tclSplit(name); oName = split[split.length - 1]; } findMethodMixin(tclNameToKey(name), oName); } protected void findMethodFromMixinNS(String name, String namespace) { if (name.startsWith("::")) { if (name.startsWith("::")) { name = name.substring(2); } } String oName = name; if (name.indexOf("::") != -1) { String[] split = TclParseUtil.tclSplit(name); oName = split[split.length - 1]; } findMethodMixinNS(tclNameToKey(name), oName, namespace); } public boolean checkMethodFrom(TypeDeclaration declaringType, SimpleReference callName, IModelElement parent) { List methodList = declaringType.getMethodList(); for (Iterator iterator = methodList.iterator(); iterator.hasNext();) { MethodDeclaration method = (MethodDeclaration) iterator.next(); if (method.getName().equals(callName.toString())) { IModelElement methodElement = TclResolver .findChildrenByName(method.getName(), (IParent) parent); addSelectionElement(methodElement); return true; } } return false; } public void fillSuperClassesTo(TypeDeclaration declaringType, List supersToHandle) { if (declaringType == null) { return; } if (declaringType.getSuperClasses() == null) { return; } List superClasses = declaringType.getSuperClasses().getChilds(); for (int i = 0; i < superClasses.size(); i++) { String superClassName = TclParseUtil .getNameFromNode((ASTNode) superClasses.get(i)); if (superClassName != null) { supersToHandle.add(superClassName); } } } protected void findVariables(String name, ASTNode parent, int beforePosition) { String originalName = name; for (int i = 0; i < this.extensions.length; i++) { this.extensions[i].findVariables(name, parent, beforePosition, this); if (this.selectionElements.size() > 0) { return; } } if (parent instanceof MethodDeclaration) { MethodDeclaration method = (MethodDeclaration) parent; List statements = method.getArguments(); if (statements != null) { for (int i = 0; i < statements.size(); ++i) { Argument a = (Argument) statements.get(i); if (a != null) { checkVariable(name, a.getName(), method); } } } // Process variable setters. statements = method.getStatements(); checkVariableStatements(name, beforePosition, statements, ""); } else if (parent instanceof ModuleDeclaration) { ModuleDeclaration module = (ModuleDeclaration) parent; checkVariableStatements(name, beforePosition, module.getStatements(), ""); } else if (parent instanceof TypeDeclaration) { TypeDeclaration type = (TypeDeclaration) parent; checkVariableStatements(name, beforePosition, type.getStatements(), ""); } else { List levels = TclParseUtil.findLevelsTo(this.parser.getModule(), parent); ASTNode realParent = findRealParent(levels); if (realParent != null) { findVariables(name, realParent, beforePosition); } } if (this.selectionElements.size() > 0) { return; } // Find global variables if (name.startsWith("$")) { name = name.substring(1); if (!name.startsWith("::")) { name = "::" + name; } String typeName = name.substring(0, name.lastIndexOf("::")); String varName = name.substring(name.lastIndexOf("::") + 2); typeName = typeName.replaceAll("::", "\\$"); try { IModelElement type = null; if (typeName.length() > 0) { type = (IModelElement) TclResolver.findTypeFrom( sourceModule.getChildren(), "", typeName, '$'); } else { type = this.sourceModule; } if (type != null && type instanceof IParent) { IModelElement field = TclResolver .findChildrenByName(varName, (IParent) type); if (field != null) { addSelectionElement(field); } } } catch (ModelException e) { if (DLTKCore.DEBUG) { e.printStackTrace(); } } // findFieldFromSearch(name); findFieldFromMixin(parent, name); } if (this.selectionElements.size() > 0) { return; } // Search from mixins if not found local. findFieldFromMixin(parent, originalName); } protected void findFieldFromMixin(ASTNode parent, String name) { // findFieldMixin(IMixinRequestor.MIXIN_NAME_SEPARATOR + // tclNameToKey(name), name); if (name.startsWith("$")) { name = name.substring(1); } if (name.startsWith("{") || name.endsWith("}")) { name = name.substring(1, name.length() - 1); } if (parent instanceof ModuleDeclaration || name.startsWith("::")) { if (name.startsWith("::")) { name = name.substring(2); } String oName = name; if (name.indexOf("::") != -1) { String[] split = TclParseUtil.tclSplit(name); oName = split[split.length - 1]; } findFieldMixin(tclNameToKey(name), oName); } else { List levels = TclParseUtil.findLevelsTo(this.parser.getModule(), parent); String keyFromLevels = getKeyFromLevels(levels); findFieldMixin( keyFromLevels + IMixinRequestor.MIXIN_NAME_SEPARATOR + name, name); } } public String tclNameToKey(String name) { return TclParseUtil.tclNameTo(name, IMixinRequestor.MIXIN_NAME_SEPARATOR); } protected void findFieldMixin(String pattern, String name) { IMixinElement[] find = TclMixinModel.getInstance() .getMixin(this.sourceModule.getScriptProject()).find(pattern); for (int i = 0; i < find.length; i++) { Object[] allObjects = find[i].getAllObjects(); for (int j = 0; j < allObjects.length; j++) { if (allObjects[j] != null && allObjects[j] instanceof TclField) { TclField field = (TclField) allObjects[j]; if (name.equals(field.getName())) { addSelectionElement(field.getModelElement()); return; } } } } } public void findMethodMixin(String pattern, String name) { IMixinElement[] find = TclMixinModel.getInstance() .getMixin(this.sourceModule.getScriptProject()).find(pattern); List<IModelElement> selections = new ArrayList<>(); for (int i = 0; i < find.length; i++) { Object[] allObjects = find[i].getAllObjects(); for (int j = 0; j < allObjects.length; j++) { if (allObjects[j] != null && allObjects[j] instanceof TclProc) { TclProc field = (TclProc) allObjects[j]; if (name.equals(field.getName())) { // addSelectionElement(field.getModelElement()); selections.add(field.getModelElement()); // return; } } } } IModelElement result[] = TclResolver.complexFilter( selections.toArray(new IModelElement[selections.size()]), this.getScriptProject(), this.packageCollector, true); for (int i = 0; i < result.length; i++) { addSelectionElement(result[i]); } } public void findMethodMixinNS(String pattern, String name, String namespace) { if (!namespace.startsWith("::")) { namespace = "::" + namespace; } if (!namespace.endsWith("::")) { namespace = namespace + "::"; } IMixinElement[] find = TclMixinModel.getInstance() .getMixin(this.sourceModule.getScriptProject()).find(pattern); for (int i = 0; i < find.length; i++) { Object[] allObjects = find[i].getAllObjects(); for (int j = 0; j < allObjects.length; j++) { if (allObjects[j] != null && allObjects[j] instanceof TclProc) { TclProc field = (TclProc) allObjects[j]; if (name.equals(field.getName())) { IModelElement element = field.getModelElement(); String fqn = TclParseUtil .getFQNFromModelElement(element, "::"); if (fqn.startsWith(namespace)) { String substring = fqn .substring(namespace.length()); if (substring.indexOf("::") == -1) { addSelectionElement(element); } } else { addSelectionElement(element); } return; } } } } } protected String getKeyFromLevels(List nodes) { return TclParseUtil.getElementFQN(nodes, IMixinRequestor.MIXIN_NAME_SEPARATOR, this.parser.getModule()); } protected ASTNode findRealParent(List levels) { for (int i = levels.size() - 1; i >= 0; --i) { ASTNode n = (ASTNode) levels.get(i); if (n instanceof MethodDeclaration || n instanceof TypeDeclaration || n instanceof ModuleDeclaration) { return n; } } return null; } protected void checkVariableStatements(String name, int beforePosition, List statements, String prefix) { if (statements != null) { for (int i = 0; i < statements.size(); ++i) { ASTNode node = (ASTNode) statements.get(i); if (node instanceof FieldDeclaration) { FieldDeclaration decl = (FieldDeclaration) node; checkVariable(name, prefix + decl.getName(), node); } if (node instanceof TclStatement && node.sourceEnd() < beforePosition) { TclStatement s = (TclStatement) node; String[] variable = OldTclParserUtils.returnVariable(s); if (variable != null) { for (int u = 0; u < variable.length; ++u) { checkVariable(name, prefix + variable[u], node); } } } if (node instanceof TypeDeclaration) { TypeDeclaration type = (TypeDeclaration) node; String nn = type.getName(); if (nn.startsWith("::")) { nn = nn.substring(2); } // TODO: Possible bug with adding to global namespace from // inner namespace. checkVariableStatements(name, beforePosition, type.getStatements(), prefix + nn + "::"); // checkVariableStatements(name, beforePosition, type // .getStatements(), ""); } // This is in case not of type declaration else if (node.sourceStart() <= beforePosition) { List statements2 = TclResolver.findExtractBlocks(node); if (statements2.size() != 0) { checkVariableStatements(name, beforePosition, statements2, prefix); } } } } } private void checkVariable(String name, String variable, ASTNode node) { String str; if (variable.indexOf('(') != -1) { variable = variable.substring(0, variable.indexOf('(')); } if (name.startsWith("${")) { str = "$" + '{' + variable + '}'; } else { str = "$" + variable; } // strip array indexes if (name.indexOf('(') != -1) { name = name.substring(0, name.indexOf('(')); } if (name.equals(str)) { addElementFromASTNode(node); } } protected void findLocalFunctions(String name, ASTNode parent) { List levels = TclParseUtil.findLevelsTo(this.parser.getModule(), parent); int len = levels.size(); ASTNode realParent = findRealParent(levels); // At first search for namespace function if (realParent instanceof MethodDeclaration && !name.startsWith("::")) { MethodDeclaration mParent = (MethodDeclaration) realParent; String pName = mParent.getName(); if (pName.indexOf("::") != -1) { pName = pName.substring(0, pName.lastIndexOf("::") + 2); processFindLocalFunctions(pName + name, levels, len); } } processFindLocalFunctions(name, levels, len); } protected void processFindLocalFunctions(String name, List levels, int len) { List visited = new ArrayList(); for (int j = 0; j < len; ++j) { ASTNode astNodeParent = (ASTNode) levels.get(len - 1 - j); boolean topLevel = false; if (name != null && name.length() > 0 && name.charAt(0) == ':') { topLevel = true; } if (astNodeParent instanceof TypeDeclaration && !topLevel) { // Add all method here. TypeDeclaration decl = (TypeDeclaration) astNodeParent; List statements = decl.getStatements(); if (statements != null) { processMethods(name, statements, "", visited); if (!name.startsWith("::")) { processMethods(decl.getName() + "::" + name, statements, "", visited); } if (selectionElements.size() > 0) { return; } } } else if (astNodeParent instanceof ModuleDeclaration) { ModuleDeclaration decl = (ModuleDeclaration) astNodeParent; List statements = decl.getStatements(); processMethods(name, statements, "", visited); if (statements != null) { if (selectionElements.size() > 0) { return; } } } } } protected void processMethods(String name, final List statements, final String namePrefix, final List visited) { if (selectionElements.size() > 0) { return; } for (int i = 0; i < statements.size(); ++i) { ASTNode nde = (ASTNode) statements.get(i); if (nde instanceof MethodDeclaration) { String mName = ((MethodDeclaration) nde).getName(); if (!mName.startsWith("::")) { mName = namePrefix + mName; } if (mName.startsWith("::::")) { mName = mName.substring(2); } if (name.startsWith("::")) { name = name.substring(2); } if (mName.startsWith("::")) { mName = mName.substring(2); } if (name.equals(mName)) { addElementFromASTNode(nde); if (selectionElements.size() > 0) { return; } } } else if (nde instanceof TypeDeclaration && !visited.contains(nde)) { List tStatements = ((TypeDeclaration) nde).getStatements(); visited.add(nde); String ndeName = ((TypeDeclaration) nde).getName(); if (ndeName.startsWith("::")) { processMethods(name, tStatements, ndeName + "::", visited); } else { processMethods(name, tStatements, namePrefix + ndeName + "::", visited); } } else { // We need to visit blocked expressions to one level. final String fname = name; ASTVisitor visitor = new ASTVisitor() { @Override public boolean visit(Expression s) throws Exception { if (s instanceof Block) { List tStatements = ((Block) s).getStatements(); visited.add(s); processMethods(fname, tStatements, namePrefix, visited); } return false; } @Override public boolean visit(MethodDeclaration s) throws Exception { return false; } @Override public boolean visit(TypeDeclaration s) throws Exception { return false; } }; try { nde.traverse(visitor); } catch (Exception e) { if (DLTKCore.DEBUG) { e.printStackTrace(); } } } visited.add(nde); } } public void addElementFromASTNode(ASTNode nde) { ModuleDeclaration module = parser.getModule(); List statements = module.getStatements(); new TclResolver(sourceModule, parser.getModule(), parentResolver) .searchAddElementsTo(statements, nde, sourceModule, this.selectionElements); } public IModelElement findElementFromNode(ASTNode nde) { ModuleDeclaration module = parser.getModule(); List statements = module.getStatements(); List<IModelElement> elements = new ArrayList<>(); new TclResolver(sourceModule, parser.getModule(), parentResolver) .searchAddElementsTo(statements, nde, sourceModule, elements); if (elements.size() == 1) { return elements.get(0); } return null; } IResolveElementParent parentResolver = (node, name, parent) -> TclSelectionEngine.this.findElementParent(node, name, parent); protected IModelElement findElementParent(ASTNode node, String name, IParent parent) { for (int i = 0; i < extensions.length; i++) { IModelElement pel = extensions[i].findElementParent(node, name, parent, this); if (pel != null) { return pel; } } return null; } protected boolean checkSelection(String source, int selectionSourceStart, int selectionSourceEnd) { boolean cheat = false; if (selectionSourceEnd < selectionSourceStart) { selectionSourceEnd = selectionSourceStart; cheat = true; } int start = OldTclParserUtils.startLineOrNoSymbol(selectionSourceStart, source); int end = OldTclParserUtils.endLineOrNoSymbol(selectionSourceEnd, source); if (end <= start) { if (cheat) return checkSelection(source, selectionSourceEnd - 1, selectionSourceEnd - 1); return false; } if (start > source.length() || end > source.length()) { if (cheat) return checkSelection(source, selectionSourceEnd - 1, selectionSourceEnd - 1); return false; } boolean isVariable = false; if (source.charAt(start) == '$') { isVariable = true; } else { if (start > 0) { // check if it is variable if (source.charAt(start - 1) == '{') { if (start - 1 > 0 && source.charAt(start - 2) == '$') { start -= 2; isVariable = true; while (end < source.length() && source.charAt(end) != '}') end++; if (end < source.length()) { end++; } } } } } if (isVariable && end < source.length() && source.charAt(end) == '(') {// it // is // array while (end < source.length() && source.charAt(end) != ')') end++; if (end < source.length()) { end++; } } if (isVariable && start + 1 < source.length() && source.charAt(start + 1) == '{') { int pos = start; while (pos < source.length() && source.charAt(pos) != '}') pos++; if (pos < source.length()) { pos++; } end = pos; } if (!(start <= end && end <= source.length())) { System.out.println(); } String sub = source.substring(start, end); // Lets check for variable with spaces selection if (!isVariable && sub.endsWith("}")) { int pos = end; while (pos > 0) { if (source.charAt(pos) == '{') { break; } pos--; } if (pos > 0) { if (source.charAt(pos - 1) == '$') { isVariable = true; start = pos - 1; sub = source.substring(start, end); } } } // If contain tabs or spaces, then from start. if (!isVariable && (sub.indexOf(' ') != -1 || sub.indexOf('\t') != -1 || sub.indexOf('\n') != -1)) { if (cheat) return checkSelection(source, selectionSourceEnd - 1, selectionSourceEnd - 1); return false; } this.actualSelectionStart = start; this.actualSelectionEnd = end; return true; } public IAssistParser getAssistParser() { return parser; } public IParent getSourceModule() { return this.sourceModule; } public IScriptProject getScriptProject() { return this.sourceModule.getScriptProject(); } public int getActualSelectionStart() { return this.actualSelectionStart; } public void addSelectionElement(IModelElement element) { if (!selectionElements.contains(element)) { this.selectionElements.add(element); } } public int getSelectionElementsSize() { return this.selectionElements.size(); } }