/// <summary> /// Find expression type in function context /// </summary> /// <param name="expression">To evaluate</param> /// <param name="context">In context</param> /// <param name="inClass">In class</param> /// <param name="complete">Complete (sub-expression) or partial (dot-completion) evaluation</param> /// <returns>Class/member struct</returns> static private ASResult EvalExpression(string expression, ASExpr context, ASClass inClass, bool complete) { DebugConsole.Trace("** EvalExpression"); DebugConsole.Trace(expression); ASResult notFound = new ASResult(); Match mSub = null; string[] tokens = expression.Split('.'); // eval first token string token = tokens[0]; ASResult head; if (token.StartsWith("#")) { mSub = re_sub.Match(token); if (mSub.Success) { string subExpr = context.SubExpressions[ Convert.ToInt16(mSub.Groups["index"].Value) ]; // parse sub expression subExpr = subExpr.Substring(1,subExpr.Length-2).Trim(); ASExpr subContext = new ASExpr(); subContext.SubExpressions = ExtractedSubex = new StringCollection(); subExpr = re_balancedParenthesis.Replace(subExpr, new MatchEvaluator(ExtractSubex)); Match m = re_refineExpression.Match(subExpr); if (!m.Success) return notFound; subExpr = re_dot.Replace( re_whiteSpace.Replace(m.Value, " ") , ".").Trim(); int space = subExpr.LastIndexOf(' '); if (space > 0) subExpr = subExpr.Substring(space+1); // eval sub expression head = EvalExpression(subExpr, subContext, inClass, true); if (head.Member != null) head.Class = ASContext.FindClassFromName(head.Member.Type, head.Class); } else { token = token.Substring(token.IndexOf('~')+1); head = EvalVariable(token, context, inClass); } } else head = EvalVariable(token, context, inClass); // no head, exit if (head.IsNull()) return notFound; if (!head.IsStatic) DebugConsole.Trace(0+" "+token+":"+head.Class.ClassName); else if (head.Member != null) DebugConsole.Trace(0+" "+token+":"+head.Class.ClassName); else DebugConsole.Trace(0+" "+token+"="+head.Class.ClassName); // eval tail int n = tokens.Length; if (!complete) n--; // context ASResult step = head; ASClass resultClass = head.Class; // look for static or dynamic members? FlagType mask = (head.IsStatic) ? FlagType.Static : FlagType.Dynamic; // look for public only members? ASClass curClass = ASContext.CurrentClass; if (!FriendClasses(curClass, step.Class)) mask |= FlagType.Public; // explore for (int i=1; i<n; i++) { resultClass = step.Class; token = tokens[i]; DebugConsole.Trace(i+" "+token+" "+mask); FindMember(token, resultClass, step, mask); if (step.Class == null) return step; if (!step.IsStatic) //(resultClass.Flags != FlagType.Package) && ((step.Class.Flags & FlagType.Class) == 0)) { if ((mask & FlagType.Static) > 0) { mask -= FlagType.Static; mask |= FlagType.Dynamic; } } if (!FriendClasses(curClass, step.Class)) mask |= FlagType.Public; } // eval package if (step.Class.Flags == FlagType.Package) { DebugConsole.Trace("Complete package "+step.Class.ClassName); step.Class.Package = ASContext.FindPackage(step.Class.ClassName, true); } return step; }
/// <summary> /// Complete object member /// </summary> /// <param name="Sci">Scintilla control</param> /// <param name="autoHide">Don't keep the list open if the word does not match</param> /// <returns>Auto-completion has been handled</returns> static private bool HandleDotCompletion(ScintillaNet.ScintillaControl Sci, bool autoHide) { // get expression at cursor position int position = Sci.CurrentPos; ASExpr expr = GetExpression(Sci, position); if (expr.Value == null) return true; int dotIndex = expr.Value.LastIndexOf('.'); if (dotIndex == 0) return true; string tail = (dotIndex >= 0) ? expr.Value.Substring(dotIndex+1) : expr.Value; DebugConsole.Trace("? "+dotIndex+" '"+expr.separator+"' "+expr.Keyword+" ."+tail); // complete keyword if (dotIndex < 0) { if (expr.Keyword == "new") return HandleNewCompletion(Sci, expr.Value, autoHide); else if (expr.separator == ':') { if (HandleColonCompletion(Sci, expr.Value, autoHide)) return true; } else if (expr.Keyword == "import") return HandleImportCompletion(Sci, expr.Value, autoHide); else if ((expr.Keyword == "extends") || (expr.Keyword == "implements")) return HandleNewCompletion(Sci, expr.Value, autoHide); } else if ((expr.ContextBody == null) && (expr.separator != ':') && (expr.separator != '=') && (expr.Keyword != "import") && (expr.Keyword != "extends") && (expr.Keyword != "implements") ) return true; // complete declaration if ((expr.ContextBody == null) && ((expr.separator == ';') || (expr.separator == ' ') || (expr.separator == '}') || (expr.separator == '{'))) { if (expr.Value.IndexOf('#') >= 0) return true; else { string text = " "+ASClassParser.CleanClassSource(Sci.GetText(Sci.CurrentPos), null)+" "; if (Regex.IsMatch(text, "[\\s](class|interface)[\\s]")) { DebugConsole.Trace("Match class"); Match m = Regex.Match(text, "[\\s](class|interface)[\\s]+"+ASContext.CurrentClass.ClassName+"[\\s{]"); if (m.Success) { if (text.Substring(m.Index).IndexOf('{') > 0) return HandleDeclarationCompletion(Sci, "function private public static var", tail, autoHide); else if ((expr.Keyword != "extends") && (expr.Keyword != "implements")) return HandleDeclarationCompletion(Sci, "extends implements", tail, autoHide); } else return true; } else if (expr.Keyword == "class") return true; else if (dotIndex < 0) return HandleDeclarationCompletion(Sci, "import", tail, autoHide); } } DebugConsole.Trace("** Complete expression '"+expr.separator+"' "+expr.Value.Length); DebugConsole.Trace(expr.Value); // Context expr.LocalVars = ParseLocalVars(expr); ASClass cClass = ASContext.CurrentClass; ASResult result; ASClass tmpClass; if (dotIndex > 0) { // Expression before cursor result = EvalExpression(expr.Value, expr, cClass, false); if (result.IsNull()) return true; tmpClass = result.Class; } else { result = new ASResult(); tmpClass = cClass; } ASMemberList mix = new ASMemberList(); // local vars are the first thing to try if ((result.IsNull() || (dotIndex < 0)) && (expr.ContextFunction != null)) mix.Merge(expr.LocalVars); // packages if (tmpClass.Package != null) mix.Merge(tmpClass.Package); // get all members FlagType mask = 0; if ((expr.ContextFunction != null) || (expr.separator != ':')) { // user setting may ask to hide some members bool limitMembers = ASContext.HideIntrinsicMembers || (autoHide && !ASContext.AlwaysShowIntrinsicMembers); // static or dynamic members? if (!result.IsNull()) mask = (result.IsStatic) ? FlagType.Static : FlagType.Dynamic; // show private member of current class only only if (!FriendClasses(cClass, tmpClass)) mask |= FlagType.Public; DebugConsole.Trace("Filter members by: "+mask); // explore members bool classExtend = false; if (!limitMembers || (tmpClass.ClassName != "Object")) while ((tmpClass != null) && (!tmpClass.IsVoid())) { tmpClass.Sort(); // add members mix.Merge(tmpClass.Methods, mask, classExtend); // remove constructor methods foreach(ASMember meth in tmpClass.Methods) if ((meth.Flags & FlagType.Constructor) > 0) { mix.Remove(meth); break; } mix.Merge(tmpClass.Properties, mask, classExtend); mix.Merge(tmpClass.Vars, mask, classExtend); if (result.IsStatic && (tmpClass.Package != null)) { DebugConsole.Trace("Class is package "+tmpClass.ClassName); mix.Merge(tmpClass.Package, 0); } tmpClass = tmpClass.Extends; if (tmpClass != null) { // static members not inherited in AS3 classExtend = tmpClass.IsAS3; if (((tmpClass.Flags & FlagType.Intrinsic) == FlagType.Intrinsic) && (tmpClass.FileName.StartsWith(ASContext.TopLevelClassPath))) { if (limitMembers) tmpClass = null; } } } } // known classes / toplevel vars/methods if (result.IsNull() || (dotIndex < 0)) { mix.Merge(cClass.ToASMember()); mix.Merge(cClass.Imports); mix.Merge(ASContext.TopLevel.Imports); mix.Merge(ASContext.TopLevel.Methods); mix.Merge(ASContext.TopLevel.Vars); mix.Merge(ASContext.GetBasePackages()); } // show ArrayList list = new ArrayList(); foreach(ASMember member in mix) list.Add(new MemberItem(member)); CompletionList.Show(list, autoHide, tail); return true; }
/// <summary> /// Match token to a class' member /// </summary> /// <param name="token">To match</param> /// <param name="inClass">In given class</param> /// <param name="result">Class/Member struct</param> static private void FindMember(string token, ASClass inClass, ASResult result, FlagType mask) { DebugConsole.Trace("FindMember "+token+" "+mask); ASMember found = null; ASClass tmpClass = inClass; if (inClass == null) return; // variable int p = token.IndexOf('#'); if (p < 0) { // member while ((tmpClass != null) && !tmpClass.IsVoid()) { found = tmpClass.Properties.Search(token, mask); if (found != null) break; found = tmpClass.Vars.Search(token, mask); if (found != null) break; found = tmpClass.Methods.Search(token, mask); if (found != null) { // static members not inherited in AS3 if (tmpClass != inClass && (found.Flags & FlagType.Static) > 0 && inClass.IsAS3) return; //DebugConsole.Trace("Method of "+tmpClass.ClassName); result.Member = found; if ((result.Member.Flags & FlagType.Constructor) > 0) { result.Class = tmpClass; result.IsStatic = true; } else { result.inClass = tmpClass; result.Class = ASContext.FindClassFromName("Function", null); result.IsStatic = false; //((found.Flags & FlagType.Static) > 0); } //DebugConsole.Trace("Found static "+found.Name+":"+result.Class.ClassName+" in "+tmpClass.ClassName); return; } tmpClass = tmpClass.Extends; } } // method else { token = token.Substring(0,p); while ((tmpClass != null) && !tmpClass.IsVoid()) { found = tmpClass.Methods.Search(token, mask); if (found != null) { // static members not inherited in AS3 if (tmpClass != inClass && (found.Flags & FlagType.Static) > 0 && inClass.IsAS3) return; if ((found.Flags & FlagType.Constructor) > 0) { result.Class = tmpClass; result.Member = found; result.IsStatic = false; return; } break; } tmpClass = tmpClass.Extends; } } // result found! if (found != null) { result.inClass = tmpClass; result.Class = ASContext.FindClassFromName(found.Type, tmpClass); result.Member = found; result.IsStatic = false; //((found.Flags & FlagType.Static) > 0); DebugConsole.Trace("Found "+found.Name+":"+result.Class.ClassName+" in "+tmpClass.ClassName); return; } // try subpackages else if (inClass.Package != null) { DebugConsole.Trace("Find "+token+" as "+inClass.ClassName+"."+token); result.Class = ASContext.FindClassFromName(inClass.ClassName.Replace(System.IO.Path.DirectorySeparatorChar,'.')+"."+token, null); if (!result.Class.IsVoid()) return; // sub packages? ASMemberList list = ASContext.FindPackage(inClass.ClassName, false); if (list != null || inClass.Flags == FlagType.Package) { result.Class = new ASClass(); result.Class.ClassName = inClass.ClassName+System.IO.Path.DirectorySeparatorChar+token; result.Class.Flags = FlagType.Package; result.Class.Package = list; result.IsStatic = true; return; } } // not found result.Class = null; result.Member = null; DebugConsole.Trace(token+" not found in "+inClass.ClassName); }
/*/// <summary> /// List implicit known classes to a class: ie. the class, the extended class, implemented classes /// UPDATE: MTASC doesn't automatically known the extended classes /// </summary> /// <param name="cClass">Reference</param> /// <returns>List of members</returns> static private ASMemberList GetContextClassesList(ASClass cClass) { // current class ASMemberList list = new ASMemberList(); list.Add(cClass.ToASMember()); // inheritance ASClass tmpClass = cClass.Extends; while(tmpClass != null && !tmpClass.IsVoid() && !tmpClass.FileName.StartsWith(ASContext.TopLevelClassPath)) { list.Add(tmpClass.ToASMember()); tmpClass = tmpClass.Extends; } if (list.Count > 1) list.Sort(); // TODO add other implicitly known classes from the classpath, like interfaces return list; }*/ #endregion #region tooltips formatting static public string GetToolTipText(ASResult result) { if ((result.Member != null) && (result.inClass != null)) { string text = MemberTooltipText(result.Member, result.inClass); if (result.inClass == ASContext.TopLevel) text = text.Substring(0, text.IndexOf('\n')); return text; } else if ((result.Member != null) && ((result.Member.Flags & FlagType.Constructor) != FlagType.Constructor)) { return ASClass.MemberDeclaration(result.Member); } else if (result.inClass != null) { return ASClass.ClassDeclaration(result.inClass); } else if (result.Class != null) { return ASClass.ClassDeclaration(result.Class); } else return null; }
/// <summary> /// Find variable type in function context /// </summary> /// <param name="token">Variable name</param> /// <param name="context">In context</param> /// <param name="inClass">In class</param> /// <returns>Class/member struct</returns> static private ASResult EvalVariable(string token, ASExpr context, ASClass inClass) { DebugConsole.Trace("EvalVariable "+token); ASResult result = new ASResult(); // local vars if (context.LocalVars != null) foreach(ASMember var in context.LocalVars) { if (var.Name == token) { result.inClass = null; result.Class = ASContext.FindClassFromName(var.Type, inClass); result.Member = var; return result; } } // method parameters if (context.ContextFunction != null) { Match param = Regex.Match(context.ContextFunction, "[(,][\\s]*"+Regex.Escape(token)+"[\\s:,)]"); if (param.Success) { //DebugConsole.Trace("Method param "+token); param = Regex.Match(context.ContextFunction, "[(,][\\s]*"+Regex.Escape(token)+"[\\s]*:[\\s]*(?<type>[^\\s,)]*)"); if (param.Success && (param.Groups["type"].Value.Length > 0)) { //DebugConsole.Trace("Type "+param.Groups["type"].Value); result.Class = ASContext.FindClassFromName(param.Groups["type"].Value, inClass); } return result; } } // class members FindMember(token, inClass, result, 0); if (!result.IsNull()) return result; // top-level elements if (!ASContext.TopLevel.IsVoid()) { FindMember(token, ASContext.TopLevel, result, 0); if (!result.IsNull()) return result; // special _levelN if (token.StartsWith("_") && re_level.IsMatch(token)) { result.Class = ASContext.FindClassFromName("MovieClip", null); return result; } } // classes int p = token.IndexOf('#'); string ctoken = (p > 0) ? token.Substring(0,p) : token; ASClass aClass = ASContext.FindClassFromName(ctoken, inClass); if (!aClass.IsVoid()) { result.Class = aClass; result.IsStatic = (p < 0); return result; } // packages folders ASMemberList package = ASContext.FindPackage(token, false); if (package != null) { result.Class = new ASClass(); result.Class.ClassName = token; result.Class.Flags = FlagType.Package; result.Class.Package = package; result.IsStatic = true; } return result; }