-
-
Notifications
You must be signed in to change notification settings - Fork 229
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
150 additions
and
98 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,70 +1,20 @@ | ||
/** Pretty Printing. | ||
* | ||
* Copyright: Per Nordlöw 2022-. | ||
* License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). | ||
* Authors: $(WEB Per Nordlöw) | ||
* | ||
* TODO: Merge in wip-src/pretty.d | ||
* | ||
* Test: dmd -I.. -i -unittest -version=show -main -run prettyio.d | ||
*/ | ||
module dub.test.prettyio; | ||
|
||
// TODO: instead override?: | ||
// void toString(Sink)(ref scope Sink sink) const scope; | ||
|
||
/++ Colorized version of `std.stdio.write`. | ||
+/ | ||
void cwrite(T...)(T args) { | ||
import std.stdio : sw = write; | ||
version (none) import nxt.ansi_escape : putWithSGRs; // TODO: use below: | ||
alias w = pathWrapperSymbol; | ||
static foreach (arg; args) {{ | ||
static immutable S = typeof(arg).stringof; | ||
// pragma(msg, __FILE__, "(", __LINE__, ",1): Debug: ", S); | ||
// static if (S == "URL") | ||
// sw(w, arg, w); // TODO: SGR.yellowForegroundColor | ||
// else static if (S == "Path") | ||
// sw(w, arg, w); // TODO: SGR.whiteForegroundColor | ||
// else static if (S == "FilePath") | ||
// sw(w, arg, w); // TODO: SGR.whiteForegroundColor | ||
// else static if (S == "DirPath") | ||
// sw(w, arg, w); // TODO: SGR.cyanForegroundColor | ||
// else static if (S == "ExePath") | ||
// sw(w, arg, w); // TODO: SGR.lightRedForegroundColor | ||
// else static if (S == "FileName") | ||
// sw(w, arg, w); // TODO: SGR.whiteForegroundColor | ||
// else static if (S == "DirName") | ||
// sw(w, arg, w); // TODO: SGR.redForegroundColor | ||
// else static if (S == "ExitStatus") | ||
// sw(w, arg, w); // TODO: SGR.greenForegroundColor if arg == 0, otherwise SGR.redForegroundColor | ||
// else | ||
sw(arg); | ||
}} | ||
} | ||
|
||
/++ Colorized version of `std.stdio.writeln`. | ||
+/ | ||
void cwriteln(T...)(T args) { | ||
import std.stdio : swln = writeln; | ||
// TODO: is this ok?: | ||
cwrite!(T)(args); | ||
swln(); | ||
} | ||
module nxt.prettyio; | ||
|
||
/++ Wrapper symbol when printing paths and URLs to standard out (`stdout`) and standard error (`stderr`). | ||
+/ | ||
private static immutable string pathWrapperSymbol = `"`; | ||
|
||
/++ Colorized pretty print `arg`. | ||
+/ | ||
void cwritePretty(T)(T arg, | ||
in size_t depth = 0, | ||
in char[] fieldName = [], | ||
in char[] indent = "\t", | ||
in char[] lterm = "\n", | ||
in bool showType = true, | ||
in void*[] ptrs = []) { | ||
void cwritePretty(T)(T arg, in size_t depth = 0, in char[] fieldName = [], in char[] indent = "\t", in char[] lterm = "\n", in bool showType = true) { | ||
scope const(void)*[] ptrs; | ||
cwritePrettyHelper!(T)(arg, depth, fieldName, indent, lterm, showType, ptrs); | ||
} | ||
private void cwritePrettyHelper(T)(T arg, in size_t depth = 0, in char[] fieldName = [], in char[] indent = "\t", in char[] lterm = "\n", in bool showType = true, ref scope const(void)*[] ptrs) { | ||
void cwriteIndent() { | ||
foreach (_; 0 .. depth) cwrite(indent); | ||
} | ||
|
@@ -79,7 +29,10 @@ void cwritePretty(T)(T arg, | |
// cwrite("class "); | ||
if (showType) cwrite(typeName, " "); | ||
} | ||
import std.traits : isArray, isSomeString, isSomeChar; | ||
void cwriteAddress(in void* ptr) { | ||
cwrite('@', ptr); | ||
} | ||
import std.traits : isArray, isSomeString, isSomeChar, isPointer; | ||
import std.range.primitives : ElementType; | ||
cwriteIndent(); | ||
cwriteFieldName(); | ||
|
@@ -88,24 +41,52 @@ void cwritePretty(T)(T arg, | |
import std.traits : FieldNameTuple; | ||
void cwriteMembers() { | ||
foreach (memberName; FieldNameTuple!T) | ||
cwritePretty(__traits(getMember, arg, memberName), depth + 1, memberName, indent); | ||
cwritePrettyHelper(__traits(getMember, arg, memberName), depth + 1, | ||
memberName, indent, lterm, showType, ptrs); | ||
} | ||
static if (is(T == class)) { | ||
const(void)* ptr; | ||
() @trusted { ptr = cast(void*)arg; }(); | ||
cwriteAddress(ptr); | ||
if (arg is null) { | ||
cwrite("null", lterm); | ||
cwrite(lterm); | ||
} else { | ||
cwrite('@', cast(void*)arg, ' '); | ||
cwrite("{", lterm); | ||
cwriteMembers(); | ||
cwriteIndent(); | ||
cwrite("}", lterm); | ||
const ix = ptrs.indexOf(ptr); | ||
cwrite(' '); | ||
if (ix != -1) { // `ptr` already printed | ||
cwrite("#", ix, lterm); // Emacs-Lisp-style back-reference | ||
} else { | ||
cwrite("#", ptrs.length, ' '); // Emacs-Lisp-style back-reference | ||
cwrite("{", lterm); | ||
ptrs ~= ptr; | ||
cwriteMembers(); | ||
cwriteIndent(); | ||
cwrite("}", lterm); | ||
} | ||
} | ||
} else { | ||
cwrite("{", lterm); | ||
cwriteMembers(); | ||
cwriteIndent(); | ||
cwrite("}", lterm); | ||
} | ||
} else static if (isPointer!T) { | ||
const ptr = cast(void*)arg; | ||
cwriteAddress(ptr); | ||
if (arg is null) { | ||
cwrite(lterm); | ||
} else { | ||
import nxt.algorithm : indexOf; | ||
const ix = ptrs.indexOf(ptr); | ||
cwrite(' '); | ||
if (ix != -1) { // `ptr` already printed | ||
cwrite("#", ix, lterm); // Emacs-Lisp-style back-reference | ||
} else { | ||
cwrite("#", ptrs.length, " -> "); // Emacs-Lisp-style back-reference | ||
ptrs ~= ptr; | ||
cwritePrettyHelper(*arg, depth, [], indent, lterm, showType, ptrs); | ||
} | ||
} | ||
} else static if (isSomeString!T) { | ||
cwrite('"', arg, '"', lterm); | ||
} else static if (isSomeChar!T) { | ||
|
@@ -115,11 +96,11 @@ void cwritePretty(T)(T arg, | |
alias E = ElementType!(T); | ||
foreach (ref element; arg) { | ||
static if (__traits(isScalar, E)) { | ||
cwritePretty(element, 0, [], [], [], false); | ||
cwritePrettyHelper(element, 0, [], [], [], false, ptrs); | ||
cwrite(','); | ||
} else { | ||
cwriteln(); | ||
cwritePretty(element, depth + 1, [], indent); | ||
cwritePrettyHelper(element, depth + 1, [], indent, lterm, showType, ptrs); | ||
} | ||
} | ||
static if (__traits(isScalar, E)) { | ||
|
@@ -135,44 +116,113 @@ void cwritePretty(T)(T arg, | |
} | ||
} | ||
|
||
@safe struct P { | ||
double x; | ||
double y; | ||
double z; | ||
/++ Colorized version of `std.stdio.write`. | ||
+/ | ||
void cwrite(T...)(T args) { | ||
import std.stdio : sw = write; | ||
version (none) import nxt.ansi_escape : putWithSGRs; // TODO: use below: | ||
alias w = pathWrapperSymbol; | ||
static foreach (arg; args) {{ | ||
static immutable S = typeof(arg).stringof; | ||
// pragma(msg, __FILE__, "(", __LINE__, ",1): Debug: ", S); | ||
// static if (S == "URL") | ||
// sw(w, arg, w); // TODO: SGR.yellowForegroundColor | ||
// else static if (S == "Path") | ||
// sw(w, arg, w); // TODO: SGR.whiteForegroundColor | ||
// else static if (S == "FilePath") | ||
// sw(w, arg, w); // TODO: SGR.whiteForegroundColor | ||
// else static if (S == "DirPath") | ||
// sw(w, arg, w); // TODO: SGR.cyanForegroundColor | ||
// else static if (S == "ExePath") | ||
// sw(w, arg, w); // TODO: SGR.lightRedForegroundColor | ||
// else static if (S == "FileName") | ||
// sw(w, arg, w); // TODO: SGR.whiteForegroundColor | ||
// else static if (S == "DirName") | ||
// sw(w, arg, w); // TODO: SGR.redForegroundColor | ||
// else static if (S == "ExitStatus") | ||
// sw(w, arg, w); // TODO: SGR.greenForegroundColor if arg == 0, otherwise SGR.redForegroundColor | ||
// else | ||
sw(arg); | ||
}} | ||
} | ||
|
||
@safe struct S { | ||
int x; | ||
double y; | ||
char[3] c3 = "abc"; | ||
string w3 = "xyz"; | ||
int[3] i3; | ||
float[3] f3; | ||
double[3] d3; | ||
real[3] r3; | ||
int[string] ais = ["a":1, "b":2]; | ||
P[string] ps = ["a":P(1,2,3)]; | ||
/++ Colorized version of `std.stdio.writeln`. | ||
+/ | ||
void cwriteln(T...)(T args) { | ||
import std.stdio : swln = writeln; | ||
// TODO: is this ok?: | ||
cwrite!(T)(args); | ||
swln(); | ||
} | ||
|
||
@safe class Cls { | ||
this(int x) { | ||
this.x = x; | ||
version (show) | ||
unittest { | ||
@safe struct P { | ||
double x; | ||
double y; | ||
double z; | ||
} | ||
int x; | ||
} | ||
|
||
@safe struct Top { | ||
S s; | ||
S[2] s2; | ||
Cls cls; | ||
Cls clsNull; | ||
string name; | ||
int[] numbers; | ||
} | ||
@safe struct S { | ||
int x; | ||
double y; | ||
char[3] c3 = "abc"; | ||
string w3 = "xyz"; | ||
int[3] i3; | ||
float[3] f3; | ||
double[3] d3; | ||
real[3] r3; | ||
int[string] ais = ["a":1, "b":2]; | ||
P[string] ps = ["a":P(1,2,3)]; | ||
P* pp0 = null; | ||
P* pp1 = new P(1,2,3); | ||
} | ||
|
||
@safe class Cls { | ||
this(int x) { | ||
this.x = x; | ||
this.parent = this; | ||
} | ||
int x; | ||
Cls parent; | ||
} | ||
|
||
@safe struct Top { | ||
S s; | ||
S[2] s2; | ||
Cls cls; | ||
Cls clsNull; | ||
string name; | ||
int[] numbers; | ||
} | ||
|
||
version (show) | ||
@safe unittest { | ||
S s = S(10, 20.5); | ||
Top top = { s, [s,s], new Cls(1), null, "example", [1, 2, 3] }; | ||
top.cwritePretty(0, "top", "\t"); | ||
top.cwritePretty(0, "top", "\t", "\n"); | ||
} | ||
|
||
/** Array-specialization of `indexOf` with default predicate. | ||
* | ||
* TODO: Add optimized implementation for needles with length >= | ||
* `largeNeedleLength` with no repeat of elements. | ||
*/ | ||
ptrdiff_t indexOf(T)(scope inout(T)[] haystack, | ||
scope const(T)[] needle) @trusted { | ||
// enum largeNeedleLength = 4; | ||
if (haystack.length < needle.length) | ||
return -1; | ||
foreach (const offset; 0 .. haystack.length - needle.length + 1) | ||
if (haystack.ptr[offset .. offset + needle.length] == needle) | ||
return offset; | ||
return -1; | ||
} | ||
/// ditto | ||
ptrdiff_t indexOf(T)(scope inout(T)[] haystack, | ||
scope const T needle) { | ||
static if (is(T == char)) | ||
assert(needle < 128); // See_Also: https://forum.dlang.org/post/[email protected] | ||
foreach (const offset, const ref element; haystack) | ||
if (element == needle) | ||
return offset; | ||
return -1; | ||
} |