[mono-api-html] Make it possible to hide/show non-breaking changes in the html output.
authorRolf Bjarne Kvinge <rolf@xamarin.com>
Thu, 28 Jan 2016 14:14:13 +0000 (15:14 +0100)
committerRolf Bjarne Kvinge <rolf@xamarin.com>
Thu, 28 Jan 2016 14:32:56 +0000 (15:32 +0100)
We now add data-is-[non-]breaking attributes to the generated
html elements, and then use some javascript logic to show/hide
non-breaking elements (and the javascript also looks in the
container elements show/hide containers that have only
non-breaking descendants).

Also use css classes/styles to colorize.

mcs/tools/corcompare/mono-api-html/ApiChange.cs
mcs/tools/corcompare/mono-api-html/ApiDiff.cs
mcs/tools/corcompare/mono-api-html/ClassComparer.cs
mcs/tools/corcompare/mono-api-html/FieldComparer.cs
mcs/tools/corcompare/mono-api-html/MemberComparer.cs
mcs/tools/corcompare/mono-api-html/MethodComparer.cs
mcs/tools/corcompare/mono-api-html/NamespaceComparer.cs
mcs/tools/corcompare/mono-api-html/mono-api-html.csproj

index 72e905b8c58e3586a7d1bb9de5ba9bfd1873442a..1d902cacfad492a009852bce1f507a03d551d0f7 100644 (file)
@@ -21,15 +21,9 @@ namespace Xamarin.ApiDiff
 
                public ApiChange AppendAdded (string text, bool breaking = false)
                {
-                       if (breaking)
-                               Member.Append ("<span style='text-decoration: underline'>");
-                       if (State.Colorize)
-                               Member.Append ("<span style='color:green'>");
+                       Member.Append ("<span class='added ").Append (breaking ? "added-breaking-inline" : string.Empty).Append ("'>");
                        Member.Append (text);
-                       if (State.Colorize)
-                               Member.Append ("</span>");
-                       if (breaking)
-                               Member.Append ("</span>");
+                       Member.Append ("</span>");
                        Breaking |= breaking;
                        AnyChange = true;
                        return this;
@@ -37,12 +31,8 @@ namespace Xamarin.ApiDiff
 
                public ApiChange AppendRemoved (string text, bool breaking = true)
                {
-                       Member.Append ("<span style='text-decoration: line-through'>");
-                       if (State.Colorize && breaking)
-                               Member.Append ("<span style='color:red'>");
+                       Member.Append ("<span class='removed removed-inline ").Append (breaking ? "removed-breaking-inline" : string.Empty).Append ("'>");
                        Member.Append (text);
-                       if (State.Colorize && breaking)
-                               Member.Append ("</span>");
                        Member.Append ("</span>");
                        Breaking |= breaking;
                        AnyChange = true;
index 69454e1184c84e8296a1145188c0998846838587..8fe2784485220399c0646210de60a119268d78bc 100644 (file)
@@ -168,12 +168,98 @@ namespace Xamarin.ApiDiff {
                                        }
                                        if (diffHtml.Length > 0) {
                                                using (var file = new StreamWriter (diff)) {
+                                                       file.WriteLine ("<div>");
+                                                       if (State.Colorize) {
+                                                               file.WriteLine ("<style scoped>");
+                                                               file.WriteLine ("\t.obsolete { color: gray; }");
+                                                               file.WriteLine ("\t.added { color: green; }");
+                                                               file.WriteLine ("\t.removed-inline { text-decoration: line-through; }");
+                                                               file.WriteLine ("\t.removed-breaking-inline { color: red;}");
+                                                               file.WriteLine ("\t.added-breaking-inline { text-decoration: underline; }");
+                                                               file.WriteLine ("\t.nonbreaking { color: black; }");
+                                                               file.WriteLine ("\t.breaking { color: red; }");
+                                                               file.WriteLine ("</style>");
+                                                       }
+                                                       file.WriteLine (
+@"<script type=""text/javascript"">
+       // Only some elements have 'data-is-[non-]breaking' attributes. Here we
+       // iterate over all descendents elements, and set 'data-is-[non-]breaking'
+       // depending on whether there are any descendents with that attribute.
+       function propagateDataAttribute (element)
+       {
+               if (element.hasAttribute ('data-is-propagated'))
+                       return;
+
+               var i;
+               var any_breaking = element.hasAttribute ('data-is-breaking');
+               var any_non_breaking = element.hasAttribute ('data-is-non-breaking');
+               for (i = 0; i < element.children.length; i++) {
+                       var el = element.children [i];
+                       propagateDataAttribute (el);
+                       any_breaking |= el.hasAttribute ('data-is-breaking');
+                       any_non_breaking |= el.hasAttribute ('data-is-non-breaking');
+               }
+               
+               if (any_breaking)
+                       element.setAttribute ('data-is-breaking', null);
+               else if (any_non_breaking)
+                       element.setAttribute ('data-is-non-breaking', null);
+               element.setAttribute ('data-is-propagated', null);
+       }
+
+       function hideNonBreakingChanges ()
+       {
+               var topNodes = document.querySelectorAll ('[data-is-topmost]');
+               var n;
+               var i;
+               for (n = 0; n < topNodes.length; n++) {
+                       propagateDataAttribute (topNodes [n]);
+                       var elements = topNodes [n].querySelectorAll ('[data-is-non-breaking]');
+                       for (i = 0; i < elements.length; i++) {
+                               var el = elements [i];
+                               if (!el.hasAttribute ('data-original-display'))
+                                       el.setAttribute ('data-original-display', el.style.display);
+                               el.style.display = 'none';
+                       }
+               }
+               
+               var links = document.getElementsByClassName ('hide-nonbreaking');
+               for (i = 0; i < links.length; i++)
+                       links [i].style.display = 'none';
+               links = document.getElementsByClassName ('restore-nonbreaking');
+               for (i = 0; i < links.length; i++)
+                       links [i].style.display = '';
+       }
+
+       function showNonBreakingChanges ()
+       {
+               var elements = document.querySelectorAll ('[data-original-display]');
+               var i;
+               for (i = 0; i < elements.length; i++) {
+                       var el = elements [i];
+                       el.style.display = el.getAttribute ('data-original-display');
+               }
+
+               var links = document.getElementsByClassName ('hide-nonbreaking');
+               for (i = 0; i < links.length; i++)
+                       links [i].style.display = '';
+               links = document.getElementsByClassName ('restore-nonbreaking');
+               for (i = 0; i < links.length; i++)
+                       links [i].style.display = 'none';
+       }
+</script>");
                                                        if (ac.SourceAssembly == ac.TargetAssembly) {
                                                                file.WriteLine ("<h1>{0}.dll</h1>", ac.SourceAssembly);
                                                        } else {
                                                                file.WriteLine ("<h1>{0}.dll vs {1}.dll</h1>", ac.SourceAssembly, ac.TargetAssembly);
                                                        }
+                                                       file.WriteLine ("<a href='javascript: hideNonBreakingChanges (); ' class='hide-nonbreaking'>Hide non-breaking changes</a>");
+                                                       file.WriteLine ("<a href='javascript: showNonBreakingChanges (); ' class='restore-nonbreaking' style='display: none;'>Show non-breaking changes</a>");
+                                                       file.WriteLine ("<br/>");
+                                                       file.WriteLine ("<div data-is-topmost>");
                                                        file.Write (diffHtml);
+                                                       file.WriteLine ("</div> <!-- end topmost div -->");
+                                                       file.WriteLine ("</div>");
                                                }
                                        }
                                } else {
index 761f87a5552472fa51199f84daa48f8a369dbd87..1e19ab4b38efbc79a5a3d20ec1ee5d8fd5105bbf 100644 (file)
@@ -72,11 +72,13 @@ namespace Xamarin.ApiDiff {
                        string name = target.Attribute ("name").Value;
                        if (State.IgnoreNew.Any (re => re.IsMatch (name)))
                                return;
+                       Output.WriteLine ("<div> <!-- start type {0} -->", name);
                        Output.WriteLine ("<h3>New Type {0}.{1}</h3>", State.Namespace, name);
-                       Output.WriteLine (State.Colorize ? "<pre style='color: green'>" : "<pre>");
+                       Output.WriteLine ("<pre class='added' data-is-non-breaking>");
                        State.Indent = 0;
                        AddedInner (target);
                        Output.WriteLine ("</pre>");
+                       Output.WriteLine ("</div> <!-- end type {0} -->", name);
                }
 
                public void AddedInner (XElement target)
@@ -226,17 +228,17 @@ namespace Xamarin.ApiDiff {
                        var s = (Output as StringWriter).ToString ();
                        State.Output = output;
                        if (s.Length > 0) {
+                               var tn = GetTypeName (target);
+                               Output.WriteLine ("<!-- start type {0} --> <div>", tn);
                                Output.WriteLine ("<h3>Type Changed: {0}.{1}</h3>", State.Namespace, GetTypeName (target));
                                Output.WriteLine (s);
+                               Output.WriteLine ("</div> <!-- end type {0} -->", tn);
                        }
                }
 
                public override void Removed (XElement source)
                {
-                       var style = string.Empty;
-                       if (State.Colorize)
-                               style = "style='color: red'";
-                       Output.Write ("<h3>Removed Type <span {0}>{1}.{2}</span></h3>", style, State.Namespace, GetTypeName (source));
+                       Output.Write ("<h3>Removed Type <span class='breaking' data-is-breaking>{0}.{1}</span></h3>", State.Namespace, GetTypeName (source));
                }
 
                public virtual string GetTypeName (XElement type)
index bb4f6c6b075289e6256d3033099e6f9d84fc5b1e..a2b0225b4480856d2c1497bcb761c67b1abb2a33 100644 (file)
@@ -189,8 +189,9 @@ namespace Xamarin.ApiDiff {
                {
                        first = true;
                        if (State.BaseType == "System.Enum") {
+                               Output.WriteLine ("<div>");
                                Output.WriteLine ("<p>Added value{0}:</p>", list.Count () > 1 ? "s" : String.Empty);
-                               Output.WriteLine (State.Colorize ? "<pre style='color: green'>" : "<pre>");
+                               Output.WriteLine ("<pre class='added' data-is-non-breaking>");
                        } else {
                                base.BeforeAdding (list);
                        }
@@ -201,7 +202,7 @@ namespace Xamarin.ApiDiff {
                        first = true;
                        if (State.BaseType == "System.Enum") {
                                Output.WriteLine ("<p>Removed value{0}:</p>", list.Count () > 1 ? "s" : String.Empty);
-                               Output.WriteLine (State.Colorize ? "<pre style='color: red'>" : "<pre>");
+                               Output.WriteLine ("<pre class='removed' data-is-breaking>");
                        } else {
                                base.BeforeRemoving (list);
                        }
index dee528ebc7a6966e7b1472855f14d5917298ca25..92e36e8fd7953939fabedb51d186ec0b78756aa9 100644 (file)
@@ -41,6 +41,11 @@ namespace Xamarin.ApiDiff {
                public abstract string GroupName { get; }
                public abstract string ElementName { get; }
 
+               protected virtual bool IsBreakingRemoval (XElement e)
+               {
+                       return true;
+               }
+
                public void Compare (XElement source, XElement target)
                {
                        var s = source.Element (GroupName);
@@ -136,8 +141,10 @@ namespace Xamarin.ApiDiff {
                                Output.WriteLine ("<p>{0}:</p>", changes.Key);
                                Output.WriteLine ("<pre>");
                                foreach (var element in changes.Value) {
+                                       Output.Write ("<div {0}>", element.Breaking ? "data-is-breaking" : "data-is-non-breaking");
                                        foreach (var line in element.Member.ToString ().Split ('\n'))
                                                Output.WriteLine ("\t{0}", line);
+                                       Output.Write ("</div>");
 
                                }
                                Output.WriteLine ("</pre>");
@@ -192,10 +199,10 @@ namespace Xamarin.ApiDiff {
                public virtual void BeforeAdding (IEnumerable<XElement> list)
                {
                        first = true;
-                       Output.WriteLine ("<p>Added {0}:</p>", list.Count () > 1 ? GroupName : ElementName);
-
                        bool isInterface = list.Count () > 0 && IsInInterface (list.First ());
-                       Output.WriteLine (State.Colorize ? string.Format ("<pre style='color: {0}'>", isInterface ? "red" : "green") : "<pre>");
+                       Output.WriteLine ("<div>");
+                       Output.WriteLine ("<p>Added {0}:</p>", list.Count () > 1 ? GroupName : ElementName);
+                       Output.WriteLine ("<pre>");
                }
 
                public override void Added (XElement target)
@@ -203,13 +210,18 @@ namespace Xamarin.ApiDiff {
                        var o = GetObsoleteMessage (target);
                        if (!first && (o.Length > 0))
                                Output.WriteLine ();
-                       Indent ().WriteLine ("\t{0}{1}", o, GetDescription (target));
+                       Indent ();
+                       bool isInterface = IsInInterface (target);
+                       Output.Write ("\t<span class='added added-{0} {1}' {2}>", ElementName, isInterface ? "breaking" : string.Empty, isInterface ? "data-is-breaking" : "data-is-non-breaking");
+                       Output.Write ("{0}{1}", o, GetDescription (target));
+                       Output.WriteLine ("</span>");
                        first = false;
                }
 
                public virtual void AfterAdding ()
                {
-                       Output.WriteLine ("</pre>");;
+                       Output.WriteLine ("</pre>");
+                       Output.WriteLine ("</div>");
                }
 
                public override void Modified (XElement source, XElement target, ApiChanges change)
@@ -220,7 +232,7 @@ namespace Xamarin.ApiDiff {
                {
                        first = true;
                        Output.WriteLine ("<p>Removed {0}:</p>\n", list.Count () > 1 ? GroupName : ElementName);
-                       Output.WriteLine (State.Colorize ? "<pre style='color: red'>" : "<pre>");
+                       Output.WriteLine ("<pre>");
                }
 
                public override void Removed (XElement source)
@@ -228,7 +240,13 @@ namespace Xamarin.ApiDiff {
                        var o = GetObsoleteMessage (source);
                        if (!first && (o.Length > 0))
                                Output.WriteLine ();
-                       Indent ().WriteLine ("\t{0}{1}", o, GetDescription (source));
+
+                       bool is_breaking = IsBreakingRemoval (source);
+
+                       Indent ();
+                       Output.Write ("\t<span class='removed removed-{0} {2}' {1}>", ElementName, is_breaking ? "data-is-breaking" : "data-is-non-breaking", is_breaking ? "breaking" : string.Empty);
+                       Output.Write ("{0}{1}", o, GetDescription (source));
+                       Output.WriteLine ("</span>");
                        first = false;
                }
 
@@ -558,15 +576,13 @@ namespace Xamarin.ApiDiff {
                                        return; // neither is obsolete
                                var change = new ApiChange ();
                                change.Header = "Obsoleted " + GroupName;
-                               if (State.Colorize)
-                                       change.Append ("<span style='color:gray'>");
+                               change.Append (string.Format ("<span class='obsolete obsolete-{0}' data-is-non-breaking>", ElementName));
                                change.Append ("[Obsolete (");
                                if (tgtObsolete != string.Empty)
                                        change.Append ("\"").Append (tgtObsolete).Append ("\"");
                                change.Append (")]\n");
                                change.Append (GetDescription (target));
-                               if (State.Colorize)
-                                       change.Append ("</span>");
+                               change.Append ("</span>");
                                change.AnyChange = true;
                                changes.Add (source, target, change);
                        } else if (tgtObsolete == null) {
index 1dffa17dfcc512b7549127dff18e9ec6fd498a80..4c893e757ec621125fff5c82a4a9eb3fc27c93e9 100644 (file)
@@ -26,6 +26,7 @@
 
 using System;
 using System.Linq;
+using System.Reflection;
 using System.Xml.Linq;
 
 namespace Xamarin.ApiDiff {
@@ -62,5 +63,15 @@ namespace Xamarin.ApiDiff {
                                return eGPs.Count () == sGPs.Count ();
                        }
                }
+
+               protected override bool IsBreakingRemoval (XElement e)
+               {
+                       // Removing virtual methods that override another method is not a breaking change.
+                       var is_override = e.Attribute ("is-override");
+                       if (is_override != null)
+                               return is_override.Value != "true";
+                       
+                       return true; // all other removals are breaking changes
+               }
        }
 }
\ No newline at end of file
index 258374c6b9dc45230a4d715e6d5d1ce4c56c68a4..746166e0a642042f2916655984610e9fa01fcba2 100644 (file)
@@ -61,11 +61,13 @@ namespace Xamarin.ApiDiff {
                        if (State.IgnoreNew.Any (re => re.IsMatch (name)))
                                return;
 
+                       Output.WriteLine ("<!-- start namespace {0} --> <div> ", name);
                        Output.WriteLine ("<h2>New Namespace {0}</h2>", name);
                        Output.WriteLine ();
                        // list all new types
                        foreach (var addedType in target.Element ("classes").Elements ("class"))
                                comparer.Added (addedType);
+                       Output.WriteLine ("</div> <!-- end namespace {0} -->", name);
                        Output.WriteLine ();
                }
 
@@ -78,18 +80,24 @@ namespace Xamarin.ApiDiff {
                        var s = Output.ToString ();
                        State.Output = output;
                        if (s.Length > 0) {
-                               Output.WriteLine ("<h2>Namespace {0}</h2>", target.Attribute ("name").Value);
+                               var name = target.Attribute ("name").Value;
+                               Output.WriteLine ("<!-- start namespace {0} --> <div> ", name);
+                               Output.WriteLine ("<h2>Namespace {0}</h2>", name);
                                Output.WriteLine (s);
+                               Output.WriteLine ("</div> <!-- end namespace {0} -->", name);
                        }
                }
 
                public override void Removed (XElement source)
                {
-                       Output.WriteLine ("<h2>Removed Namespace {0}</h2>", source.Attribute ("name").Value);
+                       var name = source.Attribute ("name").Value;
+                       Output.WriteLine ("<!-- start namespace {0} --> <div>", name);
+                       Output.WriteLine ("<h2>Removed Namespace {0}</h2>", name);
                        Output.WriteLine ();
                        // list all removed types
                        foreach (var removedType in source.Element ("classes").Elements ("class"))
                                comparer.Removed (removedType);
+                       Output.WriteLine ("</div> <!-- end namespace {0} -->", name);
                        Output.WriteLine ();
                }
        }
index 286a4d0192abb6da70e1a5d641ede92457b022f4..6ac0f241ade40505cf481beed3438df489100aa6 100644 (file)
@@ -19,7 +19,6 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <ConsolePause>false</ConsolePause>
-    <Commandlineparameters>/Developer/MonoTouch/Source/monotouch/tools/apidiff/references/compat/monotouch.xml /Developer/MonoTouch/Source/monotouch/tools/apidiff/temp/compat/monotouch.xml -i INSObjectProtocol /tmp/diff.html</Commandlineparameters>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
     <DebugType>full</DebugType>