char/avril

(wip) why'd you have to go and make things so complicaaaaated?

git clone https://git.t4t.associates/char/avril

Charlotte Somport console extension to typescript1738b3c

main
5.5 KiB169 linesraw
1import {
2  ArrayFrom,
3  ArrayPrototypePush,
4  Map,
5  MapIteratorPrototypeNext,
6  MapPrototype,
7  MapPrototypeEntries,
8  MapPrototypeGet,
9  MapPrototypeHas,
10  MapPrototypeSet,
11  MathMin,
12  ObjectGetOwnPropertyDescriptor,
13  ReflectApply,
14  ReflectOwnKeys,
15  Set,
16  SetIteratorPrototypeNext,
17  SetPrototype,
18  SetPrototypeValues,
19  String,
20  StringPrototypeRepeat,
21  StringPrototypeSlice,
22  SymbolIterator,
23  TypeError,
24} from "ext:core/primordials";
25import { inspect, intrinsicPrototype, property } from "./inspect.js";
26import { format, labelString } from "./format.js";
27
28function table(
29  data: unknown,
30  columns: unknown,
31  print: (stderr: boolean, text: string) => void,
32): void {
33  if (data === null || typeof data !== "object") {
34    print(false, format([data]));
35    return;
36  }
37  // Keep data columns distinct from the synthetic index and value columns.
38  const rows: Map<number, string>[] = [];
39  const headers = ["(index)"];
40  const columnNames = new Map<PropertyKey, number>();
41  let valuesColumn = 0;
42  let selected: string[] | null = null;
43  if (columns !== undefined) {
44    if (
45      columns === null ||
46      (typeof columns !== "object" && typeof columns !== "function")
47    ) {
48      throw new TypeError("Console table properties must be iterable");
49    }
50    const iterator =
51      (columns as { [SymbolIterator]?: unknown })[SymbolIterator];
52    if (typeof iterator !== "function") {
53      throw new TypeError("Console table properties must be iterable");
54    }
55    selected = ArrayFrom({
56      [SymbolIterator]: () =>
57        ReflectApply(iterator, columns, []) as Iterator<unknown>,
58    }, labelString);
59  }
60  const setValue = (row: Map<number, string>, text: string) => {
61    if (!valuesColumn) valuesColumn = ArrayPrototypePush(headers, "Values") - 1;
62    MapPrototypeSet(row, valuesColumn, text);
63  };
64  try {
65    const prototype = intrinsicPrototype(data);
66    if (prototype === MapPrototype || prototype === SetPrototype) {
67      const isMap = prototype === MapPrototype;
68      const iterator = isMap
69        ? MapPrototypeEntries(data as Map<unknown, unknown>)
70        : SetPrototypeValues(data as Set<unknown>);
71      const next = isMap ? MapIteratorPrototypeNext : SetIteratorPrototypeNext;
72      headers[0] = "(iteration index)";
73      if (isMap) ArrayPrototypePush(headers, "Key");
74      valuesColumn = ArrayPrototypePush(headers, "Values") - 1;
75      for (let index = 0; index < 50; index++) {
76        const step = next(iterator);
77        if (step.done) break;
78        const row = new Map<number, string>();
79        MapPrototypeSet(row, 0, String(index));
80        if (isMap) {
81          MapPrototypeSet(
82            row,
83            1,
84            inspect((step.value as [unknown, unknown])[0], { depth: 2 }),
85          );
86        }
87        setValue(
88          row,
89          inspect(isMap ? (step.value as [unknown, unknown])[1] : step.value, {
90            depth: 2,
91          }),
92        );
93        ArrayPrototypePush(rows, row);
94      }
95    } else {
96      const keys = ReflectOwnKeys(data);
97      for (let index = 0; index < keys.length && rows.length < 50; index++) {
98        const key = keys[index];
99        const descriptor = ObjectGetOwnPropertyDescriptor(data, key);
100        if (!descriptor?.enumerable) continue;
101        const row = new Map<number, string>();
102        MapPrototypeSet(row, 0, String(key));
103        const value: unknown = descriptor.value;
104        if (value !== null && typeof value === "object") {
105          const names = selected || ReflectOwnKeys(value);
106          for (let index = 0; index < names.length && index < 50; index++) {
107            const name = names[index];
108            const descriptor = ObjectGetOwnPropertyDescriptor(value, name);
109            if (!descriptor?.enumerable) continue;
110            const label = String(name);
111            if (!MapPrototypeHas(columnNames, name)) {
112              if (headers.length === 50) continue;
113              MapPrototypeSet(
114                columnNames,
115                name,
116                ArrayPrototypePush(headers, label) - 1,
117              );
118            }
119            MapPrototypeSet(
120              row,
121              MapPrototypeGet(columnNames, name)!,
122              property(value, name, 2),
123            );
124          }
125        } else setValue(row, property(data, key, 2));
126        ArrayPrototypePush(rows, row);
127      }
128    }
129  } catch {
130    print(false, format([data]));
131    return;
132  }
133  const widths: number[] = [];
134  for (let column = 0; column < headers.length; column++) {
135    let width = headers[column].length;
136    for (let row = 0; row < rows.length; row++) {
137      const value = MapPrototypeGet(rows[row], column) || "";
138      if (value.length > width) width = value.length;
139    }
140    ArrayPrototypePush(widths, MathMin(width, 80));
141  }
142  let border = "+";
143  for (let index = 0; index < widths.length; index++) {
144    border += `${StringPrototypeRepeat("-", widths[index] + 2)}+`;
145  }
146  const line = (row: string[]) => {
147    let text = "|";
148    for (let column = 0; column < headers.length; column++) {
149      const value = StringPrototypeSlice(row[column] || "", 0, widths[column]);
150      text += ` ${value}${
151        StringPrototypeRepeat(" ", widths[column] - value.length)
152      } |`;
153    }
154    print(false, text);
155  };
156  print(false, border);
157  line(headers);
158  print(false, border);
159  for (let index = 0; index < rows.length; index++) {
160    const row: string[] = [];
161    for (let column = 0; column < headers.length; column++) {
162      ArrayPrototypePush(row, MapPrototypeGet(rows[index], column) || "");
163    }
164    line(row);
165  }
166  print(false, border);
167}
168
169export { table };