X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Ftools%2Fmonodoc%2FMonodoc%2Fecma-provider.cs;h=e0e52691b17fc19ae98151c65717519b391b951a;hb=e2b2d181084848f3c5dde2788370db1b79893c69;hp=0fc6dac121ce09aaf80ac8911eabf41248a74701;hpb=33f149233f660bc0353546fe09b782a1ab9c4289;p=mono.git diff --git a/mcs/tools/monodoc/Monodoc/ecma-provider.cs b/mcs/tools/monodoc/Monodoc/ecma-provider.cs index 0fc6dac121c..e0e52691b17 100644 --- a/mcs/tools/monodoc/Monodoc/ecma-provider.cs +++ b/mcs/tools/monodoc/Monodoc/ecma-provider.cs @@ -7,6 +7,8 @@ // // (C) 2002, 2003 Ximian, Inc. // (C) 2003 Joshua Tauberer. +// Copyright 2003-2011 Novell +// Copyright 2011 Xamarin Inc // // TODO: // Should cluster together constructors @@ -24,10 +26,11 @@ using System.Xml; using System.Xml.XPath; using System.Xml.Xsl; using System.Text; +using System.Linq; using System.Collections; -using ICSharpCode.SharpZipLib.Zip; -using Monodoc.Lucene.Net.Index; -using Monodoc.Lucene.Net.Documents; +using System.Collections.Generic; +using Mono.Lucene.Net.Index; +using Mono.Lucene.Net.Documents; using Mono.Documentation; @@ -134,6 +137,73 @@ public static class EcmaDoc { return type; } + // Lala + static bool IsItReallyAGenericType (string type) + { + switch (type) { + case "Type": + case "TimeZone": + case "TimeZoneInfo": + case "TimeSpan": + case "TypeReference": + case "TypeCode": + case "TimeZoneInfo+AdjustmentRule": + case "TimeZoneInfo+TransitionTime": + return false; + } + if (type.StartsWith ("Tuple")) + return false; + return true; + } + + public static string ConvertFromCTSName (string ctsType) + { + if (string.IsNullOrEmpty (ctsType)) + return string.Empty; + + // Most normal type should have a namespace part and thus a point in their name + if (ctsType.IndexOf ('.') != -1) + return ctsType; + + if (ctsType.EndsWith ("*")) + return ConvertFromCTSName(ctsType.Substring(0, ctsType.Length - 1)) + "*"; + if (ctsType.EndsWith ("&")) + return ConvertFromCTSName(ctsType.Substring(0, ctsType.Length - 1)) + "&"; + if (ctsType.EndsWith ("]")) { // Array may be multidimensional + var idx = ctsType.LastIndexOf ('['); + return ConvertFromCTSName (ctsType.Substring (0, idx)) + ctsType.Substring (idx); + } + + // Big hack here, we tentatively try to say if a type is a generic when it starts with a upper case T + if ((char.IsUpper (ctsType, 0) && ctsType.Length == 1) || (ctsType[0] == 'T' && IsItReallyAGenericType (ctsType))) + return ctsType; + + switch (ctsType) { + case "byte": return "System.Byte"; + case "sbyte": return "System.SByte"; + case "short": return "System.Int16"; + case "int": return "System.Int32"; + case "long": return "System.Int64"; + + case "ushort": return "System.UInt16"; + case "uint": return "System.UInt32"; + case "ulong": return "System.UInt64"; + + case "float": return "System.Single"; + case "double": return "System.Double"; + case "decimal": return "System.Decimal"; + case "bool": return "System.Boolean"; + case "char": return "System.Char"; + case "string": return "System.String"; + + case "object": return "System.Object"; + case "void": return "System.Void"; + } + + // If we arrive here, the type was probably stripped of its 'System.' + return "System." + ctsType; + } + internal static string GetNamespaceFile (string dir, string ns) { string nsxml = Path.Combine (dir, "ns-" + ns + ".xml"); @@ -141,6 +211,54 @@ public static class EcmaDoc { nsxml = Path.Combine (dir, ns + ".xml"); return nsxml; } + + internal static string GetImageFile (string dir, string img) + { + string path = Path.Combine (dir, Path.Combine ("_images", img)); + return File.Exists (path) ? path : null; + } + + public static string GetCref (XmlElement member) + { + string typeName = XmlDocUtils.ToEscapedTypeName (member.SelectSingleNode("/Type/@FullName").InnerText); + if (member.Name == "Type") + return "T:" + typeName; + string memberType = member.SelectSingleNode("MemberType").InnerText; + switch (memberType) { + case "Constructor": + return "C:" + typeName + MakeArgs(member); + case "Event": + return "E:" + typeName + "." + XmlDocUtils.ToEscapedMemberName (member.GetAttribute("MemberName")); + case "Field": + return "F:" + typeName + "." + XmlDocUtils.ToEscapedMemberName (member.GetAttribute("MemberName")); + case "Method": { + string name = "M:" + typeName + "." + XmlDocUtils.ToEscapedMemberName (member.GetAttribute("MemberName")) + MakeArgs(member); + if (member.GetAttribute("MemberName") == "op_Implicit" || member.GetAttribute("MemberName") == "op_Explicit") + name += "~" + XmlDocUtils.ToTypeName (member.SelectSingleNode("ReturnValue/ReturnType").InnerText, member); + return name; + } + case "Property": + return "P:" + typeName + "." + XmlDocUtils.ToEscapedMemberName (member.GetAttribute("MemberName")) + MakeArgs(member); + default: + throw new NotSupportedException ("MemberType '" + memberType + "' is not supported."); + } + } + + private static string MakeArgs (XmlElement member) + { + XmlNodeList parameters = member.SelectNodes ("Parameters/Parameter"); + if (parameters.Count == 0) + return member.SelectSingleNode ("MemberType").InnerText != "Property" ? "()" : ""; + StringBuilder args = new StringBuilder (); + args.Append ("("); + args.Append (XmlDocUtils.ToTypeName (parameters [0].Attributes ["Type"].Value, member)); + for (int i = 1; i < parameters.Count; ++i) { + args.Append (","); + args.Append (XmlDocUtils.ToTypeName (parameters [i].Attributes ["Type"].Value, member)); + } + args.Append (")"); + return args.ToString (); + } } // @@ -201,8 +319,8 @@ public class EcmaProvider : Provider { XmlNode ns_summary = nsSummaryFile.SelectSingleNode ("Namespace/Docs/summary"); if (ns_summary != null && ns_summary.InnerText.Trim () != "To be added." && ns_summary.InnerText != "") { - namespace_summaries [tn] = ns_summary; - namespace_remarks [tn] = nsSummaryFile.SelectSingleNode ("Namespace/Docs/remarks"); + namespace_summaries [tn] = detached.ImportNode (ns_summary, true); + namespace_remarks [tn] = detached.ImportNode (nsSummaryFile.SelectSingleNode ("Namespace/Docs/remarks"), true); } } else if (!namespace_summaries.ContainsKey (tn)) { @@ -222,7 +340,7 @@ public class EcmaProvider : Provider { } - struct TypeInfo : IComparable { + class TypeInfo : IComparable { public string type_assembly; public string type_name; public string type_full; @@ -325,6 +443,7 @@ public class EcmaProvider : Provider { } tree.HelpSource.PackXml ("mastersummary.xml", nsSummary, null); AddExtensionMethods (tree); + AddImageFiles (hs, tree); } void AddExtensionMethods (Tree tree) @@ -356,16 +475,33 @@ public class EcmaProvider : Provider { tree.HelpSource.PackXml ("ExtensionMethods.xml", extensions, "ExtensionMethods.xml"); } } - - Hashtable class_summaries = new Hashtable (); - Hashtable namespace_summaries = new Hashtable (); - Hashtable namespace_remarks = new Hashtable (); - Hashtable namespace_realpath = new Hashtable (); - XmlDocument doc; + + void AddImageFiles (HelpSource hs, Tree tree) + { + foreach (string asm in asm_dirs) { + string path = Path.Combine (asm, "_images"); + if (!Directory.Exists (path)) + return; + +#if NET_2_0 + foreach (var img in Directory.GetFiles (path)) +#else + foreach (var img in Directory.EnumerateFiles (path)) +#endif + hs.PackFile (img, Path.GetFileName (img)); + } + } + + Hashtable/*>*/ class_summaries = new Hashtable (); + Hashtable/**/ namespace_summaries = new Hashtable (); + Hashtable/**/ namespace_remarks = new Hashtable (); + Hashtable/**/ namespace_realpath = new Hashtable (); + + XmlDocument detached = new XmlDocument (); void PopulateClass (Tree tree, string ns, Node ns_node, string file) { - doc = new XmlDocument (); + XmlDocument doc = new XmlDocument (); doc.Load (file); string name = EcmaDoc.GetClassName (doc); @@ -376,7 +512,7 @@ public class EcmaProvider : Provider { Node class_node; string file_code = ns_node.tree.HelpSource.PackFile (file); - XmlNode class_summary = doc.SelectSingleNode ("/Type/Docs/summary"); + XmlNode class_summary = detached.ImportNode (doc.SelectSingleNode ("/Type/Docs/summary"), true); ArrayList l = (ArrayList) class_summaries [ns]; if (l == null){ l = new ArrayList (); @@ -402,12 +538,12 @@ public class EcmaProvider : Provider { // class_node.CreateNode ("Members", "*"); - PopulateMember (name, class_node, "Constructor", "Constructors"); - PopulateMember (name, class_node, "Method", "Methods"); - PopulateMember (name, class_node, "Property", "Properties"); - PopulateMember (name, class_node, "Field", "Fields"); - PopulateMember (name, class_node, "Event", "Events"); - PopulateMember (name, class_node, "Operator", "Operators"); + PopulateMember (doc, name, class_node, "Constructor", "Constructors"); + PopulateMember (doc, name, class_node, "Method", "Methods"); + PopulateMember (doc, name, class_node, "Property", "Properties"); + PopulateMember (doc, name, class_node, "Field", "Fields"); + PopulateMember (doc, name, class_node, "Event", "Events"); + PopulateMember (doc, name, class_node, "Operator", "Operators"); } class NodeIndex { @@ -441,7 +577,7 @@ public class EcmaProvider : Provider { // Performs an XPath query on the document to extract the nodes for the various members // we also use some extra text to pluralize the caption // - void PopulateMember (string typename, Node node, string type, string caption) + void PopulateMember (XmlDocument doc, string typename, Node node, string type, string caption) { string select = type; if (select == "Operator") select = "Method"; @@ -578,9 +714,80 @@ public class EcmaHelpSource : HelpSource { get {return js_code + base.InlineJavaScript;} } + public override string GetPublicUrl (string url) + { + if (url == null || url.Length == 0) + return url; + try { + string rest; + XmlDocument d = GetXmlFromUrl (url, out rest); + if (rest == "") + return EcmaDoc.GetCref (d.DocumentElement); + XmlElement e = GetDocElement (d, rest); + if (e == null) + return EcmaDoc.GetCref (d.DocumentElement) + "/" + rest; + return EcmaDoc.GetCref (e); + } + catch (Exception e) { + return url; + } + } + + private static XmlElement GetDocElement (XmlDocument d, string rest) + { + string memberType = null; + string memberIndex = null; + + string [] nodes = rest.Split (new char [] {'/'}); + + switch (nodes.Length) { + // e.g. C; not supported. + case 1: + return null; + // e.g. C/0 or M/MethodName; the latter isn't supported. + case 2: + try { + // XPath wants 1-based indexes, while the url uses 0-based values. + memberIndex = (int.Parse (nodes [1]) + 1).ToString (); + memberType = GetMemberType (nodes [0]); + } catch { + return null; + } + break; + // e.g. M/MethodName/0 + case 3: + memberIndex = (int.Parse (nodes [2]) + 1).ToString (); + memberType = GetMemberType (nodes [0]); + break; + // not supported + default: + return null; + } + string xpath = "/Type/Members/Member[MemberType=\"" + memberType + "\"]" + + "[position()=" + memberIndex + "]"; + return (XmlElement) d.SelectSingleNode (xpath); + } + + private static string GetMemberType (string type) + { + switch (type) { + case "C": return "Constructor"; + case "E": return "Event"; + case "F": return "Field"; + case "M": return "Method"; + case "P": return "Property"; + default: + throw new NotSupportedException ("Member Type: '" + type + "'."); + } + } + public override string GetText (string url, out Node match_node) { match_node = null; + + string cached = GetCachedText (url); + if (cached != null) + return cached; if (url == "root:") { @@ -591,7 +798,7 @@ public class EcmaHelpSource : HelpSource { XsltArgumentList args = new XsltArgumentList(); args.AddExtensionObject("monodoc:///extensions", ExtObject); args.AddParam("show", "", "masteroverview"); - string s = Htmlize(new XPathDocument (summary), args); + string s = Htmlize(summary, args); return BuildHtml (css_ecma_code, js_code, s); } @@ -638,7 +845,7 @@ public class EcmaHelpSource : HelpSource { if ((membername == "op_Implicit" || membername == "op_Explicit") && argtypes.Length == 2) { isoperator = true; - membername = "Conversion"; + membername = membername.EndsWith ("Implicit") ? "ImplicitConversion" : "ExplicitConversion"; member = argtypes[0] + " to " + argtypes[1]; } else if (membername.StartsWith("op_")) { isoperator = true; @@ -717,19 +924,57 @@ public class EcmaHelpSource : HelpSource { nicename = name.Substring(3); switch (name) { - // unary operators: no overloading possible - case "op_UnaryPlus": case "op_UnaryNegation": case "op_LogicalNot": case "op_OnesComplement": - case "op_Increment": case "op_Decrement": - case "op_True": case "op_False": + // unary operators: no overloading possible [ECMA-335 §10.3.1] + case "op_UnaryPlus": // static R operator+ (T) + case "op_UnaryNegation": // static R operator- (T) + case "op_LogicalNot": // static R operator! (T) + case "op_OnesComplement": // static R operator~ (T) + case "op_Increment": // static R operator++ (T) + case "op_Decrement": // static R operator-- (T) + case "op_True": // static bool operator true (T) + case "op_False": // static bool operator false (T) + case "op_AddressOf": // static R operator& (T) + case "op_PointerDereference": // static R operator* (T) sig = nicename; break; - // binary operators: overloading is possible based on parameter types - case "op_Addition": case "op_Subtraction": case "op_Multiply": case "op_Division": case "op_Modulus": - case "op_BitwiseAnd": case "op_BitwiseOr": case "op_ExclusiveOr": - case "op_LeftShift": case "op_RightShift": - case "op_Equality": case "op_Inequality": - case "op_GreaterThan": case "op_LessThan": case "op_GreaterThanOrEqual": case "op_LessThanOrEqual": + // binary operators: overloading is possible [ECMA-335 §10.3.2] + case "op_Addition": // static R operator+ (T1, T2) + case "op_Subtraction": // static R operator- (T1, T2) + case "op_Multiply": // static R operator* (T1, T2) + case "op_Division": // static R operator/ (T1, T2) + case "op_Modulus": // static R operator% (T1, T2) + case "op_ExclusiveOr": // static R operator^ (T1, T2) + case "op_BitwiseAnd": // static R operator& (T1, T2) + case "op_BitwiseOr": // static R operator| (T1, T2) + case "op_LogicalAnd": // static R operator&& (T1, T2) + case "op_LogicalOr": // static R operator|| (T1, T2) + case "op_Assign": // static R operator= (T1, T2) + case "op_LeftShift": // static R operator<< (T1, T2) + case "op_RightShift": // static R operator>> (T1, T2) + case "op_SignedRightShift": // static R operator>> (T1, T2) + case "op_UnsignedRightShift": // static R operator>>> (T1, T2) + case "op_Equality": // static bool operator== (T1, T2) + case "op_GreaterThan": // static bool operator> (T1, T2) + case "op_LessThan": // static bool operator< (T1, T2) + case "op_Inequality": // static bool operator!= (T1, T2) + case "op_GreaterThanOrEqual": // static bool operator>= (T1, T2) + case "op_LessThanOrEqual": // static bool operator<= (T1, T2) + case "op_UnsignedRightShiftAssignment": // static R operator>>>= (T1, T2) + case "op_MemberSelection": // static R operator-> (T1, T2) + case "op_RightShiftAssignment": // static R operator>>= (T1, T2) + case "op_MultiplicationAssignment": // static R operator*= (T1, T2) + case "op_PointerToMemberSelection": // static R operator->* (T1, T2) + case "op_SubtractionAssignment": // static R operator-= (T1, T2) + case "op_ExclusiveOrAssignment": // static R operator^= (T1, T2) + case "op_LeftShiftAssignment": // static R operator<<= (T1, T2) + case "op_ModulusAssignment": // static R operator%= (T1, T2) + case "op_AdditionAssignment": // static R operator+= (T1, T2) + case "op_BitwiseAndAssignment": // static R operator&= (T1, T2) + case "op_BitwiseOrAssignment": // static R operator|= (T1, T2) + case "op_Comma": // static R operator, (T1, T2) + case "op_DivisionAssignment": // static R operator/= (T1, T2) + default: // If all else fails, assume it can be overridden...whatever it is. XmlNodeList paramnodes = n.SelectNodes("Parameters/Parameter"); sig = nicename + "("; bool first = true; @@ -743,16 +988,14 @@ public class EcmaHelpSource : HelpSource { sig += ")"; break; - // overloading based on parameter and return type - case "op_Implicit": case "op_Explicit": - nicename = "Conversion"; + // conversion operators: overloading based on parameter and return type [ECMA-335 §10.3.3] + case "op_Implicit": // static implicit operator R (T) + case "op_Explicit": // static explicit operator R (T) + nicename = name.EndsWith ("Implicit") ? "ImplicitConversion" : "ExplicitConversion"; string arg = n.SelectSingleNode("Parameters/Parameter/@Type").InnerText; string ret = n.SelectSingleNode("ReturnValue/ReturnType").InnerText; sig = EcmaDoc.ConvertCTSName(arg) + " to " + EcmaDoc.ConvertCTSName(ret); break; - - default: - throw new InvalidOperationException(); } } @@ -951,7 +1194,7 @@ public class EcmaHelpSource : HelpSource { static string ToEscapedMemberName (string membername) { - return ToEscapedName (membername, "``"); + return ToEscapedName (membername, "`"); } public override string GetNodeXPath (XPathNavigator n) @@ -1012,7 +1255,8 @@ public class EcmaHelpSource : HelpSource { args.AddExtensionObject("monodoc:///extensions", ExtObject); args.AddParam("show", "", "namespace"); args.AddParam("namespace", "", ns_name); - string s = Htmlize(doc, args); + args.AddParam ("source-id", "", SourceID.ToString ()); + string s = Htmlize(new XmlNodeReader (doc), args); return BuildHtml (css_ecma_code, js_code, s); } @@ -1043,6 +1287,13 @@ public class EcmaHelpSource : HelpSource { string GetTextFromUrl (string url) { + if (nozip) { + string path = XmlDocUtils.GetCachedFileName (base_dir, url); + if (File.Exists (path)) + return File.OpenText (path).ReadToEnd (); + return null; + } + string rest, rest2; Node node; @@ -1104,18 +1355,14 @@ public class EcmaHelpSource : HelpSource { basetype = basetypedoc.SelectSingleNode("Type/Base/BaseTypeName"); } ArrayList extensions = new ArrayList (); + AddExtensionMethodsFromHelpSource (extensions, this); foreach (HelpSource hs in RootTree.HelpSources) { EcmaHelpSource es = hs as EcmaHelpSource; if (es == null) continue; - Stream s = es.GetHelpStream ("ExtensionMethods.xml"); - if (s != null) { - XmlDocument d = new XmlDocument (); - d.Load (s); - foreach (XmlNode n in d.SelectNodes ("/ExtensionMethods/*")) { - extensions.Add (n); - } - } + if (es == this) + continue; + AddExtensionMethodsFromHelpSource (extensions, es); } XmlDocUtils.AddExtensionMethods (doc, extensions, delegate (string s) { s = s.StartsWith ("T:") ? s : "T:" + s; @@ -1125,10 +1372,12 @@ public class EcmaHelpSource : HelpSource { XsltArgumentList args = new XsltArgumentList(); args.AddExtensionObject("monodoc:///extensions", ExtObject); + + args.AddParam ("source-id", "", SourceID.ToString ()); if (rest == "") { args.AddParam("show", "", "typeoverview"); - string s = Htmlize(doc, args); + string s = Htmlize(new XmlNodeReader (doc), args); return BuildHtml (css_ecma_code, js_code, s); } @@ -1188,32 +1437,48 @@ public class EcmaHelpSource : HelpSource { return "Unknown url: " + url; } - string html = Htmlize(doc, args); + string html = Htmlize(new XmlNodeReader (doc), args); return BuildHtml (css_ecma_code, js_code, html); } + void AddExtensionMethodsFromHelpSource (ArrayList extensions, EcmaHelpSource es) + { + Stream s = es.GetHelpStream ("ExtensionMethods.xml"); + if (s != null) { + XmlDocument d = new XmlDocument (); + d.Load (s); + foreach (XmlNode n in d.SelectNodes ("/ExtensionMethods/*")) { + extensions.Add (n); + } + } + } + public override void RenderPreviewDocs (XmlNode newNode, XmlWriter writer) { XsltArgumentList args = new XsltArgumentList (); args.AddExtensionObject ("monodoc:///extensions", ExtObject); + args.AddParam ("source-id", "", SourceID.ToString ()); - Htmlize (newNode, args, writer); + Htmlize (new XmlNodeReader (newNode), args, writer); } - static XslTransform ecma_transform; + static XslCompiledTransform ecma_transform; - public string Htmlize (IXPathNavigable ecma_xml) + public string Htmlize (XmlReader ecma_xml) { return Htmlize(ecma_xml, null); } - public string Htmlize (IXPathNavigable ecma_xml, XsltArgumentList args) + public string Htmlize (XmlReader ecma_xml, XsltArgumentList args) { EnsureTransform (); - StringWriter output = new StringWriter (); - ecma_transform.Transform (ecma_xml, args, output, CreateDocumentResolver ()); + var output = new StringBuilder (); + ecma_transform.Transform (ecma_xml, + args, + XmlWriter.Create (output, ecma_transform.OutputSettings), + CreateDocumentResolver ()); return output.ToString (); } @@ -1223,7 +1488,7 @@ public class EcmaHelpSource : HelpSource { return null; } - static void Htmlize (IXPathNavigable ecma_xml, XsltArgumentList args, XmlWriter w) + static void Htmlize (XmlReader ecma_xml, XsltArgumentList args, XmlWriter w) { EnsureTransform (); @@ -1233,22 +1498,22 @@ public class EcmaHelpSource : HelpSource { ecma_transform.Transform (ecma_xml, args, w, null); } - static XslTransform ecma_transform_css, ecma_transform_no_css; + static XslCompiledTransform ecma_transform_css, ecma_transform_no_css; static void EnsureTransform () { if (ecma_transform == null) { - ecma_transform_css = new XslTransform (); - ecma_transform_no_css = new XslTransform (); + ecma_transform_css = new XslCompiledTransform (); + ecma_transform_no_css = new XslCompiledTransform (); Assembly assembly = System.Reflection.Assembly.GetCallingAssembly (); Stream stream = assembly.GetManifestResourceStream ("mono-ecma-css.xsl"); XmlReader xml_reader = new XmlTextReader (stream); XmlResolver r = new ManifestResourceResolver ("."); - ecma_transform_css.Load (xml_reader, r, null); + ecma_transform_css.Load (xml_reader, XsltSettings.TrustedXslt, r); stream = assembly.GetManifestResourceStream ("mono-ecma.xsl"); xml_reader = new XmlTextReader (stream); - ecma_transform_no_css.Load (xml_reader, r, null); + ecma_transform_no_css.Load (xml_reader, XsltSettings.TrustedXslt, r); } if (use_css) ecma_transform = ecma_transform_css; @@ -1367,7 +1632,7 @@ public class EcmaHelpSource : HelpSource { return name.Replace("+", "."); } - public string MonoImpInfo(string assemblyname, string typename, string membername, string arglist, bool strlong) + string MonoImpInfo(string assemblyname, string typename, string membername, string arglist, bool strlong) { if (quiet) return ""; @@ -1377,7 +1642,7 @@ public class EcmaHelpSource : HelpSource { return MonoImpInfo(assemblyname, typename, membername, a, strlong); } - public string MonoImpInfo(string assemblyname, string typename, string membername, XPathNodeIterator itr, bool strlong) + string MonoImpInfo(string assemblyname, string typename, string membername, XPathNodeIterator itr, bool strlong) { if (quiet) return ""; @@ -1389,7 +1654,7 @@ public class EcmaHelpSource : HelpSource { return MonoImpInfo (assemblyname, typename, membername, rgs, strlong); } - public string MonoImpInfo(string assemblyname, string typename, string membername, ArrayList arglist, bool strlong) + string MonoImpInfo(string assemblyname, string typename, string membername, ArrayList arglist, bool strlong) { try { Assembly assembly = null; @@ -1452,7 +1717,7 @@ public class EcmaHelpSource : HelpSource { } } - public string MonoImpInfo(System.Reflection.MemberInfo mi, string itemtype, bool strlong) + string MonoImpInfo(System.Reflection.MemberInfo mi, string itemtype, bool strlong) { if (quiet) return ""; @@ -1617,9 +1882,19 @@ public class EcmaHelpSource : HelpSource { return node.URL[idx+1] + ":" + full + "." + node.Caption; } } + + public override Stream GetImage (string url) + { + if (url.Contains ("/")) + url = url.Substring (url.LastIndexOf ('/') + 1); + return GetHelpStream (url); + } // - // Populates the index. + // Populates the searchable index. + // + // The idea of this index is to capture the most common search terms, the UI for this + // usually updates automatically as the user types. // public override void PopulateIndex (IndexMaker index_maker) { @@ -1630,7 +1905,47 @@ public class EcmaHelpSource : HelpSource { string doc_tag = GetKindFromCaption (type_node.Caption); string url = "T:" + full; - + + + // + // Add MonoMac/MonoTouch [Export] attributes, those live only in classes + // + if (doc_tag == "Class" && (ns_node.Caption.StartsWith ("MonoTouch") || ns_node.Caption.StartsWith ("MonoMac"))){ + try { + string rest; + var xdoc = GetXmlFromUrl (type_node.URL, out rest); + if (xdoc != null){ + var nodesWithExports = xdoc.SelectNodes ("/Type/Members/Member[contains (Attributes/Attribute/AttributeName, 'Foundation.Export') and (MemberType='Property' or MemberType='Method' or MemberType='Constructor')]"); + + foreach (XmlNode n in nodesWithExports){ + string cref = EcmaDoc.GetCref ((XmlElement) n); + + var exports = n.SelectNodes ("Attributes/Attribute/AttributeName"); + foreach (XmlNode exportNode in exports){ + var inner = exportNode.InnerText; + int p = inner.IndexOf ("Foundation.Export(\""); + if (p == -1){ + Console.WriteLine ("Not found the Export attribute in {0}", inner); + continue; + } + var pa = inner.IndexOf ("\"", p); + if (pa == -1){ + Console.WriteLine ("Export has no target in {0}", inner); + continue; + } + var end = inner.IndexOf ("\"", pa+1); + + var export = end == -1 ? inner.Substring (pa+1) : inner.Substring (pa+1, end-(pa+1)); + + index_maker.Add (export + " selector", export, cref); + } + } + } + } catch (Exception e){ + Console.WriteLine ("Problem processing {0} for MonoTouch/MonoMac exports\n\n{0}", e); + } + } + if (doc_tag == "Class" || doc_tag == "Structure" || doc_tag == "Interface"){ index_maker.Add (type_node.Caption, typename, url); @@ -1752,6 +2067,63 @@ public class EcmaHelpSource : HelpSource { } } } + + IEnumerable ExtractArguments (string rawArgList) + { + var sb = new System.Text.StringBuilder (); + int genericDepth = 0; + int arrayDepth = 0; + + for (int i = 0; i < rawArgList.Length; i++) { + char c = rawArgList[i]; + + switch (c) { + case ',': + if (genericDepth == 0 && arrayDepth == 0) { + yield return sb.ToString (); + sb.Clear (); + continue; + } + break; + case '<': + genericDepth++; + break; + case '>': + genericDepth--; + break; + case '[': + arrayDepth++; + break; + case ']': + arrayDepth--; + break; + } + sb.Append (c); + } + if (sb.Length > 0) + yield return sb.ToString (); + } + + // Caption is what you see on a tree node, either SomeName or SomeName(ArgList) + void TryCreateXPathPredicateFragment (string caption, out string name, out string argListPredicate) + { + name = argListPredicate = null; + int parenIdx = caption.IndexOf ('('); + // In case of simple name, there is no need for processing + if (parenIdx == -1) { + name = caption; + return; + } + name = caption.Substring (0, parenIdx); + // Now we create a xpath predicate which will check for all the args in the argsList + var rawArgList = caption.Substring (parenIdx + 1, caption.Length - parenIdx - 2); // Only take what's inside the parens + if (string.IsNullOrEmpty (rawArgList)) + return; + + var argList = ExtractArguments (rawArgList).Select (arg => arg.Trim ()).Select (type => EcmaDoc.ConvertFromCTSName (type)); + argListPredicate = "and " + argList.Select (type => string.Format ("Parameters/Parameter[@Type='{0}']", type)).Aggregate ((e1, e2) => e1 + " and " + e2); + } + // // Create list of documents for searching // @@ -1781,6 +2153,7 @@ public class EcmaHelpSource : HelpSource { doc.title = type_node.Caption; doc.hottext = typename; doc.url = url; + doc.fulltitle = full; XmlNode node_sel = xdoc.SelectSingleNode ("/Type/Docs"); text = new StringBuilder (); @@ -1792,22 +2165,57 @@ public class EcmaHelpSource : HelpSource { doc.examples = text.ToString (); writer.AddDocument (doc.LuceneDoc); + var exportParsable = doc_tag == "Class" && (ns_node.Caption.StartsWith ("MonoTouch") || ns_node.Caption.StartsWith ("MonoMac")); //Add docs for contructors, methods, etc. foreach (Node c in type_node.Nodes) { // c = Constructors || Fields || Events || Properties || Methods || Operators if (c.Element == "*") continue; - int i = 1; - foreach (Node nc in c.Nodes) { + const float innerTypeBoost = 0.2f; + + var ncnodes = c.Nodes.Cast (); + // The rationale is that we need to properly handle method overloads + // so for those method node which have children, flatten them + if (c.Caption == "Methods") { + ncnodes = ncnodes + .Where (n => n.Nodes == null || n.Nodes.Count == 0) + .Concat (ncnodes.Where (n => n.Nodes.Count > 0).SelectMany (n => n.Nodes.Cast ())); + } else if (c.Caption == "Operators") { + ncnodes = ncnodes + .Where (n => !n.Caption.EndsWith ("Conversion")) + .Concat (ncnodes.Where (n => n.Caption.EndsWith ("Conversion")).SelectMany (n => n.Nodes.Cast ())); + } + foreach (Node nc in ncnodes) { //xpath to the docs xml node string xpath; - if (c.Caption == "Constructors") - xpath = String.Format ("/Type/Members/Member[{0}]/Docs", i++); - else if (c.Caption == "Operators") - xpath = String.Format ("/Type/Members/Member[@MemberName='op_{0}']/Docs", nc.Caption); - else + string name, argListPredicate; + + switch (c.Caption) { + case "Constructors": + TryCreateXPathPredicateFragment (nc.Caption, out name, out argListPredicate); + xpath = String.Format ("/Type/Members/Member[@MemberName='.ctor'{0}]/Docs", argListPredicate ?? string.Empty); + break; + case "Operators": + // The first case are explicit and implicit conversion operators which are grouped specifically + if (nc.Caption.IndexOf (" to ") != -1) { + var convArgs = nc.Caption.Split (new[] { " to " }, StringSplitOptions.None); + xpath = String.Format ("/Type/Members/Member[(@MemberName='op_Explicit' or @MemberName='op_Implicit')" + + " and ReturnValue/ReturnType='{0}'" + + " and Parameters/Parameter[@Type='{1}']]/Docs", + EcmaDoc.ConvertFromCTSName (convArgs[1]), EcmaDoc.ConvertFromCTSName (convArgs[0])); + } else { + xpath = String.Format ("/Type/Members/Member[@MemberName='op_{0}']/Docs", nc.Caption); + } + break; + case "Methods": + TryCreateXPathPredicateFragment (nc.Caption, out name, out argListPredicate); + xpath = String.Format ("/Type/Members/Member[@MemberName='{0}'{1}]/Docs", name, argListPredicate ?? string.Empty); + break; + default: xpath = String.Format ("/Type/Members/Member[@MemberName='{0}']/Docs", nc.Caption); + break; + } //construct url of the form M:Array.Sort string urlnc; if (c.Caption == "Constructors") @@ -1818,12 +2226,38 @@ public class EcmaHelpSource : HelpSource { //create the doc SearchableDocument doc_nod = new SearchableDocument (); doc_nod.title = LargeName (nc); - //dont add the parameters to the hottext - int ppos = nc.Caption.IndexOf ('('); - if (ppos != -1) - doc_nod.hottext = nc.Caption.Substring (0, ppos); - else - doc_nod.hottext = nc.Caption; + switch (c.Caption[0]) { + case 'M': + doc_nod.title += " Method"; + break; + case 'P': + doc_nod.title += " Property"; + break; + case 'E': + doc_nod.title += " Event"; + break; + case 'O': + doc_nod.title += " Operator"; + break; + case 'C': + doc_nod.title += " Constructor"; + break; + default: + break; + } + doc_nod.fulltitle = string.Format ("{0}.{1}::{2}", ns_node.Caption, typename, nc.Caption); + // Disable constructors hottext indexing as it's often "polluting" search queries + // because it has the same hottext than standard types + if (c.Caption != "Constructors") { + //dont add the parameters to the hottext + int ppos = nc.Caption.IndexOf ('('); + if (ppos != -1) + doc_nod.hottext = nc.Caption.Substring (0, ppos); + else + doc_nod.hottext = nc.Caption; + } else { + doc_nod.hottext = string.Empty; + } doc_nod.url = urlnc; @@ -1837,11 +2271,43 @@ public class EcmaHelpSource : HelpSource { GetTextFromNode (xmln, text); doc_nod.text = text.ToString (); - text = new StringBuilder (); + text.Clear (); GetExamples (xmln, text); doc_nod.examples = text.ToString (); - writer.AddDocument (doc_nod.LuceneDoc); + Document lucene_doc = doc_nod.LuceneDoc; + lucene_doc.SetBoost (innerTypeBoost); + writer.AddDocument (lucene_doc); + + // MonoTouch/Monomac specific parsing of [Export] attributes + if (exportParsable) { + try { + var exports = + xdoc.SelectNodes (string.Format ("/Type/Members/Member[@MemberName='{0}']/Attributes/Attribute/AttributeName[contains(text(), 'Foundation.Export')]", nc.Caption)); + foreach (XmlNode exportNode in exports) { + var inner = exportNode.InnerText; + var parts = inner.Split ('"'); + if (parts.Length != 3) { + Console.WriteLine ("Export attribute not found or not usable in {0}", inner); + continue; + } + + var export = parts[1]; + var export_node = new SearchableDocument (); + export_node.title = export + " Export"; + export_node.fulltitle = string.Format ("{0}.{1}::{2}", ns_node.Caption, typename, export); + export_node.url = urlnc; + export_node.hottext = export + ":"; + export_node.text = string.Empty; + export_node.examples = string.Empty; + lucene_doc = export_node.LuceneDoc; + lucene_doc.SetBoost (innerTypeBoost); + writer.AddDocument (lucene_doc); + } + } catch (Exception e){ + Console.WriteLine ("Problem processing {0} for MonoTouch/MonoMac exports\n\n{0}", e); + } + } } } // @@ -1869,6 +2335,7 @@ public class EcmaHelpSource : HelpSource { doc.title = type_node.Caption; doc.hottext = xdoc.DocumentElement.Attributes["Name"].Value; + doc.fulltitle = full; doc.url = url; doc.text = text.ToString(); writer.AddDocument (doc.LuceneDoc); @@ -1879,6 +2346,7 @@ public class EcmaHelpSource : HelpSource { SearchableDocument doc = new SearchableDocument (); doc.title = type_node.Caption; doc.hottext = xdoc.DocumentElement.Attributes["Name"].Value; + doc.fulltitle = full; doc.url = url; XmlNode node_sel = xdoc.SelectSingleNode ("/Type/Docs"); @@ -1892,7 +2360,7 @@ public class EcmaHelpSource : HelpSource { doc.examples = text.ToString(); writer.AddDocument (doc.LuceneDoc); - } + } } } } @@ -2102,7 +2570,7 @@ public class EcmaUncompiledHelpSource : EcmaHelpSource { XsltArgumentList args = new XsltArgumentList(); args.AddExtensionObject("monodoc:///extensions", ExtObject); args.AddParam("show", "", "masteroverview"); - string s = Htmlize(new XPathDocument (reader), args); + string s = Htmlize(reader, args); return BuildHtml (css_ecma_code, js_code, s); } return base.GetText(url, out match_node); @@ -2160,6 +2628,14 @@ public class EcmaUncompiledHelpSource : EcmaHelpSource { doc.Load (id); return doc; } + + public virtual Stream GetImage (string url) + { + string path = EcmaDoc.GetImageFile (basedir.FullName, url); + if (path == null) + return null; + return File.OpenRead (path); + } class UncompiledResolver : XmlResolver { public override Uri ResolveUri (Uri baseUri, string relativeUri)