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