static public void ContextualGenerator(ScintillaNet.ScintillaControl Sci) { if (ASContext.Context is ASContext) (ASContext.Context as ASContext).UpdateCurrentFile(false); // update model if ((ASContext.Context.CurrentClass.Flags & (FlagType.Enum | FlagType.TypeDef)) > 0) return; lookupPosition = -1; int position = Sci.CurrentPos; if (Sci.BaseStyleAt(position) == 19) // on keyword return; bool isNotInterface = (ASContext.Context.CurrentClass.Flags & FlagType.Interface) == 0; int line = Sci.LineFromPosition(position); contextToken = Sci.GetWordFromPosition(position); contextMatch = null; FoundDeclaration found = GetDeclarationAtLine(Sci, line); string text = Sci.GetLine(line); bool suggestItemDeclaration = false; if (isNotInterface && !String.IsNullOrEmpty(contextToken) && Char.IsDigit(contextToken[0])) { ShowConvertToConst(found); return; } ASResult resolve = ASComplete.GetExpressionType(Sci, Sci.WordEndPosition(position, true)); contextResolved = resolve; // ignore automatic vars (MovieClip members) if (isNotInterface && resolve.Member != null && (((resolve.Member.Flags & FlagType.AutomaticVar) > 0) || (resolve.InClass != null && resolve.InClass.QualifiedName == "Object"))) { resolve.Member = null; resolve.Type = null; } if (isNotInterface && found.inClass != ClassModel.VoidClass && contextToken != null) { if (resolve.Member == null && resolve.Type != null && (resolve.Type.Flags & FlagType.Interface) > 0) // implement interface { contextParam = resolve.Type.Type; ShowImplementInterface(found); return; } if (resolve.Member != null && !ASContext.Context.CurrentClass.IsVoid() && (resolve.Member.Flags & FlagType.LocalVar) > 0) // promote to class var { contextMember = resolve.Member; ShowPromoteLocalAndAddParameter(found); return; } } if (contextToken != null && resolve.Member == null) // import declaration { if ((resolve.Type == null || resolve.Type.IsVoid() || !ASContext.Context.IsImported(resolve.Type, line)) && CheckAutoImport(found)) return; if (resolve.Type == null) { int stylemask = (1 << Sci.StyleBits) - 1; suggestItemDeclaration = ASComplete.IsTextStyle(Sci.StyleAt(position - 1) & stylemask); } } if (isNotInterface && found.member != null) { // private var -> property if ((found.member.Flags & FlagType.Variable) > 0 && (found.member.Flags & FlagType.LocalVar) == 0) { // maybe we just want to import the member's non-imported type Match m = Regex.Match(text, String.Format(patternVarDecl, found.member.Name, contextToken)); if (m.Success) { contextMatch = m; ClassModel type = ASContext.Context.ResolveType(contextToken, ASContext.Context.CurrentModel); if (type.IsVoid() && CheckAutoImport(found)) return; } ShowGetSetList(found); return; } // inside a function else if ((found.member.Flags & (FlagType.Function | FlagType.Getter | FlagType.Setter)) > 0 && resolve.Member == null && resolve.Type == null) { if (contextToken != null) { // "generate event handlers" suggestion string re = String.Format(patternEvent, contextToken); Match m = Regex.Match(text, re, RegexOptions.IgnoreCase); if (m.Success) { contextMatch = m; contextParam = CheckEventType(m.Groups["event"].Value); ShowEventList(found); return; } m = Regex.Match(text, String.Format(patternAS2Delegate, contextToken), RegexOptions.IgnoreCase); if (m.Success) { contextMatch = m; ShowDelegateList(found); return; } // suggest delegate if (ASContext.Context.Features.hasDelegates) { m = Regex.Match(text, @"([a-z0-9_.]+)\s*\+=\s*" + contextToken, RegexOptions.IgnoreCase); if (m.Success) { int offset = Sci.PositionFromLine(Sci.LineFromPosition(position)) + m.Groups[1].Index + m.Groups[1].Length; resolve = ASComplete.GetExpressionType(Sci, offset); if (resolve.Member != null) contextMember = ResolveDelegate(resolve.Member.Type, resolve.InFile); contextMatch = m; ShowDelegateList(found); return; } } } else { // insert a default handler name, then "generate event handlers" suggestion Match m = Regex.Match(text, String.Format(patternEvent, ""), RegexOptions.IgnoreCase); if (m.Success) { int regexIndex = m.Index + Sci.PositionFromLine(Sci.CurrentLine); GenerateDefaultHandlerName(Sci, position, regexIndex, m.Groups["event"].Value, true); resolve = ASComplete.GetExpressionType(Sci, Sci.CurrentPos); if (resolve.Member == null || (resolve.Member.Flags & FlagType.AutomaticVar) > 0) { contextMatch = m; contextParam = CheckEventType(m.Groups["event"].Value); ShowEventList(found); } return; } // insert default delegate name, then "generate delegate" suggestion if (ASContext.Context.Features.hasDelegates) { m = Regex.Match(text, @"([a-z0-9_.]+)\s*\+=\s*", RegexOptions.IgnoreCase); if (m.Success) { int offset = Sci.PositionFromLine(Sci.LineFromPosition(position)) + m.Groups[1].Index + m.Groups[1].Length; resolve = ASComplete.GetExpressionType(Sci, offset); if (resolve.Member != null) { contextMember = ResolveDelegate(resolve.Member.Type, resolve.InFile); string delegateName = resolve.Member.Name; if (delegateName.StartsWith("on")) delegateName = delegateName.Substring(2); GenerateDefaultHandlerName(Sci, position, offset, delegateName, false); resolve = ASComplete.GetExpressionType(Sci, Sci.CurrentPos); if (resolve.Member == null || (resolve.Member.Flags & FlagType.AutomaticVar) > 0) { contextMatch = m; ShowDelegateList(found); } return; } } } } } // "Generate fields from parameters" suggestion if (found.member != null && (found.member.Flags & FlagType.Function) > 0 && found.member.Parameters != null && (found.member.Parameters.Count > 0) && resolve.Member != null && (resolve.Member.Flags & FlagType.ParameterVar) > 0) { contextMember = resolve.Member; ShowFieldFromParameter(found); return; } // "add to interface" suggestion if (resolve.Member != null && resolve.Member.Name == found.member.Name && line == found.member.LineFrom && ((found.member.Flags & FlagType.Function) > 0 || (found.member.Flags & FlagType.Getter) > 0 || (found.member.Flags & FlagType.Setter) > 0) && found.inClass != ClassModel.VoidClass && found.inClass.Implements != null && found.inClass.Implements.Count > 0) { string funcName = found.member.Name; FlagType flags = found.member.Flags & ~FlagType.Access; List<string> interfaces = new List<string>(); foreach (string interf in found.inClass.Implements) { bool skip = false; ClassModel cm = ASContext.Context.ResolveType(interf, ASContext.Context.CurrentModel); foreach (MemberModel m in cm.Members) { if (m.Name.Equals(funcName) && m.Flags.Equals(flags)) { skip = true; break; } } if (!skip) { interfaces.Add(interf); } } if (interfaces.Count > 0) { ShowAddInterfaceDefList(found, interfaces); return; } } // "assign var to statement" suggestion int curLine = Sci.CurrentLine; string ln = Sci.GetLine(curLine).TrimEnd(); if (ln.Length > 0 && ln.IndexOf("=") == -1 && ln.Length <= Sci.CurrentPos - Sci.PositionFromLine(curLine)) // cursor at end of line { ShowAssignStatementToVarList(found); return; } } // suggest generate constructor / toString if (isNotInterface && found.member == null && found.inClass != ClassModel.VoidClass && contextToken == null) { bool hasConstructor = false; bool hasToString = false; foreach (MemberModel m in ASContext.Context.CurrentClass.Members) { if (!hasConstructor && (m.Flags & FlagType.Constructor) > 0) hasConstructor = true; if (!hasToString && (m.Flags & FlagType.Function) > 0 && m.Name.Equals("toString")) hasToString = true; } if (!hasConstructor || !hasToString) { ShowConstructorAndToStringList(found, hasConstructor, hasToString); return; } } if (isNotInterface && resolve.Member != null && resolve.Type != null && resolve.Type.QualifiedName == "String" && found.inClass != ClassModel.VoidClass) { int lineStartPos = Sci.PositionFromLine(Sci.CurrentLine); string lineStart = text.Substring(0, Sci.CurrentPos - lineStartPos); Match m = Regex.Match(lineStart, String.Format(@"new\s+(?<event>\w+)\s*\(\s*\w+", lineStart)); if (m.Success) { Group g = m.Groups["event"]; ASResult eventResolve = ASComplete.GetExpressionType(Sci, lineStartPos + g.Index + g.Length); if (eventResolve != null && eventResolve.Type != null) { ClassModel aType = eventResolve.Type; aType.ResolveExtends(); while (!aType.IsVoid() && aType.QualifiedName != "Object") { if (aType.QualifiedName == "flash.events.Event") { contextParam = eventResolve.Type.QualifiedName; ShowEventMetatagList(found); return; } aType = aType.Extends; } } } } // suggest declaration if (contextToken != null) { if (suggestItemDeclaration) { Match m = Regex.Match(text, String.Format(patternClass, contextToken)); if (m.Success) { contextMatch = m; ShowNewClassList(found); } else if (!found.inClass.IsVoid()) { m = Regex.Match(text, String.Format(patternMethod, contextToken)); if (m.Success) { contextMatch = m; ShowNewMethodList(found); } else ShowNewVarList(found); } } else { if (resolve != null && resolve.InClass != null && resolve.InClass.InFile != null && resolve.Member != null && (resolve.Member.Flags & FlagType.Function) > 0 && File.Exists(resolve.InClass.InFile.FileName) && !resolve.InClass.InFile.FileName.StartsWith(PathHelper.AppDir)) { Match m = Regex.Match(text, String.Format(patternMethodDecl, contextToken)); Match m2 = Regex.Match(text, String.Format(patternMethod, contextToken)); if (!m.Success && m2.Success) { contextMatch = m; ShowChangeMethodDeclList(found); } } else if (resolve != null && resolve.Type != null && resolve.Type.InFile != null && resolve.RelClass != null && File.Exists(resolve.Type.InFile.FileName) && !resolve.Type.InFile.FileName.StartsWith(PathHelper.AppDir)) { Match m = Regex.Match(text, String.Format(patternClass, contextToken)); if (m.Success) { contextMatch = m; ShowChangeConstructorDeclList(found); } } } } // TODO: Empty line, show generators list? }
static public void ContextualGenerator(ScintillaNet.ScintillaControl Sci) { if (ASContext.Context is ASContext) (ASContext.Context as ASContext).UpdateCurrentFile(false); // update model lookupPosition = -1; int position = Sci.CurrentPos; if (Sci.BaseStyleAt(position) == 19) // on keyword return; int line = Sci.LineFromPosition(position); contextToken = Sci.GetWordFromPosition(position); contextMatch = null; FoundDeclaration found = GetDeclarationAtLine(Sci, line); string text = Sci.GetLine(line); bool suggestItemDeclaration = false; if (!String.IsNullOrEmpty(contextToken) && Char.IsDigit(contextToken[0])) { ShowConvertToConst(found); return; } ASResult resolve = ASComplete.GetExpressionType(Sci, Sci.WordEndPosition(position, true)); // ignore automatic vars (MovieClip members) if (resolve.Member != null && (((resolve.Member.Flags & FlagType.AutomaticVar) > 0) || (resolve.inClass != null && resolve.inClass.QualifiedName == "Object"))) { resolve.Member = null; resolve.Type = null; } if (found.inClass != ClassModel.VoidClass && contextToken != null) { if (resolve.Member == null && resolve.Type != null && (resolve.Type.Flags & FlagType.Interface) > 0) // implement interface { contextParam = resolve.Type.Type; ShowImplementInterface(found); return; } if (resolve.Member != null && !ASContext.Context.CurrentClass.IsVoid() && (resolve.Member.Flags & FlagType.LocalVar) > 0) // promote to class var { contextMember = resolve.Member; ShowPromoteLocalAndAddParameter(found); return; } if (resolve.Member == null && resolve.Type == null) // import declaration { if (CheckAutoImport(found)) { return; } else { int stylemask = (1 << Sci.StyleBits) - 1; if (ASComplete.IsTextStyle(Sci.StyleAt(position - 1) & stylemask)) { suggestItemDeclaration = true; } } } } if (found.member != null) { // private var -> property if ((found.member.Flags & FlagType.Variable) > 0 && (found.member.Flags & FlagType.LocalVar) == 0) { // maybe we just want to import the member's non-imported type Match m = Regex.Match(text, String.Format(patternVarDecl, found.member.Name, contextToken)); if (m.Success) { contextMatch = m; ClassModel type = ASContext.Context.ResolveType(contextToken, ASContext.Context.CurrentModel); if (type.IsVoid() && CheckAutoImport(found)) return; } // create property ShowGetSetList(found); return; } // inside a function else if ((found.member.Flags & (FlagType.Function | FlagType.Getter | FlagType.Setter)) > 0 && resolve.Member == null && resolve.Type == null) { if (contextToken != null) { // "generate event handlers" suggestion Match m = Regex.Match(text, String.Format(patternEvent, contextToken), RegexOptions.IgnoreCase); if (m.Success) { contextMatch = m; contextParam = CheckEventType(m.Groups["event"].Value); ShowEventList(found); return; } m = Regex.Match(text, String.Format(patternDelegate, contextToken), RegexOptions.IgnoreCase); if (m.Success) { contextMatch = m; ShowDelegateList(found); return; } } } // "Generate fields from parameters" suggestion if (found.member != null && (found.member.Flags & FlagType.Function) > 0 && (found.member.Flags & FlagType.Static) == 0 && found.member.Parameters != null && (found.member.Parameters.Count > 0) && resolve.Member != null && (resolve.Member.Flags & FlagType.ParameterVar) > 0) { contextMember = resolve.Member; ShowFieldFromParameter(found); return; } // "add to interface" suggestion if (resolve.Member != null && resolve.Member.Name == found.member.Name && (found.member.Flags & FlagType.Function) > 0 && found.inClass != null && found.inClass.Implements != null && found.inClass.Implements.Count > 0) { string funcName = found.member.Name; int classPosStart = Sci.PositionFromLine(found.inClass.LineFrom); bool skip = false; foreach (string interf in found.inClass.Implements) { if (skip) { break; } ClassModel cm = ASContext.Context.ResolveType(interf, ASContext.Context.CurrentModel); contextParam = cm.Type; MemberList members = cm.Members; foreach (MemberModel m in members) { if (m.Name.Equals(funcName)) { skip = true; break; } } } if (!skip && contextParam != null) { ShowAddInterfaceDefList(found); return; } } // "assign var to statement" siggestion int curLine = Sci.LineFromPosition(Sci.CurrentPos); string ln = Sci.GetLine(curLine); if (ln.Trim().Length > 0 && ln.TrimEnd().Length <= Sci.CurrentPos - Sci.PositionFromLine(curLine)) { Regex re = new Regex("="); Match m = re.Match(ln); if (!m.Success) { ShowAssignStatementToVarList(found); } } } // suggest generate constructor / toString if (found.member == null && found.inClass != ClassModel.VoidClass && contextToken == null) { ClassModel cm = ASContext.Context.CurrentClass; MemberList members = cm.Members; bool hasConstructor = false; bool hasToString = false; foreach (MemberModel m in members) { if ((m.Flags & FlagType.Constructor) > 0) { hasConstructor = true; } if ((m.Flags & FlagType.Function) > 0 && m.Name.Equals("toString")) { hasToString = true; } } if (!hasConstructor || !hasToString) { ShowConstructorAndToStringList(found, hasConstructor, hasToString); return; } } if (resolve.Member != null && resolve.Type != null && resolve.Type.QualifiedName == "String" && found.inClass != null) { int lineStartPos = Sci.PositionFromLine(Sci.LineFromPosition(Sci.CurrentPos)); string lineStart = text.Substring(0, Sci.CurrentPos - lineStartPos); Match m = Regex.Match(lineStart, String.Format(@"new\s+(?<event>\w+)\s*\(\s*\w+", lineStart)); if (m.Success) { Group g = m.Groups["event"]; ASResult eventResolve = ASComplete.GetExpressionType(Sci, lineStartPos + g.Index + g.Length); if (eventResolve != null && eventResolve.Type != null) { ClassModel aType = eventResolve.Type; aType.ResolveExtends(); while (!aType.IsVoid() && aType.QualifiedName != "Object") { if (aType.QualifiedName == "flash.events.Event") { contextParam = eventResolve.Type.QualifiedName; ShowEventMetatagList(found); return; } aType = aType.Extends; } } } } // suggest declaration if (contextToken != null) { if (suggestItemDeclaration) { Match m = Regex.Match(text, String.Format(patternClass, contextToken)); if (m.Success) { contextMatch = m; ShowNewClassList(found); } else { m = Regex.Match(text, String.Format(patternMethod, contextToken)); if (m.Success) { contextMatch = m; ShowNewMethodList(found); } else ShowNewVarList(found); } } else { if (resolve != null && resolve.inClass != null && resolve.inClass.InFile != null && resolve.Member != null && (resolve.Member.Flags & FlagType.Function) > 0 && File.Exists(resolve.inClass.InFile.FileName) && !resolve.inClass.InFile.FileName.StartsWith(PathHelper.AppDir)) { Match m = Regex.Match(text, String.Format(patternMethodDecl, contextToken)); Match m2 = Regex.Match(text, String.Format(patternMethod, contextToken)); if (!m.Success && m2.Success) { contextMatch = m; ShowChangeMethodDeclList(found); } } } } // TODO Empty line, show generators list? }
public static void GenerateExtractVariable(ScintillaNet.ScintillaControl Sci, string NewName) { FileModel cFile; IASContext context = ASContext.Context; Int32 pos = Sci.CurrentPos; string expression = Sci.SelText.Trim(new char[] { '=', ' ', '\t', '\n', '\r', ';', '.' }); expression = expression.TrimEnd(new char[] { '(', '[', '{', '<' }); expression = expression.TrimStart(new char[] { ')', ']', '}', '>' }); cFile = ASContext.Context.CurrentModel; ASFileParser parser = new ASFileParser(); parser.ParseSrc(cFile, Sci.Text); MemberModel current = cFile.Context.CurrentMember; string characterClass = ScintillaNet.ScintillaControl.Configuration.GetLanguage(Sci.ConfigurationLanguage).characterclass.Characters; int funcBodyStart = ASGenerator.GetBodyStart(current.LineFrom, current.LineTo, Sci); Sci.SetSel(funcBodyStart, Sci.LineEndPosition(current.LineTo)); string currentMethodBody = Sci.SelText; bool isExprInSingleQuotes = (expression.StartsWith("'") && expression.EndsWith("'")); bool isExprInDoubleQuotes = (expression.StartsWith("\"") && expression.EndsWith("\"")); int stylemask = (1 << Sci.StyleBits) - 1; int lastPos = -1; char prevOrNextChar; Sci.Colourise(0, -1); while (true) { lastPos = currentMethodBody.IndexOf(expression, lastPos + 1); if (lastPos > -1) { if (lastPos > 0) { prevOrNextChar = currentMethodBody[lastPos - 1]; if (characterClass.IndexOf(prevOrNextChar) > -1) { continue; } } if (lastPos + expression.Length < currentMethodBody.Length) { prevOrNextChar = currentMethodBody[lastPos + expression.Length]; if (characterClass.IndexOf(prevOrNextChar) > -1) { continue; } } int style = Sci.StyleAt(funcBodyStart + lastPos) & stylemask; if (ASComplete.IsCommentStyle(style)) { continue; } else if ((isExprInDoubleQuotes && currentMethodBody[lastPos] == '"' && currentMethodBody[lastPos + expression.Length - 1] == '"') || (isExprInSingleQuotes && currentMethodBody[lastPos] == '\'' && currentMethodBody[lastPos + expression.Length - 1] == '\'')) { } else if (!ASComplete.IsTextStyle(style)) { continue; } Sci.SetSel(funcBodyStart + lastPos, funcBodyStart + lastPos + expression.Length); Sci.ReplaceSel(NewName); currentMethodBody = currentMethodBody.Substring(0, lastPos) + NewName + currentMethodBody.Substring(lastPos + expression.Length); lastPos += NewName.Length; } else { break; } } Sci.CurrentPos = funcBodyStart; Sci.SetSel(Sci.CurrentPos, Sci.CurrentPos); MemberModel m = new MemberModel(NewName, "", FlagType.LocalVar, 0); m.Value = expression; string snippet = TemplateUtils.GetTemplate("Variable"); snippet = TemplateUtils.ReplaceTemplateVariable(snippet, "Modifiers", null); snippet = TemplateUtils.ToDeclarationString(m, snippet); snippet += NewLine + "$(Boundary)"; SnippetHelper.InsertSnippetText(Sci, Sci.CurrentPos, snippet); }
/// <summary> /// Find Actionscript expression at cursor position /// </summary> /// <param name="sci">Scintilla Control</param> /// <param name="position">Cursor position</param> /// <param name="ignoreWhiteSpace">Skip whitespace at position</param> /// <returns></returns> private static ASExpr GetExpression(ScintillaNet.ScintillaControl Sci, int position, bool ignoreWhiteSpace) { ASExpr expression = new ASExpr(); expression.Position = position; expression.Separator = ' '; // file's member declared at this position expression.ContextMember = ASContext.Context.CurrentMember; int minPos = 0; string body = null; if (expression.ContextMember != null) { minPos = Sci.PositionFromLine(expression.ContextMember.LineFrom); StringBuilder sbBody = new StringBuilder(); for (int i = expression.ContextMember.LineFrom; i <= expression.ContextMember.LineTo; i++) sbBody.Append(Sci.GetLine(i)).Append('\n'); body = sbBody.ToString(); //int tokPos = body.IndexOf(expression.ContextMember.Name); //if (tokPos >= 0) minPos += tokPos + expression.ContextMember.Name.Length; if ((expression.ContextMember.Flags & (FlagType.Function | FlagType.Constructor | FlagType.Getter | FlagType.Setter)) > 0) { expression.ContextFunction = expression.ContextMember; expression.FunctionOffset = expression.ContextMember.LineFrom; Match mStart = Regex.Match(body, "(\\)|[a-z0-9*.,-<>])\\s*{", RegexOptions.IgnoreCase); if (mStart.Success) { // cleanup function body & offset int pos = mStart.Index + mStart.Length; expression.BeforeBody = (position < Sci.PositionFromLine(expression.ContextMember.LineFrom) + pos); string pre = body.Substring(0, pos); for (int i = 0; i < pre.Length - 1; i++) if (pre[i] == '\r') { expression.FunctionOffset++; if (pre[i + 1] == '\n') i++; } else if (pre[i] == '\n') expression.FunctionOffset++; body = body.Substring(pos); } expression.FunctionBody = body; } else { int eqPos = body.IndexOf('='); expression.BeforeBody = (eqPos < 0 || position < Sci.PositionFromLine(expression.ContextMember.LineFrom) + eqPos); } } // get the word characters from the syntax definition string characterClass = ScintillaNet.ScintillaControl.Configuration.GetLanguage(Sci.ConfigurationLanguage).characterclass.Characters; // get expression before cursor ContextFeatures features = ASContext.Context.Features; int stylemask = (1 << Sci.StyleBits) - 1; int style = (position >= minPos) ? Sci.StyleAt(position) & stylemask : 0; StringBuilder sb = new StringBuilder(); StringBuilder sbSub = new StringBuilder(); int subCount = 0; char c = ' '; char c2; int startPos = position; int braceCount = 0; int sqCount = 0; int genCount = 0; bool hasGenerics = features.hasGenerics; bool hadWS = false; bool hadDot = ignoreWhiteSpace; bool inRegex = false; char dot = features.dot[features.dot.Length - 1]; while (position > minPos) { position--; style = Sci.StyleAt(position) & stylemask; if (style == 14) // regex literal { inRegex = true; } else if (!ASComplete.IsCommentStyle(style)) { c2 = c; c = (char)Sci.CharAt(position); // end of regex literal if (inRegex) { inRegex = false; if (expression.SubExpressions == null) expression.SubExpressions = new List<string>(); expression.SubExpressions.Add(""); sb.Insert(0, "RegExp.#" + (subCount++) + "~"); } // array access if (c == '[') { sqCount--; if (sqCount == 0) { if (sbSub.Length > 0) sbSub.Insert(0, '['); if (braceCount == 0) sb.Insert(0, ".[]"); continue; } if (sqCount < 0) { expression.Separator = ';'; break; } } else if (c == ']') { sqCount++; } // else if (c == '<' && hasGenerics) { genCount--; if (genCount < 0) { expression.Separator = ';'; break; } } // ignore sub-expressions (method calls' parameters) else if (c == '(') { braceCount--; if (braceCount == 0) { sbSub.Insert(0, c); expression.SubExpressions.Add(sbSub.ToString()); sb.Insert(0, ".#" + (subCount++) + "~"); // method call or sub expression int testPos = position - 1; string testWord = ASComplete.GetWordLeft(Sci, ref testPos); if (testWord == "return" || testWord == "case" || testWord == "defaut") { // ex: return (a as B).<complete> expression.Separator = ';'; expression.WordBefore = testWord; break; } else continue; } else if (braceCount < 0) { expression.Separator = ';'; int testPos = position - 1; string testWord = ASComplete.GetWordLeft(Sci, ref testPos); // anonymous function string testWord2 = ASComplete.GetWordLeft(Sci, ref testPos) ?? "null"; // regular function if (testWord == features.functionKey || testWord == "catch" || testWord2 == features.functionKey || testWord2 == features.getKey || testWord2 == features.setKey) { expression.Separator = ','; expression.coma = ComaExpression.FunctionDeclaration; } break; } } else if (c == ')') { if (!hadDot) { expression.Separator = ';'; break; } if (braceCount == 0) // start sub-expression { if (expression.SubExpressions == null) expression.SubExpressions = new List<string>(); sbSub = new StringBuilder(); } braceCount++; } else if (c == '>' && hasGenerics) { if (c2 == '.' || c2 == '(') genCount++; else break; } if (braceCount > 0 || sqCount > 0 || genCount > 0) { if (c == ';') // not expected: something's wrong { expression.Separator = ';'; break; } // build sub expression sbSub.Insert(0, c); continue; } // build expression if (c <= 32) { if (genCount == 0) hadWS = true; } else if (c == dot) { if (features.dot.Length == 2) hadDot = position > 0 && Sci.CharAt(position - 1) == features.dot[0]; else hadDot = true; sb.Insert(0, c); } else if (characterClass.IndexOf(c) >= 0) { if (hadWS && !hadDot) { expression.Separator = ' '; break; } hadWS = false; hadDot = false; sb.Insert(0, c); startPos = position; } else if (c == ';') { expression.Separator = ';'; break; } else if (hasGenerics && (genCount > 0 || c == '<')) { sb.Insert(0, c); } else if (c == '{') { expression.coma = DisambiguateComa(Sci, position, minPos); expression.Separator = (expression.coma == ComaExpression.None) ? ';' : ','; break; } else if (c == ',') { expression.coma = DisambiguateComa(Sci, position, minPos); expression.Separator = (expression.coma == ComaExpression.None) ? ';' : ','; break; } else if (c == ':') { expression.Separator = ':'; break; } else if (c == '=') { expression.Separator = '='; break; } else //if (hadWS && !hadDot) { if (c == '\'' || c == '"') expression.Separator = '"'; else expression.Separator = ';'; break; } } // string literals only allowed in sub-expressions else { if (braceCount == 0) // not expected: something's wrong { expression.Separator = ';'; break; } } } // check if there is a particular keyword if (expression.Separator == ' ') { expression.WordBefore = ASComplete.GetWordLeft(Sci, ref position); } // result expression.Value = sb.ToString(); expression.PositionExpression = startPos; return expression; }
private static StatementReturnType GetStatementReturnType(ScintillaNet.ScintillaControl Sci, ClassModel inClass, string line, int startPos) { Regex target = new Regex(@"[;\s\n\r]*", RegexOptions.RightToLeft); Match m = target.Match(line); if (!m.Success) { return null; } line = line.Substring(0, m.Index); if (line.Length == 0) { return null; } line = ReplaceAllStringContents(line); ASResult resolve = null; int pos = -1; string word = null; int stylemask = (1 << Sci.StyleBits) - 1; ClassModel type = null; if (line[line.Length - 1] == ')') { pos = -1; int lastIndex = 0; int bracesBalance = 0; while (true) { int pos1 = line.IndexOf("(", lastIndex); int pos2 = line.IndexOf(")", lastIndex); if (pos1 != -1 && pos2 != -1) { lastIndex = Math.Min(pos1, pos2); } else if (pos1 != -1 || pos2 != -1) { lastIndex = Math.Max(pos1, pos2); } else { break; } if (lastIndex == pos1) { bracesBalance++; if (bracesBalance == 1) { pos = lastIndex; } } else if (lastIndex == pos2) { bracesBalance--; } lastIndex++; } } else { pos = line.Length; } if (pos != -1) { line = line.Substring(0, pos); pos += startPos; pos -= line.Length - line.TrimEnd().Length + 1; pos = Sci.WordEndPosition(pos, true); resolve = ASComplete.GetExpressionType(Sci, pos); word = Sci.GetWordFromPosition(pos); } char c = (char)Sci.CharAt(pos); if (word != null && Char.IsDigit(word[0])) { type = inClass.InFile.Context.ResolveType("Number", inClass.InFile); } else if (word != null && (word == "true" || word == "false")) { type = inClass.InFile.Context.ResolveType("Boolean", inClass.InFile); } else if (!(ASComplete.IsTextStyle(Sci.StyleAt(pos - 1) & stylemask))) { type = inClass.InFile.Context.ResolveType("String", inClass.InFile); } else if (c == '}') { type = inClass.InFile.Context.ResolveType("Object", inClass.InFile); } else if (c == '>') { type = inClass.InFile.Context.ResolveType("XML", inClass.InFile); } else if (c == ']') { type = inClass.InFile.Context.ResolveType("Array", inClass.InFile); } if (resolve == null) { resolve = new ASResult(); } if (resolve.Type == null) { resolve.Type = type; } return new StatementReturnType(resolve, pos, word); }
static private bool HandleColonCompletion(ScintillaNet.ScintillaControl Sci, string tail, bool autoHide) { DebugConsole.Trace("** Complete: ':'<class>"); // Context ASClass cClass = ASContext.CurrentClass; // Valid statement int position = Sci.CurrentPos-1; string keyword = null; int stylemask = (1 << Sci.StyleBits) -1; char c; while (position > 0) { position--; c = (char)Sci.CharAt(position); if ((c == '{') || (c == '=')) return false; else if ((Sci.StyleAt(position) & stylemask) == 19) { keyword = GetWordLeft(Sci, ref position); DebugConsole.Trace("'"+keyword+"'"); break; } } if (keyword != "var" && keyword != "function" && keyword != "get" && keyword != "set" && keyword != "const") return false; // Consolidate known classes ASMemberList known = new ASMemberList(); known.Merge(cClass.ToASMember()); known.Merge(cClass.Imports); known.Merge(ASContext.TopLevel.Imports); known.Merge(ASContext.GetBasePackages()); // show ArrayList list = new ArrayList(); foreach(ASMember member in known) list.Add(new MemberItem(member)); CompletionList.Show(list, autoHide, tail); return true; }
/// <summary> /// Character written in editor /// </summary> /// <param name="Value">Character inserted</param> static public bool OnChar(ScintillaNet.ScintillaControl Sci, int Value, bool autoHide) { if (autoHide && !ASContext.HelpersEnabled) return false; try { int eolMode = Sci.EOLMode; // code auto if (((Value == 10) && (eolMode != 1)) || ((Value == 13) && (eolMode == 1))) { DebugConsole.Trace("Struct"); HandleStructureCompletion(Sci); return false; } // ignore repeated characters int position = Sci.CurrentPos; if ((Sci.CharAt(position-2) == Value) && (Sci.CharAt(position-1) == Value) && (Value != '*')) return false; // ignore text in comments & quoted text Sci.Colourise(0,-1); int stylemask = (1 << Sci.StyleBits) -1; int style = Sci.StyleAt(position-1) & stylemask; DebugConsole.Trace("style "+style); if (!IsTextStyle(style) && !IsTextStyle(Sci.StyleAt(position) & stylemask)) { // documentation completion if (ASContext.DocumentationCompletionEnabled && IsCommentStyle(style)) return ASDocumentation.OnChar(Sci, Value, position, style); else if (autoHide) return false; } // stop here if the class is not valid if (!ASContext.IsClassValid()) return false; // handle switch (Value) { case '.': return HandleDotCompletion(Sci, autoHide); case ' ': position--; string word = GetWordLeft(Sci, ref position); DebugConsole.Trace("Word? "+word); if (word.Length > 0) switch (word) { case "new": case "extends": case "implements": return HandleNewCompletion(Sci, "", autoHide); case "import": return HandleImportCompletion(Sci, "", autoHide); case "public": return HandleDeclarationCompletion(Sci, "function static var", "", autoHide); case "private": return HandleDeclarationCompletion(Sci, "function static var", "", autoHide); case "static": return HandleDeclarationCompletion(Sci, "function private public var", "", autoHide); } break; case ':': ASContext.UnsetOutOfDate(); bool result = HandleColonCompletion(Sci, "", autoHide); ASContext.SetOutOfDate(); return result; case '(': return HandleFunctionCompletion(Sci); case ')': if (InfoTip.CallTipActive) InfoTip.Hide(); return false; case '*': return CodeAutoOnChar(Sci, Value); } } catch (Exception ex) { ErrorHandler.ShowError("Completion error", ex); } // CodeAuto context if (!PluginCore.Controls.CompletionList.Active) LastExpression = null; return false; }
/// <summary> /// Display method signature /// </summary> /// <param name="Sci">Scintilla control</param> /// <returns>Auto-completion has been handled</returns> static public bool HandleFunctionCompletion(ScintillaNet.ScintillaControl Sci) { // find method int position = Sci.CurrentPos-1; int parCount = 0; int braCount = 0; int comaCount = 0; int arrCount = 0; int style = 0; int stylemask = (1 << Sci.StyleBits) -1; char c; while (position >= 0) { style = Sci.StyleAt(position) & stylemask; if (style == 19) { string keyword = GetWordLeft(Sci, ref position); DebugConsole.Trace("Keyword "+keyword); if (!"new".StartsWith(keyword)) { position = -1; break; } } if (IsTextStyleEx(style)) { c = (char)Sci.CharAt(position); if (c == ';') { position = -1; break; } // skip {} () [] blocks if ( ((braCount > 0) && (c != '{')) || ((arrCount > 0) && (c != '[')) || ((parCount > 0) && (c != '('))) { position--; continue; } // new block if (c == '}') braCount++; else if (c == ']') arrCount++; else if (c == ')') parCount++; // block closed else if (c == '{') { if (braCount == 0) comaCount = 0; else braCount--; } else if (c == '[') { if (arrCount == 0) comaCount = 0; else arrCount--; } else if (c == '(') { if (--parCount < 0) // function start found break; } // new parameter reached else if (c == ',' && parCount == 0 && Sci.BaseStyleAt(position) != 6) comaCount++; } position--; } // continuing calltip ? if (HasCalltip()) { if (calltipPos == position) { ShowCalltip(Sci, comaCount); return true; } else InfoTip.Hide(); } else if (position < 0) return false; // get expression at cursor position ASExpr expr = GetExpression(Sci, position); DebugConsole.Trace("Expr: "+expr.Value); if (expr.Value == null || expr.Value.Length == 0 || expr.separator == ':' || (expr.Keyword == "function" && expr.separator == ' ')) return false; DebugConsole.Trace("** Display method parameters"); DebugConsole.Trace(expr.Value); // Context expr.LocalVars = ParseLocalVars(expr); ASClass aClass = ASContext.CurrentClass; // Expression before cursor ASResult result = EvalExpression(expr.Value, expr, aClass, true); // Show calltip if (!result.IsNull()) { ASMember method = result.Member; if (method == null) { string constructor = ASContext.GetLastStringToken(result.Class.ClassName,"."); method = result.Class.Methods.Search(constructor, FlagType.Constructor); if (method == null) return true; } else if ((method.Flags & FlagType.Function) == 0) return true; // calltip content calltipPos = position; calltipOffset = method.Name.Length; calltipDef = method.Name+"("+method.Parameters+")"; if (method.Type.Length > 0) calltipDef += " : "+method.Type; calltipMember = method; calltipDetails = UITools.ShowDetails; // show prevParam = ""; ShowCalltip(Sci, comaCount); } return true; }
static public string GetWordLeft(ScintillaNet.ScintillaControl Sci, ref int position) { string word = ""; string exclude = "(){};,+*/\\=:.%\"<>"; bool skipWS = true; int style; int stylemask = (1 << Sci.StyleBits) -1; char c; while (position >= 0) { style = Sci.StyleAt(position) & stylemask; if (IsTextStyleEx(style)) { c = (char)Sci.CharAt(position); if (c <= ' ') { if (!skipWS) break; } else if (exclude.IndexOf(c) >= 0) break; else if (style != 6) { word = c+word; skipWS = false; } } position--; } return word; }
/// <summary> /// Find Actionscript expression at cursor position /// TODO Improve this method /// </summary> /// <param name="sci">Scintilla Control</param> /// <param name="position">Cursor position</param> /// <returns></returns> static private ASExpr GetExpression(ScintillaNet.ScintillaControl Sci, int position) { ASExpr expression = new ASExpr(); int startPos = position; expression.Position = position; expression.separator = ' '; // get last expression (until ';') excluding comments int stylemask = (1 << Sci.StyleBits) -1; int style = (position > 0) ? Sci.StyleAt(position-1) & stylemask : 0; bool ignoreKey = false; if (style == 19) { DebugConsole.Trace("Ignore keyword"); ignoreKey = true; } StringBuilder sb = new StringBuilder(); char c; while ((position > 0) && (style != 19 || ignoreKey)) { position--; style = Sci.StyleAt(position) & stylemask; if (IsTextStyle(style) || ignoreKey) { c = (char)Sci.CharAt(position); if (c == ';') { expression.separator = c; break; } else if (c == '\n' || c == '\r') { if (sb.ToString().Trim().Length == 0) break; } sb.Insert(0,c); if (ignoreKey && IsTextStyle(style)) ignoreKey = false; } // we found a keyword else if (style == 19) { expression.separator = ' '; int keywordPos = position; string keyword = GetWordLeft(Sci, ref keywordPos); if ((keyword == "function") || (keyword == "get") || (keyword == "set")) { // we found a function declaration string test = sb.ToString().Trim(); // ignore anonymous function if ((keyword == "function") && test.StartsWith("(")) { keyword = null; break; } // guess context more precisely bool hasBraces = (test.IndexOf('{') >= 0); test = re_balancedBraces.Replace(test, ";"); // is it inside the function? if (test.IndexOf('{') >= 0) { c = ' '; while ((position < startPos) && (sb.Length > 0)) { position++; c = (char)Sci.CharAt(position); sb.Remove(0,1); if (c == '{') break; } expression.separator = c; } // is it NOT inside function parameters? else if (test.IndexOf(')') >= 0) { if (hasBraces) { expression.separator = '}'; expression.Value = ""; return expression; } else { // is it before the return type declaration? int colon = test.LastIndexOf(':'); if ((colon < 0) || (colon < test.LastIndexOf(')'))) // this is invalid return expression; } } // inside function parameters? else { int colon = test.LastIndexOf(':'); if ((colon < 0) || (colon < test.LastIndexOf(','))) return expression; } } else expression.Keyword = keyword; // note that we found a "case" statement //else if (keyword == "case") expression.Keyword = "case"; DebugConsole.Trace("Stopped at '"+keyword+"'"); DebugConsole.Trace("Raw '"+sb.ToString()+"'"); break; } } position++; string expr = sb.ToString(); sb = null; if (expr.Length > 0 && (expr[expr.Length-1] <= 32 && Sci.CharAt(startPos) != '(')) { expr = ""; expression.separator = ' '; expression.Position = Sci.CurrentPos; } else expression.PositionExpression = position; // refine last expression if (expr.Length > 0) { expr = re_balancedBraces.Replace(expr, ";"); expression.SubExpressions = ExtractedSubex = new StringCollection(); expr = re_balancedParenthesis.Replace(expr, new MatchEvaluator(ExtractSubex)); //DebugConsole.Trace("Raw '"+expr+"' @"+expression.PositionExpression); Match m = re_refineExpression.Match(expr); if (!m.Success) return expression; if (m.Index > 0) { expression.separator = expr[m.Index-1]; expression.PositionExpression = position += m.Index; // treat ':' as ';' after a case statement if (expression.separator == ':' && expression.Keyword == "case") expression.separator = ';'; expression.Keyword = null; } //DebugConsole.Trace("Refined '"+m.Value+"' @"+m.Index); expr = re_dot.Replace( re_whiteSpace.Replace(m.Value, " ") , "."); expr = re_subexMarker.Replace(expr, "#").Trim(); int space = Math.Max(expr.LastIndexOf(' '), expr.LastIndexOf(';')); if (space > 0) { expression.separator = ' '; expr = expr.Substring(space+1); } //ErrorHandler.ShowInfo("Clean '"+expr+"' @"+expression.PositionExpression); } expression.Value = expr; // get context function body int braceCount = 0; StringBuilder body = new StringBuilder(); StringBuilder context = new StringBuilder(); while (braceCount >= 0) { while (position > 0) { position--; style = Sci.StyleAt(position) & stylemask; if (IsTextStyleEx(style)) { c = (char)Sci.CharAt(position); if (c == '}') { body.Insert(0,c); braceCount++; } else if ((c == '{') && (--braceCount < 0)) break; if (braceCount == 0) body.Insert(0,c); } } if (braceCount >= 0) break; // get context function definition while (position > 0) { position--; style = Sci.StyleAt(position) & stylemask; if (IsTextStyleEx(style)) { c = (char)Sci.CharAt(position); if ((c == ';') || (c == '}') || (c == '{')) break; context.Insert(0,c); } } expression.ContextFunction = context.ToString(); // ignore dynamic function definition if (!re_validFunction.IsMatch(expression.ContextFunction)) { // stop if we reached the class definition if (re_classDefinition.IsMatch(expression.ContextFunction)) { expression.ContextFunction = null; expression.PositionContext = 0; break; } // continue search for function definition body.Insert(0,'{').Insert(0,context.ToString()); context = new StringBuilder(); position++; braceCount++; } else { expression.ContextBody = body.ToString(); expression.PositionContext = position+1; } } // result LastExpression = expression; return expression; }