public DslBuilder(XElement builderClassNode, XmlDocCommentReader commentReader, Type builderType) { input = builderClassNode.Value; syntaxStateClassname = builderClassNode.Attributes().Single(f => f.Name == "name").Value; outputNamespace = builderClassNode.Attributes().Single(f => f.Name == "namespace").Value; type = builderType; this.commentReader = commentReader; }
public ILexer<int> CreateDslParserFromRegularExpression(string input, Type builderType, XmlDocCommentReader commentReader) { List<Identifier> identifiers = CreateIdentifiers(builderType, commentReader).ToList(); IDictionary<string, char> usedIdentifiers = new Dictionary<string, char>(); // We are going to create a lexer to read the input var inputLexer = LexerFactory<char>.Configure(configurator => { Action<char> singleChar = c => configurator.Token(Regex.Escape(c.ToString()), f => c); char identifierChar = 'a'; configurator.Token("[A-Za-z_][A-Za-z_0-9]+", f => { if (usedIdentifiers.ContainsKey(f)) { return usedIdentifiers[f]; } // Make sure the identifer exists if (identifiers.SingleOrDefault(i => i.DslName == f) == null) { throw new Exception("No buildermethod called " + f + " was found in the class!"); } if (identifierChar == 'Z' + 1) identifierChar = 'a'; if (identifierChar > 'z') throw new Exception("Snout cannot handle the amount of symbols you're using. Use less"); usedIdentifiers.Add(f, identifierChar); return identifierChar++; }); // Identifiers singleChar('+'); singleChar('*'); singleChar('|'); singleChar('?'); singleChar('('); singleChar(')'); configurator.Ignore("\\s+"); configurator.Ignore(@"/\*([^*]+|\*[^/])*\*/"); }); inputLexer.SetSource(input); var dslRegEx = new StringBuilder(); // Use the lexer to get a regular expression! for (var token = inputLexer.Next(); token.Item1 != -1; token = inputLexer.Next() ) { dslRegEx.Append(token.Item2); } // Condense it into the Identifers dictionary Identifiers = identifiers.Where(f => usedIdentifiers.ContainsKey(f.DslName)).ToDictionary( f => usedIdentifiers[f.DslName]); // Create another lexer, this one we will return return LexerFactory<int>.Configure(c => c.Token(dslRegEx.ToString(), f => 0)); }
private string GetDocumentation(MethodInfo method, XmlDocCommentReader commentReader) { var methodComments = commentReader.GetComments(method); // We are going to take the comments verbatim, but remove the <buildermethod/> tag var builderNode = methodComments.Descendants().Single(f => f.Name == "buildermethod"); builderNode.Remove(); return "///" + string.Join("\n///", methodComments.Nodes().SelectMany( n => n.ToString().Split('\n').Where(f => !string.IsNullOrWhiteSpace(f)).Select(f => f.TrimStart()))); }
private BuilderMethod GetBuilderMethod(MethodInfo methodInfo, XmlDocCommentReader commentReader) { var comments = commentReader.GetComments(methodInfo); if (comments != null) { // Look for a buildermethod subnode. If this is found create a BuilderMethod instance var builderMethodNode = comments.Descendants().SingleOrDefault(f => f.Name == "buildermethod"); if (builderMethodNode != null) { Func<string, string, string> attributeValueOrDefault = (attributeName, defaultValue) => { var attribute = builderMethodNode.Attributes().SingleOrDefault(n => n.Name == attributeName); return attribute == null ? defaultValue : attribute.Value; }; string fluentName = attributeValueOrDefault("name", methodInfo.Name); string dslName = attributeValueOrDefault("dslname", fluentName); bool useProperty = Convert.ToBoolean(attributeValueOrDefault("useproperty", "true")); return new BuilderMethod { FluentName = fluentName, DslName = dslName, UseProperty = useProperty }; } } return null; }
private IEnumerable<Identifier> CreateIdentifiers(Type builderType, XmlDocCommentReader commentReader) { foreach (var methodInfo in builderType.GetMethods()) { // Read the xml documentation for this method. Look for the buildermethod tag // If this is found, we create a suitable terminal for this method var builderMethod = GetBuilderMethod(methodInfo, commentReader); if (builderMethod != null) { var identifier = new Identifier {DslName = Regex.Escape(builderMethod.DslName)}; var method = new StringBuilder(builderMethod.FluentName); // If there are generic type arguments to the builder method these will need to be transferred to the // new interface method var genericArguments = methodInfo.GetGenericArguments(); if (genericArguments.Any()) { method.Append("<"); var dynamicAttribute = methodInfo.GetCustomAttributes(typeof(DynamicAttribute), true).OfType<DynamicAttribute>().FirstOrDefault(); IEnumerable<bool> transformFlags = dynamicAttribute == null ? null : dynamicAttribute.TransformFlags; method.Append(string.Join(", ", genericArguments.Select(f => { var ret = GetFullName(f, transformFlags); if (transformFlags != null) transformFlags = transformFlags.Skip(1); return ret; }))); method.Append(">"); } Func<string, string> applyBracersIfNotEmpty = f => f.Length > 0 ? "<" + f + ">" : ""; // Append stuff to the debug name to make it call the builder method // and to carry along the properties var parameters = methodInfo.GetParameters(); var builderCall = string.Format("{0}{1}({2})", methodInfo.Name, applyBracersIfNotEmpty(string.Join(",", methodInfo.GetGenericArguments().Select(f => f.Name))), string.Join(", ", parameters.Select(f => f.Name).ToArray())); if (!parameters.Any() && !methodInfo.GetGenericArguments().Any() && builderMethod.UseProperty) { // No parameters and user wants this rendered as a property method.Append(@" {{ get {{ builder." + builderCall + @"; return new {0}{1}(builder); }} }}"); } else { // There is supposed to be a method call method.AppendFormat("({0})", parameters.Any() ? string.Join(", ", parameters.Select(f => { var dynamicAttribute = f.GetCustomAttributes(typeof(DynamicAttribute), true).OfType<DynamicAttribute>().FirstOrDefault(); return string.Format("{0} {1}", GetFullName(f.ParameterType, dynamicAttribute != null ? dynamicAttribute.TransformFlags : null), f.Name); }).ToArray<object>()) : "" ); method.Append(@" {{ builder." + builderCall + @"; return new {0}{1}(builder); }}"); } identifier.MethodContents = method.ToString(); identifier.Documentation = GetDocumentation(methodInfo, commentReader); yield return identifier; } } }
/// <summary> /// Creates a new instances that reads comments from the specified XML Doc comments file. /// </summary> /// <param name="docCommentsFullPath">The full path of the XML Doc comments file.</param> public JoltCommentReader(string docCommentsFullPath) { if (docCommentsFullPath == null) throw new ArgumentNullException("docCommentsFullPath"); _proxy = new XmlDocCommentReader(docCommentsFullPath); }
/// <summary> /// Formats the text to be displayed in the tooltip using information from the object's member and from the XML documentation, if existing. /// </summary> /// <param name="scintilla"></param> /// <param name="member">Reflected member to display information from.</param> /// <param name="reader">Jolt's XmlDocCommentReader instance, to get and display comments from assembly-generated XML file.</param> /// <returns>The formatted text to display in the tooltip.</returns> /// <remarks></remarks> private static string FormatHelpTip(this ScintillaNET.Scintilla scintilla, MemberInfo member, Jolt.XmlDocCommentReader reader) { switch (member.MemberType) { case MemberTypes.Method: dynamic methods = Type.GetType(member.DeclaringType.FullName).GetMethods().Where(m => m.Name == member.Name).ToList(); dynamic method = methods[0]; string summary = ""; string returntype = ""; string returndescription = ""; string remarks = ""; Dictionary <string, string> argumentdescriptions = new Dictionary <string, string>(); string txthelp = method.DeclaringType.Name + " method '" + member.Name + "'" + "\r\n"; dynamic xmlhelp = reader.GetComments(method); if ((xmlhelp != null)) { dynamic @params = xmlhelp.Elements("param").ToList; foreach (var p_loopVariable in @params) { var p = p_loopVariable; if (p.Value.ToString.Length > 70) { argumentdescriptions.Add(p.Attribute("name"), p.Value.ToString.Substring(0, 70).Trim("\n") + " [...]"); } else { argumentdescriptions.Add(p.Attribute("name"), p.Value.ToString.Trim("\r\n")); } } if (method.ReturnType.Name != "Void") { dynamic rdesc = xmlhelp.Elements("returns").FirstOrDefault; if ((rdesc != null)) { returndescription = rdesc.Value; } } dynamic redesc = xmlhelp.Elements("remarks").FirstOrDefault; if ((redesc != null)) { if (redesc.Value.Length > 1000) { remarks = redesc.Value.Substring(0, 1000) + " [...]"; } else { remarks = redesc.Value; } } redesc = xmlhelp.Elements("summary").FirstOrDefault; if ((redesc != null)) { summary = redesc.Value; txthelp += summary + "\r\n"; } } if (method.GetParameters.Count > 0) { txthelp += "Parameters:" + "\r\n" + "\r\n"; txthelp += "Type".PadRight(18) + "Name".PadRight(15) + "Description" + "\r\n"; foreach (var par_loopVariable in method.GetParameters) { var par = par_loopVariable; if (argumentdescriptions.ContainsKey(par.Name)) { txthelp += par.ParameterType.Name.PadRight(18) + par.Name.PadRight(15) + argumentdescriptions[par.Name] + "\r\n"; } else { txthelp += par.ParameterType.Name.PadRight(18) + par.Name.PadRight(15) + "\r\n"; } } txthelp += "\r\n"; } txthelp += "Return Type: " + method.ReturnType.ToString; if (!string.IsNullOrEmpty(returndescription)) { txthelp += "\r\n" + "Return Parameter Description: " + returndescription; } if (!string.IsNullOrEmpty(remarks)) { txthelp += "\r\n" + "\r\n" + "Remarks: " + remarks; } return(txthelp); case MemberTypes.Property: dynamic props = Type.GetType(member.DeclaringType.FullName).GetProperties().Where(p => p.Name == member.Name).ToList(); dynamic prop = props(0); summary = ""; string proptype = ""; txthelp = prop.DeclaringType.Name + " property '" + prop.Name + "'" + "\r\n"; txthelp += "Type: " + prop.PropertyType.ToString; xmlhelp = reader.GetComments(prop); if ((xmlhelp != null)) { dynamic redesc = xmlhelp.Elements("summary").FirstOrDefault; if ((redesc != null)) { txthelp += "\r\n" + "Description: " + redesc.Value; } } return(txthelp); default: return(""); } }
/// <summary> /// Show a tooltip with information about the entered object method or property. /// </summary> /// <param name="scintilla"></param> /// <param name="reader">Jolt's XmlDocCommentReader instance, to get and display comments from assembly-generated XML file.</param> /// <remarks></remarks> public static void ShowToolTip(this ScintillaNET.Scintilla scintilla, Jolt.XmlDocCommentReader reader) { //parses the last keyword (object) (before the ".") and get suggestions for the autocomplete box from its properties and methods string [] splitters = { ".", "(", ")", " ", "\r", "\n", "\r\n" }; dynamic text = ScintillaExtender.getLastWord(scintilla).Split(splitters, StringSplitOptions.RemoveEmptyEntries); dynamic lastchar = Convert.ToChar(scintilla.GetCharAt(scintilla.CurrentPosition)); string helptext = ""; if (text.Length >= 2) { string lastkeyword = text[text.Length - 1]; string lastobj = text[text.Length - 2].Trim(); //string obj = Convert.ToString(lastobj)); switch (lastobj) { case "Browser": dynamic prop = typeof(TopDown_QA_FrameWork.Driver).GetMember(lastkeyword); if (prop.Length > 0) { helptext = ScintillaExtender.FormatHelpTip(scintilla, prop[0], reader); } break; case "ims1": case "ims2": case "ims3": case "ims4": case "ims5": case "ims6": case "oms1": case "oms2": case "oms3": case "oms4": case "oms5": case "MaterialStream": prop = Type.GetType("DWSIM.DWSIM.SimulationObjects.Streams.MaterialStream").GetMember(lastkeyword); if (prop.Length > 0) { helptext = ScintillaExtender.FormatHelpTip(scintilla, prop[0], reader); } break; case "ies1": case "oes1": case "EnergyStream": prop = Type.GetType("DWSIM.DWSIM.SimulationObjects.Streams.EnergyStream").GetMember(lastkeyword); if (prop.Length > 0) { helptext = ScintillaExtender.FormatHelpTip(scintilla, prop[0], reader); } break; case "Flowsheet": prop = Type.GetType("DWSIM.FormFlowsheet").GetMember(lastkeyword); if (prop.Length > 0) { helptext = ScintillaExtender.FormatHelpTip(scintilla, prop[0], reader); } break; case "Spreadsheet": prop = Type.GetType("DWSIM.SpreadsheetForm").GetMember(lastkeyword); if (prop.Length > 0) { helptext = ScintillaExtender.FormatHelpTip(scintilla, prop[0], reader); } break; case "PropertyPackage": prop = Type.GetType("DWSIM.DWSIM.SimulationObjects.PropertyPackages.PropertyPackage").GetMember(lastkeyword); if (prop.Length > 0) { helptext = ScintillaExtender.FormatHelpTip(scintilla, prop[0], reader); } break; case "UnitOp": case "Me": prop = Type.GetType("DWSIM.SimulationObjects_UnitOpBaseClass").GetMember(lastkeyword); if (prop.Length > 0) { helptext = ScintillaExtender.FormatHelpTip(scintilla, prop(0), reader); } break; case "Solver": prop = Type.GetType("DWSIM.DWSIM.Flowsheet.FlowsheetSolver").GetMember(lastkeyword); if (prop.Length > 0) { helptext = ScintillaExtender.FormatHelpTip(scintilla, prop(0), reader); } break; } //shows the tooltip if (!string.IsNullOrEmpty(helptext)) { scintilla.CallTipShow(scintilla.CurrentPosition, helptext); } else { scintilla.CallTipCancel(); } } else { //hides tooltip if visible scintilla.CallTipCancel(); } }
static void Main(string[] args) { string assemblyPath = null; string docPath = null; string outputPath = null; var optionSet = new OptionSet { { "a=|assembly=", "Path to assembly to use", f => assemblyPath = f }, { "d=|doc=", "Path to documentation to use", f => docPath = f }, { "o=|output=", "Output path", f => outputPath = f } }; try { optionSet.Parse(args); } catch (OptionException) { Usage(optionSet); return; } if (assemblyPath == null) { Usage(optionSet); return; } assemblyPath = Path.GetFullPath(assemblyPath); if (docPath == null) { docPath = Path.Combine(Path.GetDirectoryName(assemblyPath),Path.GetFileNameWithoutExtension(assemblyPath) + ".xml"); } if (outputPath == null) { outputPath = Directory.GetCurrentDirectory(); } Console.WriteLine("Generating DSL from assembly {0}", assemblyPath); var targetAssembly = Assembly.LoadFile(assemblyPath); Console.WriteLine("Loading documentation from {0}", docPath); var commentReader = new XmlDocCommentReader(docPath); foreach (var type in targetAssembly.GetTypes()) { var comments = commentReader.GetComments(type); if (comments != null) { var builderClassNode = comments.Descendants().FirstOrDefault(f => f.Name == "builderclass"); if (builderClassNode != null) { var outputFile = string.Format("{0}.cs", builderClassNode.Attributes().Single(f => f.Name == "name").Value); var dslBuilder = new DslBuilder(builderClassNode, commentReader, type); string dslCode = dslBuilder.CreateDslCode(); using (var fileWriter = new StreamWriter(new FileStream(Path.Combine(outputPath, outputFile), FileMode.Create, FileAccess.Write))) { fileWriter.Write(dslCode); } } } } }