Merge pull request #409 from Alkarex/patch-1
[mono.git] / mcs / tools / monodoc / Monodoc / ecma-provider.cs
index 62e81f9ca55b2c12224b4699cffbc9e01b1e650a..e0e52691b17fc19ae98151c65717519b391b951a 100644 (file)
@@ -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
 //
 namespace Monodoc {
 using System;
+using System.Diagnostics;
 using System.Reflection;
 using System.IO;
 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;
 
@@ -133,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");
@@ -140,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 ();
+       }
 }
 
 //
@@ -187,7 +306,7 @@ public class EcmaProvider : Provider {
 
                                if (ns_node == null) {
                                        tn = Path.GetFileName (ns);
-                                       Console.Error.WriteLine ("Processing namespace {0}", tn);
+                                       tree.HelpSource.Message (TraceLevel.Info, "Processing namespace {0}", tn);
                                        ns_node = tree.LookupNode (tn, "N:" + tn);
                                        string ns_summary_file = EcmaDoc.GetNamespaceFile (basedir, tn);
                                        
@@ -200,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)) {
@@ -209,9 +328,9 @@ public class EcmaProvider : Provider {
                                                namespace_remarks [tn] = null;
                                        }
                                }
-                               Console.Error.WriteLine ("    Processing input file {0}", Path.GetFileName (file));
+                               tree.HelpSource.Message (TraceLevel.Verbose, "    Processing input file {0}", Path.GetFileName (file));
 
-                               PopulateClass (tn, ns_node, file);
+                               PopulateClass (tree, tn, ns_node, file);
                        }
                        
                        // Sort the list of types in each namespace
@@ -221,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;
@@ -271,7 +390,7 @@ public class EcmaProvider : Provider {
                        else
                                elements.AppendChild (doc.CreateElement("remarks"));
                        
-                       Console.Error.WriteLine ("Have {0} elements in the {1}", list.Count, ns);
+                       hs.Message (TraceLevel.Info, "Have {0} elements in the {1}", list.Count, ns);
                        foreach (TypeInfo p in list){
                                XmlElement e = null;
                                
@@ -324,6 +443,7 @@ public class EcmaProvider : Provider {
                }
                tree.HelpSource.PackXml ("mastersummary.xml", nsSummary, null);
                AddExtensionMethods (tree);
+               AddImageFiles (hs, tree);
        }
 
        void AddExtensionMethods (Tree tree)
@@ -351,20 +471,37 @@ public class EcmaProvider : Provider {
                        }
                }
                if (extensions != null) {
-                       Console.Error.WriteLine ("Have {0} extension methods", numMethods);
+                       tree.HelpSource.Message (TraceLevel.Info, "Have {0} extension methods", numMethods);
                        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/*<string, List<TypeInfo>>*/ class_summaries = new Hashtable ();
+       Hashtable/*<string, XmlNode>*/ namespace_summaries = new Hashtable ();
+       Hashtable/*<string, XmlNode>*/ namespace_remarks = new Hashtable ();
+       Hashtable/*<string, string -- path>*/ namespace_realpath = new Hashtable ();
+
+       XmlDocument detached = new XmlDocument ();
        
-       void PopulateClass (string ns, Node ns_node, string file)
+       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);
@@ -375,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 ();
@@ -387,7 +524,7 @@ public class EcmaProvider : Provider {
                
                if (kind == "Delegate") {
                        if (doc.SelectSingleNode("/Type/ReturnValue") == null)
-                               Console.Error.WriteLine("Delegate " + name + " does not have a ReturnValue node.  See the ECMA-style updates.");
+                               tree.HelpSource.Message (TraceLevel.Error, "Delegate " + name + " does not have a ReturnValue node.  See the ECMA-style updates.");
                }
 
                if (kind == "Enumeration")
@@ -401,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 {
@@ -440,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";
@@ -552,6 +689,11 @@ public class EcmaHelpSource : HelpSource {
                        return css_ecma;
                }
        }
+
+       public override string InlineCss {
+               get {return base.InlineCss + css_ecma_code;}
+       }
+
        static string js;
        public static string js_code {
                get {
@@ -559,7 +701,7 @@ public class EcmaHelpSource : HelpSource {
                                return js;
                        if (use_css) {
                                System.Reflection.Assembly assembly = typeof(EcmaHelpSource).Assembly;
-                               Stream str_js = assembly.GetManifestResourceStream ("mono-ecma-css.js");
+                               Stream str_js = assembly.GetManifestResourceStream ("helper.js");
                                js = (new StreamReader (str_js)).ReadToEnd();
                        } else {
                                js = String.Empty;
@@ -568,9 +710,84 @@ public class EcmaHelpSource : HelpSource {
                }
        }
 
+       public override string InlineJavaScript {
+               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:")
                {
@@ -581,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); 
                }
                
@@ -628,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;
@@ -707,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;
@@ -733,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();
                }       
        }
 
@@ -941,7 +1194,7 @@ public class EcmaHelpSource : HelpSource {
 
        static string ToEscapedMemberName (string membername)
        {
-               return ToEscapedName (membername, "``");
+               return ToEscapedName (membername, "`");
        }
        
        public override string GetNodeXPath (XPathNavigator n)
@@ -976,12 +1229,13 @@ public class EcmaHelpSource : HelpSource {
                        }
                }
                
-               Console.WriteLine ("WARNING: Was not able to get clean XPath expression for node {0}", EditingUtils.GetXPath (n));
+               Message (TraceLevel.Warning, "WARNING: Was not able to get clean XPath expression for node {0}", EditingUtils.GetXPath (n));
                return base.GetNodeXPath (n);
        }
 
-       protected virtual XmlReader GetNamespaceDocument (string ns) {
-               return GetHelpXml ("xml.summary." + ns);
+       protected virtual XmlDocument GetNamespaceDocument (string ns)
+       {
+               return GetHelpXmlWithChanges ("xml.summary." + ns);
        }
 
        public override string RenderNamespaceLookup (string nsurl, out Node match_node)
@@ -993,7 +1247,7 @@ public class EcmaHelpSource : HelpSource {
                        match_node = ns_node;
                        string ns_name = nsurl.Substring (2);
                        
-                       XmlDocument doc = GetHelpXmlWithChanges("xml.summary." + ns_name);
+                       XmlDocument doc = GetNamespaceDocument (ns_name);
                        if (doc == null)
                                return null;
 
@@ -1001,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); 
 
                }
@@ -1032,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;
 
@@ -1093,30 +1355,29 @@ 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) {
-                               return RootTree.GetHelpXml ("T:" + s);
+                               s = s.StartsWith ("T:") ? s : "T:" + s;
+                               return RootTree.GetHelpXml (s);
                });
 
                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); 
                }
                
@@ -1176,36 +1437,58 @@ 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 static string Htmlize (IXPathNavigable ecma_xml)
+       public string Htmlize (XmlReader ecma_xml)
        {
                return Htmlize(ecma_xml, null);
        }
-       
-       public static 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, null);
+               var output = new StringBuilder ();
+               ecma_transform.Transform (ecma_xml, 
+                               args, 
+                               XmlWriter.Create (output, ecma_transform.OutputSettings),
+                               CreateDocumentResolver ());
                return output.ToString ();
        }
+
+       protected virtual XmlResolver CreateDocumentResolver ()
+       {
+               // results in using XmlUrlResolver
+               return null;
+       }
        
-       static void Htmlize (IXPathNavigable ecma_xml, XsltArgumentList args, XmlWriter w)
+       static void Htmlize (XmlReader ecma_xml, XsltArgumentList args, XmlWriter w)
        {
                EnsureTransform ();
                
@@ -1215,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;
@@ -1349,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 "";
@@ -1359,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 "";
@@ -1371,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;
@@ -1434,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 "";
@@ -1534,7 +1817,7 @@ public class EcmaHelpSource : HelpSource {
        {
                Stream s = hs.GetHelpStream (fname);
                if (s == null){
-                       Console.Error.WriteLine ("Could not fetch document {0}", fname);
+                       Error ("Could not fetch document {0}", fname);
                        return null;
                }
                
@@ -1599,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)
        {
@@ -1612,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);
@@ -1734,6 +2067,63 @@ public class EcmaHelpSource : HelpSource {
                        }
                }
        }
+
+       IEnumerable<string> 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
        //
@@ -1741,7 +2131,7 @@ public class EcmaHelpSource : HelpSource {
        {
                StringBuilder text;
                foreach (Node ns_node in Tree.Nodes) {
-                       Console.WriteLine ("\tNamespace: {0} ({1})", ns_node.Caption, ns_node.Nodes.Count);
+                       Message (TraceLevel.Info, "\tNamespace: {0} ({1})", ns_node.Caption, ns_node.Nodes.Count);
                        foreach (Node type_node in ns_node.Nodes) {
                                string typename = type_node.Caption.Substring (0, type_node.Caption.IndexOf (' '));
                                string full = ns_node.Caption + "." + typename;
@@ -1763,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 ();
@@ -1774,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<Node> ();
+                                               // 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<Node> ()));
+                                               } 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<Node> ()));
+                                               }
+                                               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")
@@ -1800,18 +2226,44 @@ 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;
 
                                                        XmlNode xmln = xdoc.SelectSingleNode (xpath);
                                                        if (xmln == null) {
-                                                               Console.WriteLine ("Problem: {0}, with xpath: {1}", urlnc, xpath);
+                                                               Error ("Problem: {0}, with xpath: {1}", urlnc, xpath);
                                                                continue;
                                                        }
 
@@ -1819,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);
+                                                               }
+                                                       }
                                                }
                                        }
                                //
@@ -1851,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);
@@ -1861,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");
@@ -1874,7 +2360,7 @@ public class EcmaHelpSource : HelpSource {
                                        doc.examples = text.ToString();
 
                                        writer.AddDocument (doc.LuceneDoc);
-                               } 
+                               }
                        }
                }
        }
@@ -1939,11 +2425,12 @@ public class EcmaUncompiledHelpSource : EcmaHelpSource {
        readonly DirectoryInfo basedir;
        readonly XmlDocument basedoc;
        
-       public new readonly string Name, BasePath;
+       public new readonly string Name;
+       public     readonly string BasePath;
        
        public EcmaUncompiledHelpSource (string base_file) : base ()
        {
-               Console.Error.WriteLine("Loading uncompiled help from " + base_file);
+               Message (TraceLevel.Info, "Loading uncompiled help from " + base_file);
                
                basedir = new DirectoryInfo(base_file);
                BasePath = basedir.FullName;
@@ -2043,8 +2530,7 @@ public class EcmaUncompiledHelpSource : EcmaHelpSource {
                
                string ns, type;
                if (!RootTree.GetNamespaceAndType (url, out ns, out type)) {
-                       Console.Error.WriteLine ("Could not determine namespace/type for {0}",
-                                       url);
+                       Message (TraceLevel.Error, "Could not determine namespace/type for {0}", url);
                        return null;
                }
                
@@ -2084,18 +2570,20 @@ public class EcmaUncompiledHelpSource : EcmaHelpSource {
                        XsltArgumentList args = new XsltArgumentList();
                        args.AddExtensionObject("monodoc:///extensions", ExtObject);
                        args.AddParam("show", "", "masteroverview");
-                       string s = EcmaHelpSource.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);
        }
        
-       protected override XmlReader GetNamespaceDocument (string ns) {
+       protected override XmlDocument GetNamespaceDocument (string ns)
+       {
                XmlDocument nsdoc = new XmlDocument();
                nsdoc.Load (EcmaDoc.GetNamespaceFile (basedir.FullName, ns));
                
                XmlDocument elements = new XmlDocument();
                XmlElement docnode = elements.CreateElement("elements");
+               elements.AppendChild (docnode);
                
                foreach (XmlElement doc in nsdoc.SelectNodes("Namespace/Docs/*")) {
                        docnode.AppendChild(elements.ImportNode(doc, true));
@@ -2123,9 +2611,52 @@ public class EcmaUncompiledHelpSource : EcmaHelpSource {
                        docnode.AppendChild(typenode);
                }
 
-               return new XmlNodeReader(docnode);
+               return elements;
        }
        
+       public override Stream GetHelpStream (string id)
+       {
+               if (id == "ExtensionMethods.xml") {
+                       // TODO: generate ExtensionMethods.xml based on index.xml contents.
+               }
+               return null;
+       }
+
+       public override XmlDocument GetHelpXmlWithChanges (string id)
+       {
+               XmlDocument doc = new XmlDocument ();
+               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)
+               {
+                       return null;
+               }
+
+               public override object GetEntity (Uri absoluteUri, string role, Type ofObjectToReturn)
+               {
+                       return null;
+               }
+
+               public override System.Net.ICredentials Credentials {
+                       set {/* ignore */}
+               }
+       }
+
+       protected override XmlResolver CreateDocumentResolver ()
+       {
+               return new UncompiledResolver ();
+       }
 }
 
 }