DynamoVisualProgramming.DynamoServices by Autodesk

<PackageReference Include="DynamoVisualProgramming.DynamoServices" Version="3.6.0-beta8641" />

 PythonCodeCompletionProviderCommon

This class represents a base class for Python code completion providers It partially implements the IExternalCodeCompletionProviderCore interface and contains a collection of utility functions/properties that are common among existing code completion provider classes
using Autodesk.DesignScript.Interfaces; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; namespace Dynamo.PythonServices { internal abstract class PythonCodeCompletionProviderCommon : IExternalCodeCompletionProviderCore { protected enum PythonScriptType { SingleStatement, Statements, Expression } internal static readonly string commaDelimitedVariableNamesRegex = "(([0-9a-zA-Z_]+,?\\s?)+)"; internal static readonly string variableName = "([0-9a-zA-Z_]+(\\.[a-zA-Z_0-9]+)*)"; internal static readonly string spacesOrNone = "(\\s*)"; internal static readonly string atLeastOneSpaceRegex = "(\\s+)"; internal static readonly string dictRegex = "({.*})"; internal static readonly string basicImportRegex = "(import)"; internal static readonly string fromImportRegex = "^(from)"; internal static string arrayRegex = "(\\[.*\\])"; internal static string doubleRegex = "([-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?)"; internal static string intRegex = "([-+]?\\d+)[\\s\\n]*$"; internal const string quotesStringRegex = "[\"']([^\"']*)[\"']"; internal const string equalsRegex = "(=)"; internal static readonly Regex MATCH_LAST_NAMESPACE = new Regex("[\\w.]+$", RegexOptions.Compiled); internal static readonly Regex MATCH_LAST_WORD = new Regex("\\w+$", RegexOptions.Compiled); internal static readonly Regex MATCH_FIRST_QUOTED_NAME = new Regex("[\"']([^\"']*)[\"']", RegexOptions.Compiled); internal static readonly Regex MATCH_VALID_TYPE_NAME_CHARACTERS_ONLY = new Regex("^\\w+", RegexOptions.Compiled); internal static readonly Regex TRIPLE_QUOTE_STRINGS = new Regex(".*?\\\"{{3}}[\\s\\S]+?\\\"{{3}}", RegexOptions.Compiled); internal static readonly Regex MATCH_IMPORT_STATEMENTS = new Regex("^import\\s+?(.+)", RegexOptions.Multiline | RegexOptions.Compiled); internal static readonly Regex MATCH_FROM_IMPORT_STATEMENTS = new Regex("from\\s+?([\\w.]+)\\s+?import\\s+?([\\w, *]+)", RegexOptions.Multiline | RegexOptions.Compiled); internal static readonly Regex MATCH_VARIABLE_ASSIGNMENTS = new Regex("^[ \\t]*?(\\w+(\\s*?,\\s*?\\w+)*)\\s*?=\\s*(.+)", RegexOptions.Multiline | RegexOptions.Compiled); internal static readonly Regex STRING_VARIABLE = new Regex("^[\"']([^\"']*)[\"']$", RegexOptions.Compiled); internal static readonly Regex DOUBLE_VARIABLE = new Regex("^-?\\d+\\.\\d+$", RegexOptions.Compiled); internal static readonly Regex INT_VARIABLE = new Regex("^-?\\d+$", RegexOptions.Compiled); internal static readonly Regex LIST_VARIABLE = new Regex("^\\[.*\\]$", RegexOptions.Compiled); internal static readonly Regex DICT_VARIABLE = new Regex("^{.*}$", RegexOptions.Compiled); internal static readonly string BAD_ASSIGNEMNT_ENDS = ",([{"; internal static readonly string inBuiltMethod = "built-in"; internal static readonly string method = "method"; internal static readonly string internalType = "Autodesk"; internal static readonly string clrReference = "clr.AddReference"; private static string[] knownAssemblies = new string[4] { "mscorlib", "RevitAPI", "RevitAPIUI", "ProtoGeometry" }; protected List<Tuple<Regex, Type>> BasicVariableTypes; protected HashSet<string> ClrModules { get; set; } protected Dictionary<string, int> BadStatements { get; set; } public Dictionary<string, Type> VariableTypes { get; set; } public Dictionary<string, Type> ImportedTypes { get; set; } protected abstract object GetDescriptionObject(string docCommand); public abstract IExternalCodeCompletionData[] GetCompletionData(string code, bool expand = false); public string GetDescription(string stub, string item, bool isInstance) { string result = "No description available"; if (!string.IsNullOrEmpty(item)) try { string text = ""; text = ((!isInstance) ? (stub + "." + item + ".__doc__") : ("type(" + stub + ")." + item + ".__doc__")); object descriptionObject = GetDescriptionObject(text); if (string.IsNullOrEmpty((string)descriptionObject)) return result; result = (string)descriptionObject; return result; } catch { return result; } return result; } public abstract bool IsSupportedEngine(string engineName); public abstract void Initialize(string dynamoCorePath); protected IEnumerable<Tuple<string, string, bool, ExternalCodeCompletionType>> EnumerateMembers(Type type, string name) { List<Tuple<string, string, bool, ExternalCodeCompletionType>> list = new List<Tuple<string, string, bool, ExternalCodeCompletionType>>(); SortedList<string, ExternalCodeCompletionType> sortedList = new SortedList<string, ExternalCodeCompletionType>(); MethodInfo[] methods = type.GetMethods(); PropertyInfo[] properties = type.GetProperties(); FieldInfo[] fields = type.GetFields(); MethodInfo[] array = methods; foreach (MethodInfo methodInfo in array) { if (methodInfo.IsPublic && methodInfo.Name.IndexOf("get_") != 0 && methodInfo.Name.IndexOf("set_") != 0 && methodInfo.Name.IndexOf("add_") != 0 && methodInfo.Name.IndexOf("remove_") != 0 && methodInfo.Name.IndexOf("__") != 0 && !sortedList.ContainsKey(methodInfo.Name)) sortedList.Add(methodInfo.Name, ExternalCodeCompletionType.Method); } PropertyInfo[] array2 = properties; foreach (PropertyInfo propertyInfo in array2) { if (!sortedList.ContainsKey(propertyInfo.Name)) sortedList.Add(propertyInfo.Name, ExternalCodeCompletionType.Property); } FieldInfo[] array3 = fields; foreach (FieldInfo fieldInfo in array3) { if (!sortedList.ContainsKey(fieldInfo.Name)) sortedList.Add(fieldInfo.Name, ExternalCodeCompletionType.Field); } if (type.IsEnum) { string[] enumNames = type.GetEnumNames(); foreach (string key in enumNames) { if (!sortedList.ContainsKey(key)) sortedList.Add(key, ExternalCodeCompletionType.Field); } } foreach (KeyValuePair<string, ExternalCodeCompletionType> item in sortedList) { list.Add(Tuple.Create(item.Key, name, true, item.Value)); } return list; } protected static string GetLastName(string text) { return MATCH_LAST_WORD.Match(text.Trim(new char[1] { '.' }).Trim()).Value; } protected static string GetLastNameSpace(string text) { return MATCH_LAST_NAMESPACE.Match(text.Trim(new char[1] { '.' }).Trim()).Value; } protected static string GetFirstPossibleTypeName(string line) { Match match = MATCH_VALID_TYPE_NAME_CHARACTERS_ONLY.Match(line); return match.Success ? match.Value : ""; } protected static string StripDocStrings(string code) { string[] value = TRIPLE_QUOTE_STRINGS.Split(code); return string.Join("", value); } protected static List<string> FindClrReferences(string code) { List<string> list = new List<string>(); string[] array = code.Split('\n', ';'); foreach (string text in array) { if (text.Contains(clrReference)) list.Add(text.Trim()); } return list; } protected static Type TryGetTypeFromFullName(string name) { string[] array = knownAssemblies; foreach (string arg in array) { Type type = Type.GetType($"{name}""{arg}"); if (type != (Type)null) return type; } return null; } protected abstract object EvaluateScript(string script, PythonScriptType type); protected abstract void LogError(string msg); protected internal abstract bool ScopeHasVariable(string name); protected abstract Type GetCLRType(string name); protected internal void UpdateImportedTypes(string code) { List<string> list = FindClrReferences(code); foreach (string item4 in list) { int value = 0; BadStatements.TryGetValue(item4, out value); if (value <= 3) try { string libName = MATCH_FIRST_QUOTED_NAME.Match(item4).Groups[1].Value; if (!ClrModules.Contains(libName)) { if (item4.Contains("AddReferenceToFileAndPath")) { EvaluateScript(item4, PythonScriptType.SingleStatement); ClrModules.Add(libName); } else if (AppDomain.CurrentDomain.GetAssemblies().Any((Assembly x) => x.GetName().Name == libName)) { EvaluateScript(item4, PythonScriptType.SingleStatement); ClrModules.Add(libName); } } } catch (Exception ex) { LogError($"""{item4}"); LogError(ex.ToString()); BadStatements[item4] = value + 1; } } List<Tuple<string, string, string>> list2 = FindAllImportStatements(code); foreach (Tuple<string, string, string> item5 in list2) { string item = item5.Item1; string item2 = item5.Item2; string item3 = item5.Item3; string text = item3 ?? item2; string text2 = ""; int value2 = 0; if (!(text != "*") || (!ScopeHasVariable(text) && !ImportedTypes.ContainsKey(text))) try { text2 = ((item == null) ? ((item3 != null) ? $"""{item2}""{item3}" : $"""{item2}") : ((!(item2 != "*") || item3 == null) ? $"""{item}""" : $"""{item}""{item2}""{item3}")); BadStatements.TryGetValue(text2, out value2); if (value2 <= 3) { EvaluateScript(text2, PythonScriptType.SingleStatement); if (!(item2 == "*")) { string typeName = (item == null) ? item2 : $"{item}""{item2}"; Type type = Type.GetType(typeName); ImportedTypes.Add(text, type); } } } catch (Exception) { LogError($"""{item2}""{text2}"); BadStatements[text2] = value2 + 1; } } } internal void UpdateVariableTypes(string code) { VariableTypes.Clear(); VariableTypes = FindAllVariableAssignments(code); } protected Dictionary<string, Type> FindAllVariableAssignments(string code) { Dictionary<string, Type> dictionary = new Dictionary<string, Type>(); MatchCollection matchCollection = MATCH_VARIABLE_ASSIGNMENTS.Matches(code); foreach (Match item in matchCollection) { string text = item.Groups[1].Value.Trim(); string text2 = item.Groups[3].Value.Trim(); if (!Enumerable.Contains(BAD_ASSIGNEMNT_ENDS, text2.Last())) { string[] array = (from x in text.Split(new char[1] { ',' }) select x.Trim()).ToArray(); string[] array2 = (from x in text2.Split(new char[1] { ',' }) select x.Trim()).ToArray(); if (array2.Length >= array.Length) { if (array.Length == 1 && array2.Length > 1) array2 = new string[1] { text2 }; if (array.Length == array2.Length) { for (int i = 0; i < array.Length; i++) { bool flag = false; foreach (Tuple<Regex, Type> basicVariableType in BasicVariableTypes) { if (basicVariableType.Item1.IsMatch(array2[i])) { dictionary[array[i]] = basicVariableType.Item2; flag = true; break; } } if (!flag) { string firstPossibleTypeName = GetFirstPossibleTypeName(array2[i]); if (!string.IsNullOrEmpty(firstPossibleTypeName)) { if (!dictionary.TryGetValue(firstPossibleTypeName, out Type value)) value = TryGetType(firstPossibleTypeName); if (value != (Type)null) dictionary[array[i]] = value; } } } } } } } return dictionary; } protected Type TryGetType(string name) { if (ImportedTypes.ContainsKey(name)) return ImportedTypes[name]; Type type = null; try { type = GetCLRType(name); } catch (Exception ex) { LogError($"""{name}"); LogError(ex.ToString()); } if (type != (Type)null) ImportedTypes[name] = type; return type; } internal static Dictionary<string, string> FindAllTypeImportStatements(string code) { string pattern = fromImportRegex + atLeastOneSpaceRegex + variableName + atLeastOneSpaceRegex + basicImportRegex + atLeastOneSpaceRegex + "\\*$"; MatchCollection matchCollection = Regex.Matches(code, pattern, RegexOptions.Multiline); Dictionary<string, string> dictionary = new Dictionary<string, string>(); for (int i = 0; i < matchCollection.Count; i++) { string value = matchCollection[i].Groups[0].Value; string key = matchCollection[i].Groups[3].Value.Trim(); if (!dictionary.ContainsKey(key)) dictionary.Add(key, value); } return dictionary; } internal static Dictionary<string, string> FindTypeSpecificImportStatements(string code) { string pattern = fromImportRegex + atLeastOneSpaceRegex + variableName + atLeastOneSpaceRegex + basicImportRegex + atLeastOneSpaceRegex + commaDelimitedVariableNamesRegex + "$"; MatchCollection matchCollection = Regex.Matches(code, pattern, RegexOptions.Multiline); Dictionary<string, string> dictionary = new Dictionary<string, string>(); for (int i = 0; i < matchCollection.Count; i++) { string text = matchCollection[i].Groups[0].Value.TrimEnd('\r', '\n'); string text2 = matchCollection[i].Groups[8].Value.Trim(); string[] array = text2.Replace(" ", "").Split(new char[1] { ',' }); string[] array2 = array; foreach (string text3 in array2) { if (!dictionary.ContainsKey(text3)) dictionary.Add(text3, text.Replace(text2, text3)); } } return dictionary; } internal static Dictionary<string, string> FindVariableStatementWithRegex(string code, string valueRegex) { string pattern = variableName + spacesOrNone + "(=)" + spacesOrNone + valueRegex; MatchCollection matchCollection = Regex.Matches(code, pattern); Dictionary<string, string> dictionary = new Dictionary<string, string>(); for (int i = 0; i < matchCollection.Count; i++) { string key = matchCollection[i].Groups[1].Value.Trim(); string value = matchCollection[i].Groups[6].Value.Trim(); dictionary.Add(key, value); } return dictionary; } protected static List<Tuple<string, string, string>> FindAllImportStatements(string code) { List<Tuple<string, string, string>> list = new List<Tuple<string, string, string>>(); MatchCollection matchCollection = MATCH_IMPORT_STATEMENTS.Matches(code); foreach (Match item5 in matchCollection) { List<string> list2 = new List<string>(); if (item5.Value.EndsWith(".")) { foreach (Group group in item5.Groups) { string value = item5.Value; value = value.Replace("\t", " ").Replace("\n", " ").Replace("\r", " "); int val = value.LastIndexOf(' '); int val2 = value.LastIndexOf('='); string text = value.Substring(Math.Max(val, val2) + 1).Trim(new char[1] { '.' }).Trim(new char[1] { '(' }); List<string> list3 = (from x in text.Trim().Split(new char[1] { ',' }) select x.Trim()).ToList(); foreach (string item6 in list3) { list2.Add(item6); } } } else list2 = (from x in item5.Groups[1].Value.Trim().Split(new char[1] { ',' }) select x.Trim()).ToList(); foreach (string item7 in list2) { string[] array = item7.Split(new string[1] { " as " }, 2, StringSplitOptions.RemoveEmptyEntries); string item = array[0]; string item2 = (array.Length > 1) ? array[1] : null; list.Add(new Tuple<string, string, string>(null, item, item2)); } } MatchCollection matchCollection2 = MATCH_FROM_IMPORT_STATEMENTS.Matches(code); foreach (Match item8 in matchCollection2) { string value2 = item8.Groups[1].Value; IEnumerable<string> enumerable = from x in item8.Groups[2].Value.Trim().Split(new char[1] { ',' }) select x.Trim(); foreach (string item9 in enumerable) { string[] array2 = item9.Split(new string[1] { " as " }, 2, StringSplitOptions.RemoveEmptyEntries); string item3 = array2[0]; string item4 = (array2.Length > 1) ? array2[1] : null; list.Add(new Tuple<string, string, string>(value2, item3, item4)); } } return list; } internal Dictionary<string, Tuple<string, int, Type>> FindAllVariables(string code) { Dictionary<string, Tuple<string, int, Type>> dictionary = new Dictionary<string, Tuple<string, int, Type>>(); string pattern = variableName + spacesOrNone + "(=)" + spacesOrNone + "(.*)"; MatchCollection matchCollection = Regex.Matches(code, pattern, RegexOptions.Multiline); for (int i = 0; i < matchCollection.Count; i++) { string key = matchCollection[i].Groups[1].Value.Trim(); string text = matchCollection[i].Groups[6].Value.Trim(); int index = matchCollection[i].Index; string firstPossibleTypeName = GetFirstPossibleTypeName(text); if (!string.IsNullOrEmpty(firstPossibleTypeName)) { Type type = TryGetType(firstPossibleTypeName); if (type != (Type)null) { if (dictionary.ContainsKey(key)) { if (index > dictionary[key].Item2) dictionary[key] = new Tuple<string, int, Type>(text, index, type); } else dictionary.Add(key, new Tuple<string, int, Type>(text, index, type)); continue; } } foreach (Tuple<Regex, Type> basicVariableType in BasicVariableTypes) { MatchCollection matchCollection2 = Regex.Matches(text, "^" + basicVariableType.Item1?.ToString() + "$", RegexOptions.Singleline); if (matchCollection2.Count > 0) { if (dictionary.ContainsKey(key)) { if (index > dictionary[key].Item2) dictionary[key] = new Tuple<string, int, Type>(text, index, basicVariableType.Item2); } else dictionary.Add(key, new Tuple<string, int, Type>(text, index, basicVariableType.Item2)); break; } } } return dictionary; } internal static Dictionary<string, string> FindBasicImportStatements(string code) { string pattern = "^" + basicImportRegex + spacesOrNone + variableName; MatchCollection matchCollection = Regex.Matches(code, pattern, RegexOptions.Multiline); Dictionary<string, string> dictionary = new Dictionary<string, string>(); for (int i = 0; i < matchCollection.Count; i++) { string value = matchCollection[i].Groups[0].Value; string key = matchCollection[i].Groups[3].Value.Trim(); if (!dictionary.ContainsKey(key)) dictionary.Add(key, value); } return dictionary; } } }