main 98594d0

Program.cs RawHistoryFolder

1// SPDX-License-Identifier: MIT
2
3using System;
4using System.Collections.Generic;
5using System.IO;
6using System.Linq;
7using System.Text;
8using GitHub.DistributedTask.Expressions2;
9using GitHub.DistributedTask.Expressions2.Sdk;
10using GitHub.DistributedTask.Expressions2.Sdk.Functions;
11using GitHub.DistributedTask.Pipelines.ContextData;
12using Newtonsoft.Json;
13using Newtonsoft.Json.Linq;
14
15// Differential harness: runs expressions through the runner's own
16// Expressions2 code (compiled from the runner repository) and prints one JSON
17// line per case.
18//
19// Input (argv[0]): JSON {"contexts": {name: json}, "cases": [{"e": expr, "ctx": {name: json} | null}]}
20// Output: JSON lines {"k": kind, "s": string form, "j": toJson, "err": {"t": "parse"|"eval", "m": message}}
21
22internal sealed class CtxNode : NamedValue
23{
24    protected override Object EvaluateCore(EvaluationContext context, out ResultMemory resultMemory)
25    {
26        resultMemory = null;
27        return (context.State as DictionaryContextData)[Name];
28    }
29}
30
31internal sealed class Success : Function { protected override Object EvaluateCore(EvaluationContext c, out ResultMemory m) { m = null; return true; } }
32internal sealed class Always : Function { protected override Object EvaluateCore(EvaluationContext c, out ResultMemory m) { m = null; return true; } }
33internal sealed class HashFiles : Function
34{
35    protected override Object EvaluateCore(EvaluationContext c, out ResultMemory m)
36    {
37        m = null;
38        var s = "hash:";
39        for (var i = 0; i < Parameters.Count; i++)
40        {
41            if (i > 0) s += "|";
42            s += Parameters[i].Evaluate(c).ConvertToString();
43        }
44        return s;
45    }
46}
47
48internal static class P
49{
50    static DictionaryContextData Load(JObject ctx)
51    {
52        var d = new DictionaryContextData();
53        foreach (var p in ctx.Properties())
54        {
55            var token = p.Value;
56            d[p.Name] = token.ToPipelineContextData();
57        }
58        return d;
59    }
60
61    static int Main(string[] args)
62    {
63        if (args[0] == "--fold")
64        {
65            // Debug aid: the case folding of StringComparison.OrdinalIgnoreCase. Prints "XXXX YYYY" (hexadecimal
66            // code points) for every code point c whose class representative m(c) differs from c, after checking
67            // that string.Equals(a, b, OrdinalIgnoreCase) holds exactly when m(a) == m(b) over all the affected
68            // code points. m(c) is ToUpperInvariant(c) when OrdinalIgnoreCase agrees that they are equal.
69            var map = new SortedDictionary<int, int>();
70            for (var c = 0; c <= 0x10FFFF; c++)
71            {
72                if (c >= 0xD800 && c <= 0xDFFF) continue;
73                var str = char.ConvertFromUtf32(c);
74                var up = str.ToUpperInvariant();
75                if (up != str && char.ConvertToUtf32(up, 0) is var u && up.Length == char.ConvertFromUtf32(u).Length
76                    && string.Equals(str, up, StringComparison.OrdinalIgnoreCase))
77                {
78                    map[c] = u;
79                }
80            }
81            var members = new SortedSet<int>(map.Keys);
82            foreach (var v in map.Values) members.Add(v);
83            var list = new List<int>(members);
84            Func<int, int> m = c => map.TryGetValue(c, out var x) ? x : c;
85            var bad = 0;
86            foreach (var a in list)
87            {
88                foreach (var b in list)
89                {
90                    var eq = string.Equals(char.ConvertFromUtf32(a), char.ConvertFromUtf32(b), StringComparison.OrdinalIgnoreCase);
91                    if (eq != (m(a) == m(b))) { bad++; if (bad < 10) Console.Error.WriteLine($"inconsistent: {a:X} {b:X}"); }
92                }
93            }
94            // Nothing outside the table may be equal to anything but itself.
95            for (var c = 0; c <= 0x10FFFF; c++)
96            {
97                if (c >= 0xD800 && c <= 0xDFFF) continue;
98                if (members.Contains(c)) continue;
99                var str = char.ConvertFromUtf32(c);
100                foreach (var d in new[] { c ^ 1, c + 1, c - 1 })
101                {
102                    if (d < 0 || d > 0x10FFFF || (d >= 0xD800 && d <= 0xDFFF)) continue;
103                    if (string.Equals(str, char.ConvertFromUtf32(d), StringComparison.OrdinalIgnoreCase)) { bad++; if (bad < 10) Console.Error.WriteLine($"outside: {c:X} {d:X}"); }
104                }
105            }
106            Console.Error.WriteLine($"{map.Count} mappings, {bad} inconsistencies");
107            foreach (var kv in map) Console.WriteLine($"{kv.Key:X} {kv.Value:X}");
108            return bad == 0 ? 0 : 1;
109        }
110        if (args[0] == "--cmp")
111        {
112            // Debug aid: each input line is two space-separated strings written as dot-separated hexadecimal UTF-16 code
113            // units; prints Equals, Compare sign, Contains, StartsWith and EndsWith, all OrdinalIgnoreCase.
114            Func<string, string> parse = h => { var sb = new StringBuilder(); if (h != "-") foreach (var u in h.Split('.')) sb.Append((char)Convert.ToInt32(u, 16)); return sb.ToString(); };
115            foreach (var line in File.ReadLines(args[1]))
116            {
117                var parts = line.Split(' ');
118                var a = parse(parts[0]);
119                var b = parse(parts[1]);
120                Console.WriteLine($"{(string.Equals(a, b, StringComparison.OrdinalIgnoreCase) ? 1 : 0)} {Math.Sign(string.Compare(a, b, StringComparison.OrdinalIgnoreCase))} {(a.IndexOf(b, StringComparison.OrdinalIgnoreCase) >= 0 ? 1 : 0)} {(a.StartsWith(b, StringComparison.OrdinalIgnoreCase) ? 1 : 0)} {(a.EndsWith(b, StringComparison.OrdinalIgnoreCase) ? 1 : 0)}");
121            }
122            return 0;
123        }
124        if (args[0] == "--num")
125        {
126            // Debug aid: each input line is a string as dot-separated hexadecimal UTF-16 code units ("-" for the
127            // empty string); prints the IEEE 754 bits (hexadecimal) of ExpressionUtility.ParseNumber of it.
128            foreach (var line in File.ReadLines(args[1]))
129            {
130                var sb = new StringBuilder();
131                if (line.Trim() != "-") foreach (var u in line.Trim().Split('.')) sb.Append((char)Convert.ToInt32(u, 16));
132                var d = ExpressionUtility.ParseNumber(sb.ToString());
133                Console.WriteLine(double.IsNaN(d) ? "NaN" : BitConverter.DoubleToInt64Bits(d).ToString("x"));
134            }
135            return 0;
136        }
137        if (args[0] == "--g15")
138        {
139            // Debug aid: one hexadecimal IEEE 754 bit pattern per input line, the G15 form per output line.
140            foreach (var line in File.ReadLines(args[1]))
141            {
142                var d = BitConverter.Int64BitsToDouble(Convert.ToInt64(line.Trim(), 16));
143                Console.WriteLine(d.ToString("G15", System.Globalization.CultureInfo.InvariantCulture));
144            }
145            return 0;
146        }
147        JObject root; using (var jr0 = new JsonTextReader(new StringReader(File.ReadAllText(args[0]))) { DateParseHandling = DateParseHandling.None }) root = JObject.Load(jr0);
148        var defaultCtx = (JObject)root["contexts"];
149        var funcs = new IFunctionInfo[]
150        {
151            new FunctionInfo<Success>("success", 0, 0),
152            new FunctionInfo<Always>("always", 0, 0),
153            new FunctionInfo<HashFiles>("hashFiles", 1, 255),
154        };
155        var output = new StreamWriter(Console.OpenStandardOutput(), new UTF8Encoding(false));
156        var defaultData = Load(defaultCtx);
157        foreach (var c in (JArray)root["cases"])
158        {
159            var e = (string)c["e"];
160            var custom = c["ctx"] as JObject;
161            var data = custom != null ? Load(custom) : defaultData;
162            var names = (custom ?? defaultCtx).Properties().Select(p => p.Name);
163            var res = new JObject();
164            IExpressionNode tree = null;
165            try
166            {
167                tree = new ExpressionParser().CreateTree(e, null, names.Select(n => (INamedValueInfo)new NamedValueInfo<CtxNode>(n)).ToList(), funcs);
168            }
169            catch (Exception ex)
170            {
171                res["err"] = new JObject { ["t"] = "parse", ["m"] = ex.Message, ["x"] = ex.GetType().Name };
172            }
173            if (res["err"] == null)
174            {
175                if (tree == null)
176                {
177                    res["empty"] = true;
178                }
179                else
180                {
181                    try
182                    {
183                        var r = tree.Evaluate(null, null, data, null);
184                        res["k"] = r.Kind.ToString();
185                        res["s"] = r.ConvertToString();
186                        // toJson of the result
187                        var tj = new ToJson();
188                        tj.AddParameter(new Literal(r.Value));
189                        try
190                        {
191                            var jr = ((IExpressionNode)tj).Evaluate(null, null, data, null);
192                            res["j"] = jr.ConvertToString();
193                        }
194                        catch (Exception ex2)
195                        {
196                            res["jerr"] = ex2.Message;
197                        }
198                    }
199                    catch (Exception ex)
200                    {
201                        res["err"] = new JObject { ["t"] = "eval", ["m"] = ex.Message, ["x"] = ex.GetType().Name };
202                    }
203                }
204            }
205            // ASCII-escaped, so that a lone surrogate (which UTF-8 cannot carry) survives.
206            var sw = new StringWriter();
207            using (var jw = new JsonTextWriter(sw) { StringEscapeHandling = StringEscapeHandling.EscapeNonAscii, Formatting = Formatting.None })
208            {
209                res.WriteTo(jw);
210            }
211            output.WriteLine(sw.ToString());
212        }
213        output.Flush();
214        return 0;
215    }
216}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 3

↑↓ moveenter openesc close