Merge pull request #601 from knocte/sock_improvements
[mono.git] / mcs / class / monodoc / Monodoc / providers / EcmaDoc.cs
index 62dab9e0b8a0f1f263e333fc9e014d27c57d1617..e3371d141d0e46ab9a591ae2b590cc9532fdf0b9 100644 (file)
@@ -202,9 +202,9 @@ namespace Monodoc.Providers
                        // Namespace search
                        Node currentNode = tree.RootNode;
                        Node searchNode = new Node () { Caption = desc.Namespace };
-                       int index = currentNode.Nodes.BinarySearch (searchNode, EcmaGenericNodeComparer.Instance);
+                       int index = currentNode.ChildNodes.BinarySearch (searchNode, EcmaGenericNodeComparer.Instance);
                        if (index >= 0)
-                               result = currentNode.Nodes[index];
+                               result = currentNode.ChildNodes[index];
                        if (desc.DescKind == EcmaDesc.Kind.Namespace || index < 0)
                                return result;
 
@@ -212,9 +212,12 @@ namespace Monodoc.Providers
                        currentNode = result;
                        result = null;
                        searchNode.Caption = desc.ToCompleteTypeName ();
-                       index = currentNode.Nodes.BinarySearch (searchNode, EcmaTypeNodeComparer.Instance);
+                       if (!desc.GenericTypeArgumentsIsNumeric)
+                               index = currentNode.ChildNodes.BinarySearch (searchNode, EcmaTypeNodeComparer.Instance);
+                       else
+                               index = GenericTypeBacktickSearch (currentNode.ChildNodes, desc);
                        if (index >= 0)
-                               result = currentNode.Nodes[index];
+                               result = currentNode.ChildNodes[index];
                        if ((desc.DescKind == EcmaDesc.Kind.Type && !desc.IsEtc) || index < 0)
                                return result;
 
@@ -222,7 +225,7 @@ namespace Monodoc.Providers
                        currentNode = result;
                        result = null;
                        var caption = desc.IsEtc ? EtcKindToCaption (desc.Etc) : MemberKindToCaption (desc.DescKind);
-                       currentNode = FindNodeForCaption (currentNode.Nodes, caption);
+                       currentNode = FindNodeForCaption (currentNode.ChildNodes, caption);
                        if (currentNode == null
                            || (desc.IsEtc && desc.DescKind == EcmaDesc.Kind.Type && string.IsNullOrEmpty (desc.EtcFilter)))
                                return currentNode;
@@ -231,24 +234,66 @@ namespace Monodoc.Providers
                        result = null;
                        var format = desc.DescKind == EcmaDesc.Kind.Constructor ? EcmaDesc.Format.WithArgs : EcmaDesc.Format.WithoutArgs;
                        searchNode.Caption = desc.ToCompleteMemberName (format);
-                       index = currentNode.Nodes.BinarySearch (searchNode, EcmaGenericNodeComparer.Instance);
+                       index = currentNode.ChildNodes.BinarySearch (searchNode, EcmaGenericNodeComparer.Instance);
                        if (index < 0)
                                return null;
-                       result = currentNode.Nodes[index];
-                       if (result.Nodes.Count == 0 || desc.IsEtc)
+                       result = currentNode.ChildNodes[index];
+                       if (result.ChildNodes.Count == 0 || desc.IsEtc)
                                return result;
 
                        // Overloads search
                        currentNode = result;
                        searchNode.Caption = desc.ToCompleteMemberName (EcmaDesc.Format.WithArgs);
-                       index = currentNode.Nodes.BinarySearch (searchNode, EcmaGenericNodeComparer.Instance);
+                       index = currentNode.ChildNodes.BinarySearch (searchNode, EcmaGenericNodeComparer.Instance);
                        if (index < 0)
                                return result;
-                       result = result.Nodes[index];
+                       result = result.ChildNodes[index];
 
                        return result;
                }
 
+               static int GenericTypeBacktickSearch (IList<Node> childNodes, EcmaDesc desc)
+               {
+                       /* Our strategy is to search for the non-generic variant of the type
+                        * (which in most case should fail) and then use the closest index
+                        * to linearily search for the generic variant with the right generic arg number
+                        */
+                       var searchNode = new Node () { Caption = desc.TypeName };
+                       int index = childNodes.BinarySearch (searchNode, EcmaTypeNodeComparer.Instance);
+                       // Place the index in the right start position
+                       if (index < 0)
+                               index = ~index;
+
+                       for (int i = index; i < childNodes.Count; i++) {
+                               var currentNode = childNodes[i];
+                               // Find the index of the generic argument list
+                               int genericIndex = currentNode.Caption.IndexOf ('<');
+                               // If we are not on the same base type name anymore, there is no point
+                               int captionSlice = genericIndex != -1 ? genericIndex : currentNode.Caption.LastIndexOf (' ');
+                               if (string.Compare (searchNode.Caption, 0,
+                                                   currentNode.Caption, 0,
+                                                   Math.Max (captionSlice, searchNode.Caption.Length),
+                                                   StringComparison.Ordinal) != 0)
+                                       break;
+
+                               var numGenerics = CountTypeGenericArguments (currentNode.Caption, genericIndex);
+                               if (numGenerics == desc.GenericTypeArguments.Count) {
+                                       // Simple comparison if we are not looking for an inner type
+                                       if (desc.NestedType == null)
+                                               return i;
+                                       // If more complicated, we fallback to using EcmaUrlParser
+                                       var caption = currentNode.Caption;
+                                       caption = "T:" + caption.Substring (0, caption.LastIndexOf (' ')).Replace ('.', '+');
+                                       EcmaDesc otherDesc;
+                                       var parser = new EcmaUrlParser ();
+                                       if (parser.TryParse (caption, out otherDesc) && desc.NestedType.Equals (otherDesc.NestedType))
+                                               return i;
+                               }
+                       }
+
+                       return -1;
+               }
+
                // This comparer returns the answer straight from caption comparison
                class EcmaGenericNodeComparer : IComparer<Node>
                {
@@ -428,7 +473,7 @@ namespace Monodoc.Providers
                        }
                }
 
-               public static Node FindNodeForCaption (List<Node> nodes, string caption)
+               public static Node FindNodeForCaption (IList<Node> nodes, string caption)
                {
                        foreach (var node in nodes)
                                if (node.Caption.Equals (caption, StringComparison.OrdinalIgnoreCase))
@@ -436,6 +481,33 @@ namespace Monodoc.Providers
                        return null;
                }
 
+               public static int CountTypeGenericArguments (string typeDefinition, int startIndex = 0)
+               {
+                       int nestedLevel = 0;
+                       int count = 0;
+                       bool started = false;
+
+                       foreach (char c in typeDefinition.Skip (startIndex)) {
+                               switch (c) {
+                               case '<':
+                                       if (!started)
+                                               count = 1;
+                                       started = true;
+                                       nestedLevel++;
+                                       break;
+                               case ',':
+                                       if (started && nestedLevel == 1)
+                                               count++;
+                                       break;
+                               case '>':
+                                       nestedLevel--;
+                                       break;
+                               }
+                       }
+
+                       return count;
+               }
+
                internal static string MakeOperatorSignature (XElement member, out string memberSignature)
                {
                        string name = (string)member.Attribute ("MemberName");