浏览代码

Restore variable width font, !dbg scrolls into view and highlights col. (#852)

Some perf improvements to colorization.
Marcelo Lopez Ruiz 7 年之前
父节点
当前提交
08990d21e3

+ 0 - 1
tools/clang/tools/dotnetc/EditorForm.Designer.cs

@@ -1011,7 +1011,6 @@ namespace MainNs
             this.Controls.Add(this.TopSplitContainer);
             this.Controls.Add(this.TheStatusStrip);
             this.Controls.Add(this.TheMenuStrip);
-            this.Font = new System.Drawing.Font("Consolas", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
             this.MainMenuStrip = this.TheMenuStrip;
             this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
             this.Name = "EditorForm";

+ 48 - 32
tools/clang/tools/dotnetc/EditorForm.cs

@@ -638,16 +638,16 @@ namespace MainNs
         class RtbColorization
         {
             private RichTextBox rtb;
+            private NumericRanges colored;
             private AsmColorizer colorizer;
             private string text;
-            private int maxColored;
 
             public RtbColorization(RichTextBox rtb, string text)
             {
                 this.rtb = rtb;
                 this.colorizer = new AsmColorizer();
                 this.text = text;
-                this.maxColored = 0;
+                this.colored = new NumericRanges();
             }
 
             public void Start()
@@ -675,44 +675,47 @@ namespace MainNs
                 int firstCharIdx = rtb.GetCharIndexFromPosition(new Point(0, 0));
                 firstCharIdx = rtb.GetFirstCharIndexFromLine(rtb.GetLineFromCharIndex(firstCharIdx));
                 int lastCharIdx = rtb.GetCharIndexFromPosition(new Point(rtb.ClientSize));
+                NumericRange visibleRange = new NumericRange(firstCharIdx, lastCharIdx + 1);
 
-                // See whether we grow our simple range or skip all work.
-                if (lastCharIdx <= maxColored) return;
-                if (firstCharIdx <= maxColored)
+                if (this.colored.Contains(visibleRange))
                 {
-                    maxColored = Math.Max(maxColored, lastCharIdx);
+                    return;
                 }
 
                 var doc = GetTextDocument(rtb);
                 using (new RichTextBoxEditAction(rtb))
                 {
-                    foreach (var range in this.colorizer.GetColorRanges(this.text, firstCharIdx, lastCharIdx))
+                    foreach (var idxRange in this.colored.ListGaps(visibleRange))
                     {
-                        if (range.RangeKind == AsmRangeKind.WS) continue;
-                        if (range.Start + range.Length < firstCharIdx) continue;
-                        if (lastCharIdx < range.Start) return;
-                        Color color;
-                        switch (range.RangeKind)
+                        this.colored.Add(idxRange);
+                        foreach (var range in this.colorizer.GetColorRanges(this.text, idxRange.Lo, idxRange.Hi - 1))
                         {
-                            case AsmRangeKind.Comment:
-                                color = Color.DarkGreen;
-                                break;
-                            case AsmRangeKind.LLVMTypeName:
-                            case AsmRangeKind.Keyword:
-                            case AsmRangeKind.Instruction:
-                                color = Color.Blue;
-                                break;
-                            case AsmRangeKind.StringConstant:
-                                color = Color.DarkRed;
-                                break;
-                            case AsmRangeKind.Metadata:
-                                color = Color.DarkOrange;
-                                break;
-                            default:
-                                color = Color.Black;
-                                break;
+                            if (range.RangeKind == AsmRangeKind.WS) continue;
+                            if (range.Start + range.Length < firstCharIdx) continue;
+                            if (lastCharIdx < range.Start) return;
+                            Color color;
+                            switch (range.RangeKind)
+                            {
+                                case AsmRangeKind.Comment:
+                                    color = Color.DarkGreen;
+                                    break;
+                                case AsmRangeKind.LLVMTypeName:
+                                case AsmRangeKind.Keyword:
+                                case AsmRangeKind.Instruction:
+                                    color = Color.Blue;
+                                    break;
+                                case AsmRangeKind.StringConstant:
+                                    color = Color.DarkRed;
+                                    break;
+                                case AsmRangeKind.Metadata:
+                                    color = Color.DarkOrange;
+                                    break;
+                                default:
+                                    color = Color.Black;
+                                    break;
+                            }
+                            SetStartLengthColor(doc, range.Start, range.Length, color);
                         }
-                        SetStartLengthColor(doc, range.Start, range.Length, color);
                     }
                 }
             }
@@ -1329,10 +1332,14 @@ namespace MainNs
             return range;
         }
 
+        private static Regex dbgLineRegEx;
+        private static Regex DbgLineRegEx { get { return (dbgLineRegEx = dbgLineRegEx ?? new Regex(@"line: (\d+)")); } }
+        private static Regex dbgColRegEx;
+        private static Regex DbgColRegEx { get { return (dbgColRegEx = dbgColRegEx ?? new Regex(@"column: (\d+)")); } }
+
         private static void HandleDebugMetadata(string dbgLine, RichTextBox sourceBox)
         {
-            Regex lineRE = new Regex(@"line: (\d+)");
-            Match lineMatch = lineRE.Match(dbgLine);
+            Match lineMatch = DbgLineRegEx.Match(dbgLine);
             if (!lineMatch.Success)
             {
                 return;
@@ -1341,10 +1348,19 @@ namespace MainNs
             int lineVal = Int32.Parse(lineMatch.Groups[1].Value) - 1;
             int targetStart = sourceBox.GetFirstCharIndexFromLine(lineVal);
             int targetEnd = sourceBox.GetFirstCharIndexFromLine(lineVal + 1);
+            Match colMatch = DbgColRegEx.Match(dbgLine);
+            if (colMatch.Success)
+            {
+                targetStart += Int32.Parse(colMatch.Groups[1].Value) - 1;
+            }
+
             var highlights = SelectionHighlightData.FromRtb(sourceBox);
             highlights.ClearFromRtb(sourceBox);
             highlights.Add(targetStart, targetEnd - targetStart);
             highlights.ApplyToRtb(sourceBox, Color.Yellow);
+
+            sourceBox.SelectionStart = targetStart;
+            sourceBox.ScrollToCaret();
         }
 
         private static void HandleDebugTokenOnDisassemblyLine(RichTextBox rtb, RichTextBox sourceBox)

+ 376 - 1
tools/clang/tools/dotnetc/EditorModels.cs

@@ -11,11 +11,11 @@
 
 using DotNetDxc;
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Diagnostics;
 using System.Linq;
-using System.Xml;
 using System.Xml.Linq;
 
 namespace MainNs
@@ -204,6 +204,293 @@ namespace MainNs
         #endregion Public methods.
     }
 
+    /// <summary>Use this class to represent a range of int values.</summary>
+    [DebuggerDisplay("{Lo} - {Hi}")]
+    struct NumericRange
+    {
+        /// <summary>Low range, inclusive.</summary>
+        public int Lo;
+        /// <summary>High range, exclusive.</summary>
+        public int Hi;
+
+        public NumericRange(int lo, int hi)
+        {
+            this.Lo = lo;
+            this.Hi = hi;
+        }
+
+        public bool Equals(NumericRange other)
+        {
+            return this.IsEmpty && other.IsEmpty ||
+                this.Lo == other.Lo && this.Hi == other.Hi;
+        }
+
+        public override bool Equals(object other)
+        {
+            return other is NumericRange && Equals((NumericRange)other);
+        }
+
+        public override int GetHashCode()
+        {
+            return this.Lo | (this.Hi & 0xffff) << 16;
+        }
+
+        public static bool operator==(NumericRange left, NumericRange right)
+        {
+            return left.Equals(right);
+        }
+
+        public static bool operator !=(NumericRange left, NumericRange right)
+        {
+            return !left.Equals(right);
+        }
+
+        public bool IsEmpty { get { return Lo == Hi; } }
+
+        public bool Contains(int val)
+        {
+            return (Lo <= val && val < Hi);
+        }
+
+        public bool Contains(NumericRange range)
+        {
+            return (Lo <= range.Lo && range.Hi <= Hi);
+        }
+
+        public bool IsAdjacent(NumericRange other)
+        {
+            return !this.IsEmpty && !other.IsEmpty &&
+                (other.Hi == this.Lo || this.Hi == other.Lo);
+        }
+
+        public bool IsDisjoint(NumericRange other)
+        {
+            return this.Hi <= other.Lo || other.Hi <= this.Lo;
+        }
+
+        public bool Subtract(NumericRange other, out NumericRange reminder)
+        {
+            // If empty, nothing to subtract.
+            reminder = new NumericRange();
+            if (this.IsEmpty || other.IsEmpty)
+            {
+                return false;
+            }
+
+            // If disjoint, result is unchanged.
+            if (IsDisjoint(other))
+            {
+                return false;
+            }
+
+            // If fully contained, result is empty.
+            if (other.Contains(this))
+            {
+                this.Lo = 0;
+                this.Hi = 0;
+                return true;
+            }
+
+            // If only low edge is contained, the high segment is a reminder.
+            if (other.Contains(this.Lo) && !other.Contains(this.Hi - 1))
+            {
+                this.Lo = other.Hi;
+                return true;
+            }
+            // If only high edge is contained, the low segment is a reminder.
+            if (other.Contains(this.Hi - 1) && !other.Contains(this.Lo))
+            {
+                this.Hi = other.Lo;
+                return true;
+            }
+            // In this case, the other result is contained within, so the
+            // subtraction produces two segments: 'this' keeps the low end,
+            // and 'reminder' takes the high end.
+            reminder.Hi = this.Hi;
+            this.Hi = other.Lo;
+            reminder.Lo = other.Hi;
+            return true;
+        }
+
+        public bool TryCoalesce(NumericRange other, out NumericRange coalesced)
+        {
+            // If either is empty, position is meaningless.
+            // Coalescing can happen unless disjoint.
+            if (this.IsEmpty || other.IsEmpty || (IsDisjoint(other) && !IsAdjacent(other)))
+            {
+                coalesced = new NumericRange();
+                return false;
+            }
+
+            int newLo = Math.Min(this.Lo, other.Lo);
+            int newHi = Math.Max(this.Hi, other.Hi);
+            coalesced = new NumericRange(newLo, newHi);
+            return true;
+        }
+
+        public override string ToString()
+        {
+            return this.Lo.ToString() + "-" + this.Hi.ToString();
+        }
+    }
+
+    /// <summary>Use this class to represent multiple ranges that can be implicitly coalesced and split.</summary>
+    class NumericRanges : IEnumerable<NumericRange>
+    {
+        /// <summary>Ordered ranges.</summary>
+        private List<NumericRange> ranges = new List<NumericRange>();
+
+        public void Add(int lo, int hi)
+        {
+            if (lo == hi) return;
+            // Trivial common case - appending to last.
+            if (this.ranges.Count > 0)
+            {
+                NumericRange range = this.ranges[this.ranges.Count - 1];
+                if (range.Lo <= lo && lo <= range.Hi)
+                {
+                    if (hi <= range.Hi)
+                    {
+                        return; // already contained
+                    }
+                    this.ranges[this.ranges.Count - 1] = new NumericRange(range.Lo, hi);
+                    return;
+                }
+            }
+
+            // To keep this simple, add the new range by lo end, then coalesce.
+            bool added = false;
+            int coalesce = 0;
+            for (int i = 0; i < ranges.Count; ++i)
+            {
+                if (lo < ranges[i].Lo)
+                {
+                    // Comes prior to this range.
+                    ranges.Insert(i, new NumericRange(lo, hi));
+                    added = true;
+                    coalesce = i - 1;
+                    break;
+                }
+                if (hi <= ranges[i].Hi)
+                {
+                    // Fully overlapped.
+                    return;
+                }
+            }
+            if (!added)
+            {
+                ranges.Add(new NumericRange(lo, hi));
+                coalesce = ranges.Count - 2;
+            }
+
+            // Coalesce a few items ahead from our starting point.
+            coalesce = Math.Max(0, coalesce);
+            for (int i = coalesce; i < coalesce + 2;)
+            {
+                int next = i + 1;
+                if (next >= ranges.Count)
+                    return;
+                NumericRange range;
+                if (ranges[i].TryCoalesce(ranges[next], out range))
+                {
+                    ranges[i] = range;
+                    ranges.RemoveAt(next);
+                }
+                else
+                {
+                    ++i;
+                }
+            }
+        }
+
+        public void Add(NumericRange range)
+        {
+            Add(range.Lo, range.Hi);
+        }
+
+        public void Clear()
+        {
+            this.ranges.Clear();
+        }
+
+        public bool Contains(NumericRange range)
+        {
+            foreach (var r in ranges)
+            {
+                if (range.Hi < r.Lo)
+                {
+                    break;
+                }
+                if (r.Contains(range))
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public bool IsEmpty
+        {
+            get { return this.ranges.Count == 0; }
+        }
+
+        public IEnumerator<NumericRange> GetEnumerator()
+        {
+            return this.ranges.GetEnumerator();
+        }
+
+        public NumericRanges ListGaps(NumericRange range)
+        {
+            NumericRanges result = new NumericRanges();
+            result.Add(range);
+            foreach (var r in ranges)
+            {
+                result.Remove(r);
+            }
+            return result;
+        }
+
+        public void Remove(NumericRange range)
+        {
+            for (int i = 0; i < ranges.Count;)
+            {
+                NumericRange r = ranges[i];
+                if (range.Hi < r.Lo)
+                    return;
+                NumericRange remainder;
+                if (r.Subtract(range, out remainder))
+                {
+                    if (r.IsEmpty)
+                    {
+                        ranges.RemoveAt(i);
+                    }
+                    else
+                    {
+                        ranges[i] = r;
+                        if (!remainder.IsEmpty)
+                        {
+                            ranges.Insert(i + 1, remainder);
+                        }
+                    }
+                }
+                else
+                {
+                    i++;
+                }
+            }
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return this.ranges.GetEnumerator();
+        }
+
+        public override string ToString()
+        {
+            return String.Join(",", ranges.Select(r => r.ToString()));
+        }
+    }
+
     class SettingsManager
     {
         #region Private fields.
@@ -347,4 +634,92 @@ namespace MainNs
             return System.Convert.ToBase64String(bytes);
         }
     }
+
+#if FALSE
+    public class NumericRangesTests
+    {
+        public static void Assert(bool condition)
+        {
+            Debug.Assert(condition);
+        }
+        private static NumericRange NR(int lo, int hi) { return new NumericRange(lo, hi); }
+        public static void Run()
+        {
+            NumericRange empty = new NumericRange();
+            Assert(empty.IsEmpty);
+
+            NumericRange n = new NumericRange();
+            Assert(!n.Contains(0));
+            n.Hi = 1;
+            Assert(n.Contains(0));
+            Assert(!n.Contains(1));
+            Assert(n.IsDisjoint(new NumericRange()));
+            Assert(n.IsDisjoint(new NumericRange(1, 2)));
+
+            NumericRange remainder;
+            // Subtract - empty cases
+            Assert(false == n.Subtract(empty, out remainder));
+            Assert(remainder.IsEmpty);
+            Assert(false == empty.Subtract(n, out remainder));
+
+            // Subtract - disjoint
+            Assert(false == NR(0, 3).Subtract(NR(3, 5), out remainder));
+            n = NR(0, 3);
+
+            // Subtract - contained
+            Assert(n.Subtract(NR(0, 4), out remainder));
+            Assert(n.IsEmpty);
+
+            // Subtract - partial overlaps
+            n = NR(10, 12);
+            Assert(n.Subtract(NR(8, 11), out remainder));
+            Assert(n == NR(11, 12));
+            Assert(remainder.IsEmpty);
+            n = NR(10, 12);
+            Assert(n.Subtract(NR(11, 12), out remainder));
+            Assert(n == NR(10, 11));
+            Assert(remainder.IsEmpty);
+            n = NR(10, 21);
+            Assert(n.Subtract(NR(13, 17), out remainder));
+            Assert(n == NR(10, 13));
+            Assert(remainder == NR(17, 21));
+
+            // Coalesce - no-ops.
+            NumericRange coalesced;
+            n = NR(10, 21);
+            Assert(false == n.TryCoalesce(NR(0, 9), out coalesced));
+            Assert(false == n.TryCoalesce(empty, out coalesced));
+            Assert(n.TryCoalesce(NR(0, 10), out coalesced));
+            Assert(coalesced == NR(0, 21));
+            n = NR(10, 21);
+            Assert(n.TryCoalesce(NR(0, 100), out coalesced));
+            Assert(coalesced == NR(0, 100));
+
+            NumericRanges r = new NumericRanges();
+            Assert(r.IsEmpty);
+            r.Add(0, 10);
+            Assert(r.ToString() == "0-10");
+            r.Add(0, 4);
+            Assert(r.ToString() == "0-10");
+            r.Add(20, 20);
+            Assert(r.ToString() == "0-10");
+            r.Add(20, 30);
+            Assert(r.ToString() == "0-10,20-30");
+            r.Add(10, 20);
+            Assert(r.ToString() == "0-30");
+            r.Add(30, 32);
+            Assert(r.ToString() == "0-32");
+
+            r.Clear();
+            Assert(r.ToString() == "");
+
+            r.Add(NR(0, 10));
+            r.Add(NR(20, 30));
+
+            var gaps = r.ListGaps(NR(0, 40)).ToList();
+            Assert(gaps[0] == NR(10, 20));
+            Assert(gaps[1] == NR(30, 40));
+        }
+    }
+#endif
 }