File Coverage

blib/lib/Dump/Krumo.pm
Criterion Covered Total %
statement 240 356 67.4
branch 85 148 57.4
condition 48 78 61.5
subroutine 32 40 80.0
pod 2 21 9.5
total 407 643 63.3


line stmt bran cond sub pod time code
1             #!/usr/bin/env perl
2              
3 2     2   223532 use strict;
  2         4  
  2         78  
4 2     2   14 use warnings;
  2         3  
  2         120  
5 2     2   27 use v5.16;
  2         8  
6 2     2   10 use Scalar::Util;
  2         4  
  2         140  
7              
8             package Dump::Krumo;
9              
10 2     2   42 use Carp;
  2         2  
  2         141  
11 2     2   11 use Exporter 'import';
  2         3  
  2         9925  
12              
13             our @EXPORT = qw(kx kxd);
14             our @EXPORT_OK = qw(k kd);
15              
16             # If you `use Dump::Krumo (":short");` then you get k() and kd() instead
17             our %EXPORT_TAGS = ('short' => [('k', 'kd')]);
18              
19             # https://blogs.perl.org/users/grinnz/2018/04/a-guide-to-versions-in-perl.html
20             our $VERSION = 'v0.1.8';
21              
22             our $use_color = 1; # Output in color
23             our $return_string = 0; # Return a string instead of printing it
24             our $hash_sort = 1; # Sort hash keys before output
25             our $debug = 0; # Low level developer level debugging
26             our $disable = 0; # Disable Dump::Krumo
27             our $indent_spaces = 2; # Number of spaces to use for each level of indent
28             our $promote_bool = 1; # Convert JSON::PP::Boolean to raw true/false
29             our $stack_trace = 0; # kxd() prints a stack trace
30              
31             # Global var to track how many levels we're indented
32             my $current_indent_level = 0;
33             # Global var to track the indent to the right end of the most recent hash key
34             my $left_pad_width = 0;
35              
36             our $COLORS = {
37             'string' => 230, # Standard strings
38             'control_char' => 226, # the `\n`, `\r`, and `\t` inside strings
39             'undef' => 196, # undef
40             'hash_key' => 208, # hash keys on the left of =>
41             'integer' => 33, # integers
42             'float' => 51, # things that look like floating point
43             'class' => 118, # Classes/Object names
44             'binary' => 226, # \x{12} inside of strings
45             'scalar_ref' => 225, # References to scalar variables
46             'boolean_false' => 'white_on_124', # Native boolean false
47             'boolean_true' => 'white_on_22', # Native boolean true
48             'regexp' => 164, # qr() style regexp variables
49             'glob' => 40, # \*STDOUT variables
50             'coderef' => 168, # code references
51             'vstring' => 153, # Version strings
52             'empty_braces' => '15_bold', # Either [] or {} or ''
53             };
54              
55             our $WIDTH = get_terminal_width();
56             $WIDTH ||= 100;
57              
58             ###############################################################################
59             ###############################################################################
60              
61             # Dump the variable information
62             sub kx {
63 36     36 1 128087 my @arr = @_;
64              
65             # If we are globally disabled we do nothing
66 36 50       112 if ($disable) { return -1; }
  0         0  
67              
68 36         51 my @items = ();
69 36         60 my $cnt = scalar(@arr);
70 36         59 my $is_array = 0;
71              
72             # If someone passes in a real array (not ref) we fake it out
73 36 100 100     150 if ($cnt > 1 || $cnt == 0) {
74 3         6 @arr = (\@_); # Convert to arrayref
75 3         4 $is_array = 1;
76             }
77              
78             # Loop through each item and dump it out
79 36         68 foreach my $item (@arr) {
80 36         118 push(@items, __dump($item));
81             }
82              
83 36 50       76 if (!@items) {
84 0         0 @items = ("UNKNOWN TYPE");
85             }
86              
87 36         80 my $str = join(", ", @items);
88              
89             # If it's a real array we remove the false [ ] added by __dump()
90 36 100       65 if ($is_array) {
91 3         4 my $len = length($str);
92 3         6 $str = substr($str, 1, $len - 2);
93             }
94              
95 36 100 100     119 if ($cnt > 1 || $cnt == 0) {
96 3         6 $str = "($str)";
97             }
98              
99 36 50       61 if ($return_string) {
100 36         188 return $str;
101             } else {
102 0         0 print "$str\n";
103             }
104             }
105              
106             # Dump the variable and die and output file/line
107             sub kxd {
108             # If we are globally disabled we do nothing
109 0 0   0 1 0 if ($disable) { return -1; }
  0         0  
110              
111 0         0 kx(@_);
112              
113 0         0 print "\n";
114              
115 0         0 my $str = color('117', "Dump::Krumo") . " died";
116              
117 0 0       0 if ($stack_trace) {
118 0         0 confess($str);
119             } else {
120 0         0 croak($str);
121             }
122             }
123              
124             # Generic dump that handles each type appropriately
125             sub __dump {
126 56     56   82 my $x = shift();
127 56         86 my $type = ref($x);
128 56   100     175 my $class = Scalar::Util::blessed($x) || "";
129              
130 56         107 my $ret;
131              
132 56 100 100     219 if ($type eq 'ARRAY') {
    100 100        
    100 100        
    100 100        
    100 66        
    100          
    100          
    100          
    100          
    100          
    50          
    0          
    0          
133 10         24 $ret = __dump_array($x);
134             } elsif ($type eq 'HASH') {
135 6         12 $ret = __dump_hash($x);
136             } elsif ($type eq 'SCALAR') {
137 3         7 $ret = color(get_color('scalar_ref'), '\\' . quote_string($$x));
138             } elsif (!$type && is_bool_val($x)) {
139 2         6 $ret = __dump_bool($x);
140             } elsif (!$type && is_integer($x)) {
141 16         25 $ret = __dump_integer($x);
142             } elsif (!$type && is_float($x)) {
143 1         9 $ret = __dump_float($x);
144             } elsif (!$type && is_string($x)) {
145 11         22 $ret = __dump_string($x);
146             } elsif (!$type && is_undef($x)) {
147 2         6 $ret = __dump_undef();
148             } elsif ($class eq "Regexp") {
149 3         10 $ret = __dump_regexp($class, $x);
150             } elsif ($type eq "GLOB") {
151 1         4 $ret = __dump_glob($class, $x);
152             } elsif ($type eq "CODE") {
153 1         3 $ret = __dump_coderef($class, $x);
154             } elsif ($type eq "VSTRING") {
155 0         0 $ret = __dump_vstring($x);
156             } elsif ($class) {
157 0         0 $ret = __dump_class($class, $x);
158             } else {
159 0         0 $ret = "Unknown variable type: '$type'";
160             }
161              
162 56         155 return $ret;
163             }
164              
165             ################################################################################
166             # Each variable type gets it's own dump function
167             ################################################################################
168              
169             sub __dump_bool {
170 2     2   3 my $x = shift();
171 2         5 my $ret;
172              
173 2 100       8 if ($x) {
174 1         4 $ret = color(get_color('boolean_true'), "true");
175             } else {
176 1         4 $ret = color(get_color('boolean_false'), "false");
177             }
178              
179 2         5 return $ret;
180             }
181              
182             sub __dump_regexp {
183 3     3   7 my ($class, $x) = @_;
184              
185 3         8 my $ret = color(get_color('regexp'), "qr$x");
186              
187 3         9 return $ret;
188             }
189              
190             sub __dump_coderef {
191 1     1   2 my ($class, $x) = @_;
192              
193 1         3 my $ret = color(get_color('coderef'), "sub { ... }");
194              
195 1         3 return $ret;
196             }
197              
198             sub __dump_glob {
199 1     1   3 my ($class, $x) = @_;
200              
201 1         3 my $ret = color(get_color('glob'), "\\" . $$x);
202              
203 1         3 return $ret;
204             }
205              
206             sub __dump_class {
207 0     0   0 my ($class, $x) = @_;
208              
209 0         0 my $ret = '"' . color(get_color('class'), $class) . "\" :: ";
210 0         0 my $reftype = Scalar::Util::reftype($x);
211 0         0 my $y;
212              
213 0 0 0     0 if ($promote_bool && $class eq 'JSON::PP::Boolean') {
214 0         0 my $val = $$x;
215 0         0 return __dump_bool(!!$val);
216             }
217              
218 0         0 my $len = length($class) + 6; # 2x quotes and ' :: '
219 0         0 $left_pad_width += $len;
220              
221             # We need an unblessed copy of the data so we can display it
222 0 0       0 if ($reftype eq 'ARRAY') {
    0          
    0          
223 0         0 $y = [@$x];
224             } elsif ($reftype eq 'HASH') {
225 0         0 $y = {%$x};
226             } elsif ($reftype eq 'SCALAR') {
227 0         0 $y = $$x;
228             } else {
229 0         0 $y = "Unknown class?";
230             }
231              
232 0         0 $ret .= __dump($y);
233              
234 0         0 $left_pad_width -= $len;
235              
236 0         0 return $ret;
237             }
238              
239             sub __dump_integer {
240 16     16   21 my $x = shift();
241 16         54 my $ret = color(get_color('integer'), $x);
242              
243 16         25 return $ret;
244             }
245              
246             sub __dump_float {
247 1     1   3 my $x = shift();
248 1         3 my $ret = color(get_color('float'), $x);
249              
250 1         3 return $ret;
251             }
252              
253             sub __dump_vstring {
254 0     0   0 my $x = shift();
255              
256 0         0 my @parts = unpack("C*", $$x);
257 0         0 my $str = "\\v" .(join ".", @parts);
258              
259 0         0 my $ret = color(get_color('vstring'), $str);
260              
261 0         0 return $ret;
262             }
263              
264             sub __dump_string {
265 11     11   16 my $x = shift();
266              
267             # This is the catch all for "" or ''
268 11 100       24 if (length($x) == 0) {
269 2         6 return color(get_color('empty_braces'), "''"),
270             }
271              
272             # Is the whole string printable
273 9         17 my $printable = is_printable($x);
274              
275 9         14 my $ret = '';
276              
277             # For short strings we show the unprintable chars as \x{00} escapes
278 9 100 66     28 if (!$printable && (length($x) < 20)) {
    50          
279 1         6 my @p = unpack("C*", $x);
280              
281 1         3 my $str = '';
282 1         3 foreach my $x (@p) {
283 3         10 my $is_printable = is_printable(chr($x));
284              
285 3 100       8 if ($is_printable) {
286 2         6 $str .= color(get_color('string'),chr($x));
287             } else {
288 1         4 $str .= color(get_color('binary'), '\\x{' . sprintf("%02X", $x) . '}');
289             }
290             }
291              
292 1         3 $ret = "\"$str\"";
293             # Longer unprintable stuff we just spit out the raw HEX
294             } elsif (!$printable) {
295 0         0 $ret = color(get_color('binary'), 'pack("H*", ' . bin2hex($x) . ")");
296             } else {
297 8         19 my $quoted = quote_string($x);
298 8         16 $ret = color(get_color('string'), $quoted);
299             }
300              
301             # Convert special chars to printable version
302 9         16 my $slash_n = color(get_color('control_char'), '\\n') . color(get_color('string'));
303 9         18 my $slash_r = color(get_color('control_char'), '\\r') . color(get_color('string'));
304 9         18 my $slash_t = color(get_color('control_char'), '\\t') . color(get_color('string'));
305 9         18 my $slash_f = color(get_color('control_char'), '\\f') . color(get_color('string'));
306              
307 9         24 $ret =~ s/\\n/$slash_n/g;
308 9         17 $ret =~ s/\\r/$slash_r/g;
309 9         17 $ret =~ s/\\t/$slash_t/g;
310 9         13 $ret =~ s/\\t/$slash_f/g;
311              
312 9         20 return $ret;
313             }
314              
315             sub __dump_undef {
316 2     2   6 my $ret = color(get_color('undef'), 'undef');
317              
318 2         5 return $ret;
319             }
320              
321             sub __dump_array {
322 10     10   14 my $x = shift();
323              
324 10         17 $current_indent_level++;
325              
326             # Catch if it's an empty array
327 10         14 my $cnt = scalar(@$x);
328 10 100       20 if ($cnt == 0) {
329 2         2 $current_indent_level--;
330 2         4 return color(get_color('empty_braces'), '[]'),
331             }
332              
333             # See if we need to switch to column mode to output this array
334 8         18 my $column_mode = needs_column_mode($x);
335              
336             # Loop through each item and dump it approprirately
337 8         15 my $ret = '';
338 8         10 my @items = ();
339 8         15 foreach my $z (@$x) {
340 14         26 push(@items, __dump($z));
341             }
342              
343 8 50       22 if ($column_mode) {
344 0         0 $ret = "[\n";
345 0         0 my $pad = " " x ($current_indent_level * $indent_spaces);
346 0         0 foreach my $x (@items ) {
347 0         0 $ret .= $pad . "$x,\n";
348             }
349              
350 0         0 $pad = " " x (($current_indent_level - 1) * $indent_spaces);
351 0         0 $ret .= $pad . "]";
352             } else {
353 8         24 $ret = '[' . join(", ", @items) . ']';
354             }
355              
356 8         12 $current_indent_level--;
357              
358 8         18 return $ret;
359             }
360              
361             sub __dump_hash {
362 6     6   7 my $x = shift();
363 6         6 $current_indent_level++;
364              
365 6         6 my $ret;
366 6         6 my @items = ();
367 6         13 my @keys = keys(%$x);
368 6         7 my @vals = values(%$x);
369 6         7 my $cnt = scalar(@keys);
370              
371             # Catch an empty hash like: {}
372 6 100       11 if ($cnt == 0) {
373 1         2 $current_indent_level--;
374 1         2 return color(get_color('empty_braces'), '{}'),
375             }
376              
377             # There may be some weird scenario where we do NOT want to sort
378 5 50       8 if ($hash_sort) {
379 5         11 @keys = sort(@keys);
380             }
381              
382 5         5 my $key_len = 0;
383 5         7 foreach my $x (@keys) {
384 6         7 $key_len += length($x) + 4; # Add four for ' => '
385             }
386              
387             # See if we need to switch to column mode to output this array
388 5         9 my $max_length = max_length(@keys);
389 5         5 $left_pad_width = $max_length;
390 5         9 my $column_mode = needs_column_mode($x, $key_len);
391              
392             # If we're not in column mode there is no need to compensate for this
393 5 50       11 if (!$column_mode) {
394 5         6 $max_length = 0;
395             }
396              
397             # Check to see if any of the array keys need to be quoted
398 5         3 my $keys_need_quotes = 0;
399 5         6 foreach my $key (@keys) {
400 6 100       19 if ($key =~ /\W/) {
401 3         4 $keys_need_quotes = 1;
402 3         5 last;
403             }
404             }
405              
406             # Loop through each key and build the appropriate string for it
407 5         6 foreach my $key (@keys) {
408 6         8 my $val = $x->{$key};
409              
410 6         5 my $key_str = '';
411 6 100       8 if ($keys_need_quotes) {
412 3         6 $key_str = "'" . color(get_color('hash_key'), $key) . "'";
413             } else {
414 3         5 $key_str = color(get_color('hash_key'), $key);
415             }
416              
417             # Align the hash keys
418 6 50       9 if ($column_mode) {
419 0         0 my $raw_len = length($key);
420 0         0 my $append_cnt = $max_length - $raw_len;
421              
422             # Sometimes this goes negative?
423 0 0       0 if ($append_cnt < 0) {
424 0         0 $append_cnt = 0;
425             }
426              
427 0         0 $key_str .= " " x $append_cnt;
428             }
429              
430 6         8 push(@items, $key_str . ' => ' . __dump($val));
431             }
432              
433             # If we're too wide for the screen we drop to column mode
434 5 50       7 if ($column_mode) {
435 0         0 $ret = "{\n";
436              
437 0         0 foreach my $x (@items) {
438 0         0 my $pad = " " x ($current_indent_level * $indent_spaces);
439 0         0 $ret .= $pad . "$x,\n";
440             }
441              
442 0         0 my $pad = " " x (($current_indent_level - 1) * $indent_spaces);
443 0         0 $ret .= $pad . "}";
444             } else {
445 5         11 $ret = '{ ' . join(", ", @items) . ' }';
446             }
447              
448 5         5 $current_indent_level--;
449              
450 5         8 return $ret;
451             }
452              
453             ################################################################################
454             # Various helper functions
455             ################################################################################
456              
457             # Calculate the length of the longest string in an array
458             sub max_length {
459 5     5 0 5 my $max = 0;
460              
461 5         7 foreach my $item (@_) {
462 6         8 my $len = length($item);
463 6 100       9 if ($len > $max) {
464 5         5 $max = $len;
465             }
466             }
467              
468 5         5 return $max;
469             }
470              
471             # Calculate the length in chars of this array
472             sub array_str_len {
473 18     18 0 25 my @arr = @_;
474              
475 18         22 my $len = 0;
476 18         36 foreach my $x (@arr) {
477 26 100 33     86 if (!defined($x)) {
    50          
    50          
    50          
    50          
478 1         3 $len += 5; # The string "undef"
479             } elsif (ref $x eq 'ARRAY') {
480 0         0 $len += array_str_len(@$x);
481             } elsif (ref $x eq 'HASH') {
482 0         0 $len += array_str_len(%$x);
483             } elsif (is_bool_val($x) && $x) {
484 0         0 $len += 6; # 'true'
485             } elsif (is_bool_val($x)) {
486 0         0 $len += 7; # 'false'
487             } else {
488 25         33 $len += length($x);
489              
490 25 100       34 if (!is_numeric($x)) {
491 12         15 $len += 2; # For the quotes around the string
492             }
493             }
494              
495             # We stop counting after we hit $WIDTH so we don't
496             # waste a bunch of CPU cycles counting something we
497             # won't ever use (useful in big nested objects)
498 26 50       53 if ($len > $WIDTH) {
499 0         0 return $WIDTH + 999;
500             }
501             }
502              
503 18         27 return $len;
504             }
505              
506             # Calculate if this data structure will wrap the screen and needs to be in column mode instead
507             sub needs_column_mode {
508 13     13 0 22 my ($x, $extra_len) = @_;
509 13   100     40 $extra_len //= 0;
510              
511 13         18 my $ret = 0;
512 13         15 my $len = 0;
513 13         41 my $type = ref($x);
514              
515 13 100       25 if ($type eq "ARRAY") {
    50          
    0          
516 8         10 my $cnt = scalar(@$x);
517              
518 8         18 $len += array_str_len(@$x);
519 8         13 $len += 2; # For the '[' on the start/end
520 8         14 $len += 2 * $cnt; # ', ' for each item
521             } elsif ($type eq "HASH") {
522 5         8 my @keys = keys(%$x);
523 5         6 my @vals = values(%$x);
524 5         5 my $cnt = scalar(@keys);
525              
526 5         6 $len += array_str_len(@keys);
527 5         7 $len += array_str_len(@vals);
528 5         6 $len += 4; # For the '{ ' on the start/end
529 5         8 $len += 6 * $cnt; # ' => ' and the ', ' for each item
530             # This is a class/obj
531             } elsif ($type) {
532 0         0 my $cnt = scalar(@$x);
533              
534 0         0 $len += array_str_len(@$x);
535 0         0 $len += 2; # For the '[' on the start/end
536 0         0 $len += 2 * $cnt; # ' => ' and the ', ' for each item
537             }
538              
539 13         23 my $content_len = $len;
540              
541             # Current number of spaces we're indented from the left
542 13         20 my $left_indent = ($current_indent_level - 1) * $indent_spaces;
543             # Where the ' => ' in the hash key ends
544 13         18 my $pad_width = $left_pad_width + 4; # For the ' => '
545              
546             # Add it all together
547 13         15 $len = $left_indent + $pad_width + $len + $extra_len;
548              
549             # If we're too wide for the screen we drop to column mode
550             # Our math isn't 100% down the character so we use 97% to give
551             # ourselves some wiggle room
552 13 50       31 if ($len > ($WIDTH * .97)) {
553 0         0 $ret = 1;
554             }
555              
556             # This math is kinda gnarly so if we turn on debug mode we can
557             # see each array/hash and how we calculate the length
558 13 50       23 if ($debug) {
559 0         0 state $first = 1;
560              
561 0 0       0 if ($first) {
562 0         0 printf("Screen width: %d\n\n", $WIDTH * .97);
563 0         0 printf("Left Indent | Hash Padding | Content | Extra | Total\n");
564 0         0 $first = 0;
565             }
566              
567 0         0 printf("%8d + %6d + %4d + %4d = %4d (%d)\n", $left_indent, $pad_width, $content_len, $extra_len, $len, $ret);
568             }
569              
570 13         20 return $ret;
571             }
572              
573             # Convert raw bytes to hex for easier printing
574             sub bin2hex {
575 0     0 0 0 my $bytes = shift();
576 0         0 my $ret = uc(unpack("H*", $bytes));
577              
578 0         0 return $ret;
579             }
580              
581             ################################################################################
582             # Test functions to determine what type of variable something is
583             ################################################################################
584              
585             # Does the string contain only printable characters
586             sub is_printable {
587 12     12 0 22 my ($str) = @_;
588              
589             # If we're just checking a single char, anything out of the ASCII range is
590             # not considered printable
591 12 50 66     31 if (length($str) == 1 && (ord($str) >= 127)) {
592 0         0 return 0;
593             }
594              
595 12         18 my $ret = 0;
596 12 100 66     60 if (defined($str) && $str =~ /^[[:print:]\n\r\t]*$/) {
597 10         13 $ret = 1;
598             }
599              
600 12         21 return $ret;
601             }
602              
603             sub is_undef {
604 2     2 0 4 my $x = shift();
605              
606 2 50       6 if (!defined($x)) {
607 2         7 return 1;
608             } else {
609 0         0 return 0;
610             }
611             }
612              
613             # Verify this
614             sub is_nan {
615 0     0 0 0 my $x = shift();
616 0         0 my $ret = 0;
617              
618 0 0       0 if ($x != $x) {
619 0         0 $ret = 1;
620             }
621              
622 0         0 return $ret;
623             }
624              
625             # Verify this
626             sub is_infinity {
627 0     0 0 0 my $x = shift();
628 0         0 my $ret = 0;
629              
630 0 0       0 if ($x * 2 == $x) {
631 0         0 $ret = 1;
632             }
633              
634 0         0 return $ret;
635             }
636              
637             sub is_string {
638 13     13 0 22 my ($value) = @_;
639              
640             # For our purposes it's considered a string if it doesn't look like a number
641 13   66     62 return defined($value) && !is_numeric($value);
642             }
643              
644             sub is_integer {
645 30     30 0 50 my ($value) = @_;
646              
647 30   100     213 return defined($value) && $value =~ /^-?\d+$/;
648             }
649              
650             sub is_float {
651 14     14 0 27 my ($value) = @_;
652              
653             # Note 1.2e+100 is considered a float along with the more common types
654 14   100     53 my $ret = defined($value) && $value =~ /^-?\d+\.\d+(e[+-]\d+)?$/;
655              
656 14         58 return $ret;
657             }
658              
659             # Borrowed from builtin::compat
660             sub is_bool_val {
661 82     82 0 112 my $value = shift;
662              
663             # Make sure the variable is defined, is not a reference and is a dualval
664 82 100       132 if (!defined($value)) { return 0; }
  2         12  
665 80 100       153 if (length(ref($value)) != 0) { return 0; }
  2         7  
666 78 100       161 if (!Scalar::Util::isdual($value)) { return 0; }
  76         212  
667              
668             # Make sure the string and integer versions match
669 2 100 66     10 if ($value == 1 && $value eq '1') { return 1; }
  1         5  
670 1 50 33     8 if ($value == 0 && $value eq '') { return 1; }
  1         6  
671              
672 0         0 return 0;
673             }
674              
675             sub is_numeric {
676 36     36 0 101 my $ret = Scalar::Util::looks_like_number($_[0]);
677              
678 36         92 return $ret;
679             }
680              
681             # This is a wrapper needed for :short
682             sub k {
683 0     0 0 0 return kx(@_);
684             }
685              
686             # This is a wrapper needed for :short
687             sub kd {
688 0     0 0 0 return kxd(@_);
689             }
690              
691              
692              
693             ################################################################################
694              
695             # String format: '115', '165_bold', '10_on_140', 'reset', 'on_173', 'red', 'white_on_blue'
696             sub color {
697 123     123 0 195 my ($str, $txt) = @_;
698              
699             # If we're NOT connected to a an interactive terminal don't do color
700 123   33     156 state $color_available = (!$use_color || -t STDOUT == 0);
701 123 50       231 if ($color_available) {
702 123   100     383 return $txt // "";
703             }
704              
705             # No string sent in, so we just reset
706 0 0 0     0 if (!length($str) || $str eq 'reset') { return "\e[0m"; }
  0         0  
707              
708             # Some predefined colors
709 0         0 my %color_map = qw(red 160 blue 27 green 34 yellow 226 orange 214 purple 93 white 15 black 0);
710 0   0     0 $str =~ s|([A-Za-z]+)|$color_map{$1} // $1|eg;
  0         0  
711              
712             # Get foreground/background and any commands
713 0         0 my ($fc,$cmd) = $str =~ /^(\d{1,3})?_?(\w+)?$/g;
714 0         0 my ($bc) = $str =~ /on_(\d{1,3})$/g;
715              
716 0 0 0     0 if (defined($fc) && int($fc) > 255) { $fc = undef; } # above 255 is invalid
  0         0  
717              
718             # Some predefined commands
719 0         0 my %cmd_map = qw(bold 1 italic 3 underline 4 blink 5 inverse 7);
720 0   0     0 my $cmd_num = $cmd_map{$cmd // 0};
721              
722 0         0 my $ret = '';
723 0 0       0 if ($cmd_num) { $ret .= "\e[${cmd_num}m"; }
  0         0  
724 0 0       0 if (defined($fc)) { $ret .= "\e[38;5;${fc}m"; }
  0         0  
725 0 0       0 if (defined($bc)) { $ret .= "\e[48;5;${bc}m"; }
  0         0  
726 0 0       0 if (defined($txt)) { $ret .= $txt . "\e[0m"; }
  0         0  
727              
728 0         0 return $ret;
729             }
730              
731             sub get_terminal_width {
732             # If there is no $TERM then tput will bail out
733 2 50 33 2 0 28 if (!$ENV{TERM} || -t STDOUT == 0) {
734 2         5 return 0;
735             }
736              
737 0         0 my $tput = `tput cols`;
738 0         0 my $width = 0;
739              
740 0 0       0 if ($tput) {
741 0         0 $width = int($tput);
742             } else {
743 0         0 print color('orange', "Warning:") . " `tput cols` did not return numeric input\n";
744 0         0 $width = 80;
745             }
746              
747 0         0 return $width;
748             }
749              
750             # See also B::perlstring as a possible alternative
751             sub quote_string {
752 11     11 0 18 my ($s) = @_;
753              
754             # Use single quotes if no special chars
755 11 100       32 if ($s !~ /[\'\\\n\r\t\f\$@]/ ) {
756 9         28 return "'$s'";
757             }
758              
759             # Otherwise, escape for double quotes
760 2         5 (my $escaped = $s) =~ s/([\\"])/\\$1/g;
761 2         6 $escaped =~ s/\n/\\n/g;
762 2         4 $escaped =~ s/\r/\\r/g;
763 2         4 $escaped =~ s/\t/\\t/g;
764 2         3 $escaped =~ s/\f/\\f/g;
765              
766 2         7 return "\"$escaped\"";
767             }
768              
769             # This is used to look up the color for each type
770             sub get_color {
771 123   50 123 0 256 my $str = $_[0] || "";
772              
773 123   50     268 my $ret = $COLORS->{$str} // 251;
774              
775 123         278 return $ret;
776             }
777              
778             ################################################################################
779             ################################################################################
780             ################################################################################
781              
782             =encoding utf8
783              
784             =head1 NAME
785              
786             Dump::Krumo - Fancy, colorful, human readable dumps of your data
787              
788             =head1 SYNOPSIS
789              
790             use Dump::Krumo;
791              
792             my $data = { one => 1, two => 2, three => 3 };
793             kx($data);
794              
795             my $list = ['one', 'two', 'three', 'four'];
796             kxd($list);
797              
798             =head1 DESCRIPTION
799              
800             Colorfully dump your data to make debugging variables easier. C
801             focuses on making your data human readable and easily parseable.
802              
803             =begin markdown
804              
805             # SCREENSHOTS
806              
807             image
808              
809             =end markdown
810              
811             =head1 METHODS
812              
813             =over 4
814              
815             =item B
816              
817             Debug print C<$var>.
818              
819             =item B
820              
821             Debug print C<$var> and C. This outputs file and line information.
822              
823             =back
824              
825             =head1 OPTIONS
826              
827             =over 4
828              
829             =item C<$Dump::Krumo::use_color = 1>
830              
831             Turn color on/off
832              
833             =item C<$Dump::Krumo::return_string = 0>
834              
835             Return a string instead of printing out
836              
837             =item C<$Dump::Krumo::indent_spaces = 2>
838              
839             Number of spaces to indent each level
840              
841             =item C<$Dump::Krumo::disable = 0>
842              
843             Disable all output from C. This allows you to leave all of your
844             debug print statements in your code, and disable them at runtime as needed.
845              
846             =item C<$Dump::Krumo::promote_bool = 1>
847              
848             Convert JSON::PP::Booleans to true/false instead of treating them as objects.
849              
850             =item C<$Dump::Krumo::stack_trace = 0>
851              
852             When C is called it will dump a full stack trace.
853              
854             =item C<$Dump::Krumo::COLORS>
855              
856             Reference to a hash of colors for each variable type. Update this and create
857             your own color scheme.
858              
859             =back
860              
861             =head1 SEE ALSO
862              
863             =over
864              
865             =item *
866             L
867              
868             =item *
869             L
870              
871             =item *
872             L
873              
874             =item *
875             L
876              
877             =back
878              
879             =head1 AUTHOR
880              
881             Scott Baker - L
882              
883             =cut
884              
885             1;
886              
887             # vim: tabstop=4 shiftwidth=4 noexpandtab autoindent softtabstop=4