Browse Source

ansi colour,textblock::table,textblock::frame, and repl fixes

master
Julian Noble 6 months ago
parent
commit
7fd49bb2e2
  1. 67
      src/modules/oolib-0.1.2.tm
  2. 402
      src/modules/punk/ansi-999999.0a1.0.tm
  3. 4
      src/modules/punk/basictelnet-999999.0a1.0.tm
  4. 48
      src/modules/punk/repl-0.1.tm
  5. 569
      src/modules/textblock-999999.0a1.0.tm

67
src/modules/oolib-0.1.1.tm → src/modules/oolib-0.1.2.tm

@ -2,13 +2,13 @@
# #
package provide oolib [namespace eval oolib { package provide oolib [namespace eval oolib {
variable version variable version
set version 0.1.1 set version 0.1.2
}] }]
namespace eval oolib { namespace eval oolib {
oo::class create collection { oo::class create collection {
variable o_data ;#dict variable o_data ;#dict
variable o_alias #variable o_alias
constructor {} { constructor {} {
set o_data [dict create] set o_data [dict create]
} }
@ -103,37 +103,38 @@ namespace eval oolib {
} }
} }
#review - see patternlib. Is the intention for aliases to be configurable independent of whether the target exists? #review - see patternlib. Is the intention for aliases to be configurable independent of whether the target exists?
method alias {newAlias existingKeyOrAlias} { #review - what is the point of alias anyway? - why slow down other operations when a variable can hold a keyname perfectly well?
if {[string is integer -strict $newAlias]} { #method alias {newAlias existingKeyOrAlias} {
error "[self object] collection key alias cannot be integer" # if {[string is integer -strict $newAlias]} {
} # error "[self object] collection key alias cannot be integer"
if {[string length $existingKeyOrAlias]} { # }
set o_alias($newAlias) $existingKeyOrAlias # if {[string length $existingKeyOrAlias]} {
} else { # set o_alias($newAlias) $existingKeyOrAlias
unset o_alias($newAlias) # } else {
} # unset o_alias($newAlias)
} # }
method aliases {{key ""}} { #}
if {[string length $key]} { #method aliases {{key ""}} {
set result [list] # if {[string length $key]} {
foreach {n v} [array get o_alias] { # set result [list]
if {$v eq $key} { # foreach {n v} [array get o_alias] {
lappend result $n $v # if {$v eq $key} {
} # lappend result $n $v
} # }
return $result # }
} else { # return $result
return [array get o_alias] # } else {
} # return [array get o_alias]
} # }
#if the supplied index is an alias, return the underlying key; else return the index supplied. #}
method realKey {idx} { ##if the supplied index is an alias, return the underlying key; else return the index supplied.
if {[catch {set o_alias($idx)} key]} { #method realKey {idx} {
return $idx # if {[catch {set o_alias($idx)} key]} {
} else { # return $idx
return $key # } else {
} # return $key
} # }
#}
method add {value key} { method add {value key} {
if {[string is integer -strict $key]} { if {[string is integer -strict $key]} {
error "[self object] collection key must not be an integer. Use another structure if integer keys required" error "[self object] collection key must not be an integer. Use another structure if integer keys required"

402
src/modules/punk/ansi-999999.0a1.0.tm

@ -106,7 +106,7 @@ namespace eval punk::ansi::class {
#overflow is a different concept - perhaps not particularly congruent with the idea of the textblock as a mini terminal emulator. #overflow is a different concept - perhaps not particularly congruent with the idea of the textblock as a mini terminal emulator.
#overflow effectively auto-expands the block(terminal?) width #overflow effectively auto-expands the block(terminal?) width
#overflow and wrap both being true won't make sense unless we implement a max_overflow concept #overflow and wrap both being true won't make sense unless we implement a max_overflow concept
set o_rendered [overtype::left -overflow 0 -wrap 1 -width $w -height $h -appendlines 1 "" [$o_ansistringobj get]] set o_rendered [overtype::renderspace -overflow 0 -wrap 1 -width $w -height $h -appendlines 1 "" [$o_ansistringobj get]]
if {$cksum eq "not-done"} { if {$cksum eq "not-done"} {
#if dimensions changed - the checksum won't have been done #if dimensions changed - the checksum won't have been done
set o_rendered_what [$o_ansistringobj checksum] set o_rendered_what [$o_ansistringobj checksum]
@ -129,7 +129,7 @@ namespace eval punk::ansi::class {
set o_dimensions $dimensions set o_dimensions $dimensions
set rendered [overtype::left -experimental {test_mode} -overflow 0 -wrap 1 -width $w -height $h -appendlines 1 "" [$o_ansistringobj get]] set rendered [overtype::renderspace -experimental {test_mode} -overflow 0 -wrap 1 -width $w -height $h -appendlines 1 "" [$o_ansistringobj get]]
return $rendered return $rendered
} }
method render_to_input_line {args} { method render_to_input_line {args} {
@ -175,7 +175,7 @@ namespace eval punk::ansi::class {
if {$opt_minus ne "0"} { if {$opt_minus ne "0"} {
set chunk [string range $chunk 0 end-$opt_minus] set chunk [string range $chunk 0 end-$opt_minus]
} }
set rendered [overtype::left -experimental {test_mode} -overflow 0 -wrap 1 -width $w -height $h -appendlines 1 "" $chunk] set rendered [overtype::renderspace -experimental {test_mode} -overflow 0 -wrap 1 -width $w -height $h -appendlines 1 "" $chunk]
set marker "" set marker ""
for {set i 1} {$i <= $w} {incr i} { for {set i 1} {$i <= $w} {incr i} {
if {$i % 10 == 0} { if {$i % 10 == 0} {
@ -190,13 +190,13 @@ namespace eval punk::ansi::class {
set xline [lindex $rlines $x]\n set xline [lindex $rlines $x]\n
set xlinev [ansistring VIEWSTYLE $xline] set xlinev [ansistring VIEWSTYLE $xline]
set xlinev [string map $maplf $xlinev] set xlinev [string map $maplf $xlinev]
set xlinedisplay [overtype::left -wrap 1 -width $w -height 1 "" $xlinev] set xlinedisplay [overtype::renderspace -wrap 1 -width $w -height 1 "" $xlinev]
::append rendered \n $xlinedisplay ::append rendered \n $xlinedisplay
set chunk [ansistring VIEWSTYLE $chunk] set chunk [ansistring VIEWSTYLE $chunk]
set chunk [string map $maplf $chunk] set chunk [string map $maplf $chunk]
#keep chunkdisplay narrower - leave at 80 or it will get unwieldy for larger image widths #keep chunkdisplay narrower - leave at 80 or it will get unwieldy for larger image widths
set chunkdisplay [overtype::left -wrap 1 -width 80 -height 1 "" $chunk] set chunkdisplay [overtype::renderspace -wrap 1 -width 80 -height 1 "" $chunk]
set renderheight [llength [split $rendered \n]] set renderheight [llength [split $rendered \n]]
set chunkdisplay_lines [split $chunkdisplay \n] set chunkdisplay_lines [split $chunkdisplay \n]
set chunkdisplay_tail [lrange $chunkdisplay_lines end-$renderheight end] set chunkdisplay_tail [lrange $chunkdisplay_lines end-$renderheight end]
@ -215,14 +215,87 @@ namespace eval punk::ansi::class {
method viewlines {} { method viewlines {} {
return [ansistring VIEW [$o_ansistringobj get]] return [ansistring VIEW [$o_ansistringobj get]]
} }
method viewcodes {} { method viewcodes {args} {
return [ansistring VIEWCODES [$o_ansistringobj get]] set defaults [list\
-lf 0\
-vt 0\
-width "auto"\
]
foreach {k v} $args {
switch -- $k {
-lf - -vt - -width {}
default {
error "viewcodes unrecognised option '$k'. Known options [dict keys $defaults]"
}
}
}
set opts [dict merge $defaults $args]
set opts_lf [dict get $opts -lf]
set opts_vt [dict get $opts -vt]
set opts_width [dict get $opts -width]
if {$opts_width eq ""} {
return [ansistring VIEWCODES -lf $opts_lf -vt $opts_vt [$o_ansistringobj get]]
} elseif {$opts_width eq "auto"} {
lassign [punk::console::get_size] _cols columns _rows rows
set displaycols [expr {$columns -4}] ;#review
return [overtype::renderspace -width $displaycols -wrap 1 "" [ansistring VIEWCODES -lf $opts_lf -vt $opts_vt [$o_ansistringobj get]]]
} elseif {[string is integer -strict $opts_width] && $opts_width > 0} {
return [overtype::renderspace -width $opts_width -wrap 1 "" [ansistring VIEWCODES -lf $opts_lf -vt $opts_vt [$o_ansistringobj get]]]
} else {
error "viewcodes unrecognised value for -width. Try auto or a positive integer"
}
}
method viewchars {args} {
set defaults [list\
-width "auto"\
]
foreach {k v} $args {
switch -- $k {
-width {}
default {
error "viewchars unrecognised option '$k'. Known options [dict keys $defaults]"
} }
method viewchars {} { }
}
set opts [dict merge $defaults $args]
set opts_width [dict get $opts -width]
if {$opts_width eq ""} {
return [punk::ansi::stripansiraw [$o_ansistringobj get]] return [punk::ansi::stripansiraw [$o_ansistringobj get]]
} elseif {$opts_width eq "auto"} {
lassign [punk::console::get_size] _cols columns _rows rows
set displaycols [expr {$columns -4}] ;#review
return [overtype::renderspace -width $displaycols -wrap 1 "" [punk::ansi::stripansiraw [$o_ansistringobj get]]]
} elseif {[string is integer -strict $opts_width] && $opts_width > 0} {
return [overtype::renderspace -width $opts_width -wrap 1 "" [punk::ansi::stripansiraw [$o_ansistringobj get]]]
} else {
error "viewchars unrecognised value for -width. Try auto or a positive integer"
}
}
method viewstyle {args} {
set defaults [list\
-width "auto"\
]
foreach {k v} $args {
switch -- $k {
-width {}
default {
error "viewstyle unrecognised option '$k'. Known options [dict keys $defaults]"
}
}
} }
method viewstyle {} { set opts [dict merge $defaults $args]
set opts_width [dict get $opts -width]
if {$opts_width eq ""} {
return [ansistring VIEWSTYLE [$o_ansistringobj get]] return [ansistring VIEWSTYLE [$o_ansistringobj get]]
} elseif {$opts_width eq "auto"} {
lassign [punk::console::get_size] _cols columns _rows rows
set displaycols [expr {$columns -4}] ;#review
return [overtype::renderspace -width $displaycols -wrap 1 "" [ansistring VIEWSTYLE [$o_ansistringobj get]]]
} elseif {[string is integer -strict $opts_width] && $opts_width > 0} {
return [overtype::renderspace -width $opts_width -wrap 1 "" [ansistring VIEWSTYLE [$o_ansistringobj get]]]
} else {
error "viewstyle unrecognised value for -width. Try auto or a positive integer"
}
} }
method append_noreturn {ansistring} { method append_noreturn {ansistring} {
$o_ansistringobj append $ansistring $o_ansistringobj append $ansistring
@ -456,7 +529,7 @@ namespace eval punk::ansi {
set ansidata [fcat -encoding $encoding $fname] set ansidata [fcat -encoding $encoding $fname]
set obj [punk::ansi::class::class_ansi new $ansidata] set obj [punk::ansi::class::class_ansi new $ansidata]
if {$test_mode} { if {$encoding eq "cp437"} {
set result [$obj rendertest $dimensions] set result [$obj rendertest $dimensions]
} else { } else {
set result [$obj render $dimensions] set result [$obj render $dimensions]
@ -600,7 +673,6 @@ namespace eval punk::ansi {
#[para]Alternate graphics modes will be stripped - exposing the raw characters as they appear without graphics mode. #[para]Alternate graphics modes will be stripped - exposing the raw characters as they appear without graphics mode.
#[para]ie instead of a horizontal line you may see: qqqqqq #[para]ie instead of a horizontal line you may see: qqqqqq
join [::punk::ansi::ta::split_at_codes $text] "" join [::punk::ansi::ta::split_at_codes $text] ""
} }
proc stripansi1 {text} { proc stripansi1 {text} {
@ -1061,7 +1133,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
dodgerblue2\ dodgerblue2\
green4\ green4\
springgreen4\ springgreen4\
turquise4\ turquoise4\
deepskyblue3\ deepskyblue3\
deepskyblue3\ deepskyblue3\
dodgerblue1\ dodgerblue1\
@ -1459,7 +1531,9 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
append out [a] append out [a]
return [string trimleft $out \n] return [string trimleft $out \n]
} }
proc colourtable_216_names {} {
#x6 is reasonable from a width (124 screen cols) and colour viewing perspective
proc colourtable_216_names {{cols 6}} {
set out "" set out ""
#use the reverse lookup dict - the original xterm_names list has duplicates - we want the disambiguated (potentially suffixed) names #use the reverse lookup dict - the original xterm_names list has duplicates - we want the disambiguated (potentially suffixed) names
variable TERM_colour_map_reverse variable TERM_colour_map_reverse
@ -1470,7 +1544,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
$t configure -show_seps 0 -show_edge 0 $t configure -show_seps 0 -show_edge 0
for {set i 16} {$i <=231} {incr i} { for {set i 16} {$i <=231} {incr i} {
set cname [dict get $TERM_colour_map_reverse $i] ;#use term-cname etc instead of term$i - may as well let a+ cache the call by name as the preferred? option set cname [dict get $TERM_colour_map_reverse $i] ;#use term-cname etc instead of term$i - may as well let a+ cache the call by name as the preferred? option
if {[llength $row]== 8} { if {[llength $row]== $cols} {
lappend rows $row lappend rows $row
set row [list] set row [list]
} }
@ -1492,6 +1566,112 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
append out [a] append out [a]
return [string trimleft $out \n] return [string trimleft $out \n]
} }
proc colourtable_term_pastel {} {
set out ""
set rows [list]
#see https://www.hackitu.de/termcolor256/
lappend rows {59 95 131 167 174 181 188}
lappend rows {59 95 131 173 180 187 188}
lappend rows {59 95 137 179 186 187 188}
lappend rows {59 101 143 185 186 187 188}
lappend rows {59 65 107 149 186 187 188}
lappend rows {59 65 71 113 150 187 188}
lappend rows {59 65 71 77 114 151 188}
lappend rows {59 65 71 78 115 152 188}
lappend rows {59 65 72 79 116 152 188}
lappend rows {59 66 73 80 116 152 188}
lappend rows {59 60 67 74 116 152 188}
lappend rows {59 60 61 68 110 152 188}
lappend rows {59 60 61 62 104 146 188}
lappend rows {59 60 61 98 140 182 188}
lappend rows {59 60 97 134 176 182 188}
lappend rows {59 96 133 170 176 182 188}
lappend rows {59 95 132 169 176 182 188}
lappend rows {59 95 131 168 175 182 188}
set t [textblock::class::table new]
$t configure -show_seps 0 -show_edge 0
set fg "web-black"
foreach r $rows {
set rowcells [list]
foreach cnum $r {
lappend rowcells "[a+ $fg Term-$cnum][format %3s $cnum] "
}
$t add_row $rowcells
}
append out [$t print]
$t destroy
set pastel8 [list 102 138 144 108 109 103 139 145]
set p8 ""
foreach cnum $pastel8 {
append p8 "[a+ $fg Term-$cnum][format %3s $cnum] "
}
append p8 [a]\n
append out \n $p8
return $out
}
proc colourtable_term_rainbow {} {
set out ""
set rows [list]
set fgwhite [list 16 52 88 124 160 22 17 18 19 20 21 57 56 93 55 92 54 91 53 90 89 126 88 125 124 160]
#see https://www.hackitu.de/termcolor256/
lappend rows {16 52 88 124 160 196 203 210 217 224 231}
lappend rows {16 52 88 124 160 202 209 216 223 230 231}
lappend rows {16 52 88 124 166 208 215 222 229 230 231}
lappend rows {16 52 88 130 172 214 221 228 229 230 231}
lappend rows {16 52 94 136 178 220 227 227 228 230 231}
lappend rows {16 58 100 142 184 226 227 228 228 230 231}
lappend rows {16 22 64 106 148 190 227 228 229 230 231}
lappend rows {16 22 28 70 112 154 191 228 229 230 231}
lappend rows {16 22 28 34 76 118 155 192 229 230 231}
lappend rows {16 22 28 34 40 82 119 156 193 230 231}
lappend rows {16 22 28 34 40 46 83 120 157 194 231}
lappend rows {16 22 28 34 40 47 84 121 158 195 231}
lappend rows {16 22 28 34 41 48 85 122 158 195 231}
lappend rows {16 22 28 35 42 49 86 123 159 195 231}
lappend rows {16 22 29 36 43 50 87 123 159 195 231}
lappend rows {16 23 30 37 44 51 87 123 159 195 231}
lappend rows {16 17 24 31 38 45 87 123 159 195 231}
lappend rows {16 17 18 25 32 39 81 123 159 195 231}
lappend rows {16 17 18 19 26 33 75 117 159 195 231}
lappend rows {16 17 18 19 20 27 69 111 153 195 231}
lappend rows {16 17 18 19 20 21 63 105 147 189 231}
lappend rows {16 17 18 19 20 57 99 141 183 225 231}
lappend rows {16 17 18 19 56 93 135 177 219 225 231}
lappend rows {16 17 18 55 92 129 171 213 219 225 231}
lappend rows {16 17 54 91 128 165 207 213 219 225 231}
lappend rows {16 53 90 127 164 201 207 213 219 225 231}
lappend rows {16 52 89 126 163 200 207 213 219 225 231}
lappend rows {16 52 88 125 162 199 206 213 219 225 231}
lappend rows {16 52 88 124 161 198 205 212 219 225 231}
lappend rows {16 52 88 124 160 197 204 211 218 225 231}
set t [textblock::class::table new]
$t configure -show_seps 0 -show_edge 0
foreach r $rows {
set rowcells [list]
foreach cnum $r {
if {$cnum in $fgwhite} {
set fg "web-white"
} else {
set fg "web-black"
}
lappend rowcells "[a+ $fg Term-$cnum][format %3s $cnum] "
}
$t add_row $rowcells
}
append out [$t print]
$t destroy
return $out
}
#24 greys of 256 #24 greys of 256
proc colourblock_24 {} { proc colourblock_24 {} {
set out "" set out ""
@ -1548,7 +1728,8 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
# $WEB_colour_map_gray\ # $WEB_colour_map_gray\
#] #]
proc colourtable_web {{groups *}} { proc colourtable_web {{groups *}} {
set all_groupnames [list basic pink red orange yellow brown purple blue cyan green white gray] #set all_groupnames [list basic pink red orange yellow brown purple blue cyan green white gray]
set all_groupnames [list basic brown yellow red pink orange purple blue cyan green white gray]
switch -- $groups { switch -- $groups {
"" - * { "" - * {
set show_groups $all_groupnames set show_groups $all_groupnames
@ -1696,7 +1877,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
set map2 [colourmap2 $bgname] set map2 [colourmap2 $bgname]
set map2 [overtype::centre -transparent 1 $map2 "[a black $bgname]High-intensity colours[a]"] set map2 [overtype::centre -transparent 1 $map2 "[a black $bgname]High-intensity colours[a]"]
append out [textblock::join $indent [textblock::join $map1 $map2]] \n append out [textblock::join $indent [textblock::join $map1 $map2]] \n
append out "[a+ web-white]216 colours of 256 terminal colours (To see names, use: a? term)[a]" \n append out "[a+ web-white]216 colours of 256 terminal colours (To see names, use: a? term ?pastel? ?rainbow?)[a]" \n
append out [textblock::join $indent [colourblock_216]] \n append out [textblock::join $indent [colourblock_216]] \n
append out "[a+ web-white]24 Greyscale colours[a]" \n append out "[a+ web-white]24 Greyscale colours[a]" \n
append out [textblock::join $indent [colourblock_24]] \n append out [textblock::join $indent [colourblock_24]] \n
@ -1709,6 +1890,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
#dict set WEB_colour_map mediumvioletred 199-21-133 ;# #C71585 #dict set WEB_colour_map mediumvioletred 199-21-133 ;# #C71585
append out [textblock::join $indent "Example: \[a+ rgb-199-21-133\]text\[a] -> [a+ rgb-199-21-133]text[a]"] \n append out [textblock::join $indent "Example: \[a+ rgb-199-21-133\]text\[a] -> [a+ rgb-199-21-133]text[a]"] \n
append out [textblock::join $indent "Example: \[a+ Rgb#C71585\]text\[a] -> [a+ Rgb#C71585]text[a]"] \n append out [textblock::join $indent "Example: \[a+ Rgb#C71585\]text\[a] -> [a+ Rgb#C71585]text[a]"] \n
append out [textblock::join $indent "Examine a sequence: a? bold rgb-46-139-87 Rgb#C71585 "] \n
append out \n append out \n
append out "[a+ web-white]Web colours[a]" \n append out "[a+ web-white]Web colours[a]" \n
append out [textblock::join $indent "To see all names use: a? web"] \n append out [textblock::join $indent "To see all names use: a? web"] \n
@ -1730,12 +1912,34 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
} else { } else {
switch -- [lindex $args 0] { switch -- [lindex $args 0] {
term { term {
set termargs [lrange $args 1 end]
foreach ta $termargs {
switch -- $ta {
pastel - rainbow {}
default {error "unrecognised term option '$ta'. Known values: pastel rainbow"}
}
}
set out "16 basic colours\n" set out "16 basic colours\n"
append out [colourtable_16_names] \n append out [colourtable_16_names] \n
append out "216 colours\n" append out "216 colours\n"
append out [colourtable_216_names] \n append out [colourtable_216_names] \n
append out "24 greyscale colours\n" append out "24 greyscale colours\n"
append out [colourtable_24_names] append out [colourtable_24_names]
foreach ta $termargs {
switch -- $ta {
pastel {
append out \n
append out "Pastel Colour Space (punk::ansi::colourtable_term_pastel)\n"
append out [colourtable_term_pastel]
}
rainbow {
append out \n
append out "Rainbow Colours (punk::ansi::colourtable_term_rainbow)\n"
append out [colourtable_term_rainbow]
}
}
}
append out "\nNote: The 256 term colours especially 0-15 may be altered by terminal pallette settings or ansi OSC 4 codes, so specific RGB values are unavailable"
return $out return $out
} }
web { web {
@ -1748,20 +1952,130 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
return $out return $out
} }
} }
set result [list]
set map [dict merge $SGR_setting_map $SGR_colour_map] variable WEB_colour_map
set rmap [lreverse $map] variable X11_colour_map
variable TERM_colour_map
variable TERM_colour_map_reverse
variable SGR_map
set t [textblock::class::table new]
$t configure -show_edge 0 -show_seps 1 -show_header 0
set resultlist [list]
foreach i $args { foreach i $args {
if {[string is integer -strict $i]} { set f4 [string range $i 0 3]
if {[dict exists $rmap $i]} { set s [a+ $i]sample
lappend result $i [dict get $rmap $i] switch -- $f4 {
web- - Web- - WEB- {
set tail [string tolower [string trim [string range $i 4 end] -]]
if {[dict exists $WEB_colour_map $tail]} {
set dec [dict get $WEB_colour_map $tail]
set hex [colour_dec2hex $dec]
set descr "$hex $dec"
} else {
set descr "UNKNOWN colour for web"
}
$t add_row [list $i $descr $s [ansistring VIEW $s]]
}
term - Term {
set tail [string trim [string range $i 4 end] -]
if {[string is integer -strict $tail]} {
if {$tail < 256} {
set descr "[dict get $TERM_colour_map_reverse $tail]"
} else {
set descr "Invalid (> 255)"
}
} else {
set tail [string tolower $tail]
if {[dict exists $TERM_colour_map $tail]} {
set descr [dict get $TERM_colour_map $tail]
} else {
set descr "UNKNOWN colour for term"
}
}
$t add_row [list $i $descr $s [ansistring VIEW $s]]
}
x11- - X11- {
set tail [string tolower [string trim [string range $i 4 end] -]]
if {[dict exists $X11_colour_map $tail]} {
set dec [dict get $X11_colour_map $tail]
set hex [colour_dec2hex $dec]
set descr "$hex $dec"
} else {
set descr "UNKNOWN colour for x11"
}
$t add_row [list $i $descr $s [ansistring VIEW $s]]
}
rgb- - Rgb- - RGB- -
rgb0 - rgb1 - rgb2 - rgb3 - rgb4 - rgb5 - rgb6 - rgb7 - rgb8 - rgb9 -
Rgb0 - Rgb1 - Rgb2 - Rgb3 - Rgb4 - Rgb5 - Rgb6 - Rgb7 - Rgb8 - Rgb9 -
RGB0 - RGB1 - RGB2 - RGB3 - RGB4 - RGB5 - RGB6 - RGB7 - RGB8 - RGB9 -
rgb# - Rgb# - RGB# {
if {[string index $i 3] eq "#"} {
set tail [string range $i 4 end]
set hex $tail
set dec [colour_hex2dec $hex]
set info $dec ;#show opposite type as first line of info col
} else {
set tail [string trim [string range $i 3 end] -]
set dec $tail
set hex [colour_dec2hex $dec]
set info $hex
}
set webcolours_i [lsearch -all $WEB_colour_map $dec]
set webcolours [list]
foreach ci $webcolours_i {
lappend webcolours [lindex $WEB_colour_map $ci-1]
} }
set x11colours [list]
set x11colours_i [lsearch -all $X11_colour_map $dec]
foreach ci $x11colours_i {
set c [lindex $X11_colour_map $ci-1]
if {$c ni $webcolours} {
lappend x11colours $c
}
}
foreach c $webcolours {
append info \n web-$c
}
foreach c $x11colours {
append info \n x11-$c
}
$t add_row [list $i "$info" $s [ansistring VIEW $s]]
}
default {
if {[string is integer -strict $i]} {
set rmap [lreverse $SGR_map]
$t add_row [list $i "SGR [dict get $rmap $i]" $s [ansistring VIEW $s]]
} else {
if {[dict exists $SGR_map $i]} {
$t add_row [list $i "SGR [dict get $SGR_map $i]" $s [ansistring VIEW $s]]
} else { } else {
if {[dict exists $map $i]} { $t add_row [list $i UNKNOWN $s [ansistring VIEW $s]]
lappend result $i [dict get $map $i]
} }
} }
} }
}
}
set ansi [a+ {*}$args]
set s ${ansi}sample
set merged [punk::ansi::codetype::sgr_merge_singles [list $ansi]]
set s2 ${merged}sample
#lappend resultlist "RESULT: [a+ {*}$args]sample[a]"
$t add_row [list RESULT "" $s [ansistring VIEW $s]]
if {$ansi ne $merged} {
if {[string length $merged] < [string length $ansi]} {
#only refer to redundancies if shorter - merge may reorder - REVIEW
set warning "[a+ web-red Web-yellow]REDUNDANCIES FOUND"
} else {
set warning ""
}
$t add_row [list MERGED $warning $s2 [ansistring VIEW $s2]]
}
set result [$t print]
$t destroy
return $result return $result
} }
} }
@ -1788,13 +2102,33 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
set sgr_cache [dict create] set sgr_cache [dict create]
return "sgr_cache cleared" return "sgr_cache cleared"
} }
if {[catch {
set termwidth [dict get [punk::console::get_size] columns]
} errM]} {
set termwidth 80
}
set termwidth [expr [$termwidth -3]]
set out "" set out ""
set linelen 0
set RST [a] set RST [a]
set lines [list]
set line ""
#todo - terminal width? table? #todo - terminal width? table?
dict for {key ansi} $sgr_cache { dict for {key ansi} $sgr_cache {
append out "$ansi$key$RST " set thislen [expr {[string length $key]+1}]
if {$linelen + $thislen >= $termwidth-1} {
lappend lines $line
set line "$ansi$key$RST "
set linelen $thislen
} else {
append line "$ansi$key$RST "
incr linelen $thislen
}
}
if {[string length $line]} {
lappend lines $line
} }
return $out\n return [join $lines \n]
} }
proc a+ {args} { proc a+ {args} {
@ -1922,7 +2256,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
switch -- $i { switch -- $i {
Brightblack {lappend t 100} Brightblack {lappend t 100}
Brightred {lappend t 101} Brightred {lappend t 101}
Brightgreen {lappend t 101} Brightgreen {lappend t 102}
Brightyellow {lappend t 103} Brightyellow {lappend t 103}
Brightblue {lappend t 104} Brightblue {lappend t 104}
Brightpurple {lappend t 105} Brightpurple {lappend t 105}
@ -2154,7 +2488,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
switch -- $i { switch -- $i {
Brightblack {lappend t 100} Brightblack {lappend t 100}
Brightred {lappend t 101} Brightred {lappend t 101}
Brightgreen {lappend t 101} Brightgreen {lappend t 102}
Brightyellow {lappend t 103} Brightyellow {lappend t 103}
Brightblue {lappend t 104} Brightblue {lappend t 104}
Brightpurple {lappend t 105} Brightpurple {lappend t 105}
@ -2953,10 +3287,11 @@ namespace eval punk::ansi {
set defaults [dict create\ set defaults [dict create\
-filter_fg 0\ -filter_fg 0\
-filter_bg 0\ -filter_bg 0\
-filter_reset 0\
] ]
dict for {k v} $args { dict for {k v} $args {
switch -- $k { switch -- $k {
-filter_fg - -filter_bg {} -filter_fg - -filter_bg - -filter_reset {}
default { default {
error "sgr_merge unknown option '$k'. Known options [dict keys $defaults]" error "sgr_merge unknown option '$k'. Known options [dict keys $defaults]"
} }
@ -3015,9 +3350,11 @@ namespace eval punk::ansi {
set codeint [string trimleft [lindex $paramsplit 0] 0] set codeint [string trimleft [lindex $paramsplit 0] 0]
switch -- $codeint { switch -- $codeint {
"" - 0 { "" - 0 {
if {![dict get $opts -filter_reset]} {
set codestate $codestate_initial set codestate $codestate_initial
set did_reset 1 set did_reset 1
} }
}
1 { 1 {
#bold #bold
if {[llength $paramsplit] == 1} { if {[llength $paramsplit] == 1} {
@ -3086,7 +3423,7 @@ namespace eval punk::ansi {
} }
21 { 21 {
#ECMA-48 double underline - some terminals use as not-bold. For now we won't support that. #ECMA-48 double underline - some terminals use as not-bold. For now we won't support that.
dict set doubleunderline 21 dict set codestate doubleunderline 21
} }
22 { 22 {
#normal intensity #normal intensity
@ -4866,9 +5203,12 @@ namespace eval punk::ansi::ansistring {
if {$opt_cr} { if {$opt_cr} {
dict set visuals_opt CR [list \x0d \u240d] dict set visuals_opt CR [list \x0d \u240d]
} }
if {$opt_lf} { if {$opt_lf == 1} {
dict set visuals_opt LF [list \x0a \u240a] dict set visuals_opt LF [list \x0a \u240a]
} }
if {$opt_lf == 2} {
dict set visuals_opt LF [list \x0a \u240a\n]
}
if {$opt_vt} { if {$opt_vt} {
dict set visuals_opt VT [list \x0b \u240b] dict set visuals_opt VT [list \x0b \u240b]
} }

4
src/modules/punk/basictelnet-999999.0a1.0.tm

@ -528,7 +528,7 @@ namespace eval punk::basictelnet {
# -- --- --- --- # -- --- --- ---
set tailinfo "" set tailinfo ""
if {[string length $nextwaiting]} { if {[string length $nextwaiting]} {
set waitingdisplay [overtype::left -wrap 1 -width 77 -height 1 "" [ansistring VIEW -lf 1 -vt 1 $nextwaiting]] set waitingdisplay [overtype::renderspace -wrap 1 -width 77 -height 1 "" [ansistring VIEW -lf 1 -vt 1 $nextwaiting]]
set tailinfo "[a+ red]from waiting:\n $waitingdisplay[a]" set tailinfo "[a+ red]from waiting:\n $waitingdisplay[a]"
} }
::punk::basictelnet::add_debug "[a+ Yellow black]from stdin sending: [ansistring VIEW -lf 1 -vt 1 $chunk][a]\n$tailinfo\n" stdin $sock ::punk::basictelnet::add_debug "[a+ Yellow black]from stdin sending: [ansistring VIEW -lf 1 -vt 1 $chunk][a]\n$tailinfo\n" stdin $sock
@ -626,7 +626,7 @@ namespace eval punk::basictelnet {
#set rawview [ansistring VIEW -lf 1 -vt 1 [encoding convertfrom $encoding_guess $data]] #set rawview [ansistring VIEW -lf 1 -vt 1 [encoding convertfrom $encoding_guess $data]]
set rawview [ansistring VIEW -lf 1 -vt 1 $data] set rawview [ansistring VIEW -lf 1 -vt 1 $data]
#set viewblock [overtype::left -wrap 1 -width 78 -height 4 "" $rawview] #set viewblock [overtype::left -wrap 1 -width 78 -height 4 "" $rawview]
set viewblock [overtype::left -experimental test_mode -wrap 1 -width 78 -height 4 "" $rawview] set viewblock [overtype::renderspace -experimental test_mode -wrap 1 -width 78 -height 4 "" $rawview]
set lines [split $viewblock \n] set lines [split $viewblock \n]
if {[llength $lines] > 4} { if {[llength $lines] > 4} {
append debug_info [join [list {*}[lrange $lines 0 1] "...<[expr {[llength $lines] -4}] lines undisplayed>..." {*}[lrange $lines end-1 end]] \n] append debug_info [join [list {*}[lrange $lines 0 1] "...<[expr {[llength $lines] -4}] lines undisplayed>..." {*}[lrange $lines end-1 end]] \n]

48
src/modules/punk/repl-0.1.tm

@ -658,6 +658,7 @@ proc repl::start {inchan args} {
variable editbuf variable editbuf
variable editbuf_list ;#command history variable editbuf_list ;#command history
variable editbuf_linenum_submitted variable editbuf_linenum_submitted
variable editbuf_active_index
# --- # ---
variable running variable running
@ -681,6 +682,7 @@ proc repl::start {inchan args} {
set editbuf [punk::repl::class::class_editbuf new {}] set editbuf [punk::repl::class::class_editbuf new {}]
lappend editbuf_list $editbuf ;#current editbuf is always in the history lappend editbuf_list $editbuf ;#current editbuf is always in the history
set editbuf_linenum_submitted 0 set editbuf_linenum_submitted 0
set editbuf_active_index 0
# --- # ---
if {$::punk::console::ansi_wanted == 2} { if {$::punk::console::ansi_wanted == 2} {
@ -1050,6 +1052,11 @@ namespace eval repl {
} }
namespace eval punk::repl::class { namespace eval punk::repl::class {
oo::class create class_bufman {
}
#multiline editing buffer #multiline editing buffer
oo::class create class_editbuf { oo::class create class_editbuf {
variable o_context variable o_context
@ -1157,7 +1164,7 @@ namespace eval punk::repl::class {
append debug \n $mergedinfo append debug \n $mergedinfo
append debug \n "input:[ansistring VIEW -lf 1 -vt 1 $new0] before row:$o_cursor_row after row: $result_row before col:$o_cursor_col after col:$result_col" append debug \n "input:[ansistring VIEW -lf 1 -vt 1 $new0] before row:$o_cursor_row after row: $result_row before col:$o_cursor_col after col:$result_col"
package require textblock package require textblock
set debug [textblock::frame $debug] set debug [textblock::frame -buildcache 0 $debug]
catch {punk::console::move_emitblock_return $debug_first_row 1 $debug} catch {punk::console::move_emitblock_return $debug_first_row 1 $debug}
# -- --- --- --- --- --- # -- --- --- --- --- ---
@ -1222,7 +1229,7 @@ namespace eval punk::repl::class {
append debug \n $mergedinfo append debug \n $mergedinfo
append debug \n "input:[ansistring VIEW -lf 1 -vt 1 $p]" append debug \n "input:[ansistring VIEW -lf 1 -vt 1 $p]"
package require textblock package require textblock
set debug [textblock::frame $debug] set debug [textblock::frame -buildcache 0 $debug]
#catch {punk::console::move_emitblock_return [expr {$debug_first_row + ($i * 6)}] 1 $debug} #catch {punk::console::move_emitblock_return [expr {$debug_first_row + ($i * 6)}] 1 $debug}
set result [dict get $mergedinfo result] set result [dict get $mergedinfo result]
@ -1682,9 +1689,14 @@ proc repl::repl_handler {inputchan prompt_config} {
} }
set in_repl_handler [list] set in_repl_handler [list]
} }
proc repl::editbuf {args} { proc repl::editbuf {index args} {
variable editbuf variable editbuf_list
set editbuf [lindex $editbuf_list $index]
if {$editbuf ne ""} {
$editbuf {*}$args $editbuf {*}$args
} else {
return "No such index in editbuf list"
}
} }
interp alias {} editbuf {} ::repl::editbuf interp alias {} editbuf {} ::repl::editbuf
proc repl::repl_process_data {inputchan type chunk stdinlines prompt_config} { proc repl::repl_process_data {inputchan type chunk stdinlines prompt_config} {
@ -1873,11 +1885,11 @@ proc repl::repl_process_data {inputchan type chunk stdinlines prompt_config} {
set info [list_as_lines $lines] set info [list_as_lines $lines]
} }
} errM]} { } errM]} {
set info [textblock::frame -title "[a red]error[a]" $errM] set info [textblock::frame -buildcache 0 -title "[a red]error[a]" $errM]
} else { } else {
set info [textblock::frame -ansiborder [a+ green bold] -title "[a cyan]debugview_raw[a]" $info] set info [textblock::frame -buildcache 0 -ansiborder [a+ green bold] -title "[a cyan]debugview_raw[a]" $info]
} }
set debug_width [textblock::width $info] set debug_width [textblock::widthtopline $info]
set spacepatch [textblock::block $debug_width 2 " "] set spacepatch [textblock::block $debug_width 2 " "]
puts -nonewline [punk::ansi::cursor_off] puts -nonewline [punk::ansi::cursor_off]
#use non cursorsave versions - cursor save/restore will interfere with any concurrent ansi rendering that uses save/restore - because save/restore is a single item, not a stack. #use non cursorsave versions - cursor save/restore will interfere with any concurrent ansi rendering that uses save/restore - because save/restore is a single item, not a stack.
@ -1895,16 +1907,16 @@ proc repl::repl_process_data {inputchan type chunk stdinlines prompt_config} {
set info [list_as_lines $lines] set info [list_as_lines $lines]
} }
} editbuf_error]} { } editbuf_error]} {
set info [textblock::frame -title "[a red]error[a]" "$editbuf_error\n$::errorInfo"] set info [textblock::frame -buildcache 0 -title "[a red]error[a]" "$editbuf_error\n$::errorInfo"]
} else { } else {
set title "[a cyan]editbuf lines [$editbuf linecount][a]" set title "[a cyan]editbuf [expr {[llength $editbuf_list]-1}] lines [$editbuf linecount][a]"
append title "[a+ yellow bold] col:[format %3s [$editbuf cursor_column]] row:[$editbuf cursor_row][a]" append title "[a+ yellow bold] col:[format %3s [$editbuf cursor_column]] row:[$editbuf cursor_row][a]"
set row1 " lastchar:[ansistring VIEW -lf 1 [$editbuf last_char]] lastgrapheme:[ansistring VIEW -lf 1 [$editbuf last_grapheme]]" set row1 " lastchar:[ansistring VIEW -lf 1 [$editbuf last_char]] lastgrapheme:[ansistring VIEW -lf 1 [$editbuf last_grapheme]]"
set row2 " lastansi:[ansistring VIEW -lf 1 [$editbuf last_ansi]]" set row2 " lastansi:[ansistring VIEW -lf 1 [$editbuf last_ansi]]"
set info [a+ green bold]$row1\n$row2[a]\n$info set info [a+ green bold]$row1\n$row2[a]\n$info
set info [textblock::frame -ansiborder [a+ green bold] -title $title $info] set info [textblock::frame -buildcache 0 -ansiborder [a+ green bold] -title $title $info]
} }
set editbuf_width [textblock::width $info] set editbuf_width [textblock::widthtopline $info]
set spacepatch [textblock::block $editbuf_width 2 " "] set spacepatch [textblock::block $editbuf_width 2 " "]
set editbuf_offset [expr {$consolewidth - $debug_width - $editbuf_width - 2}] set editbuf_offset [expr {$consolewidth - $debug_width - $editbuf_width - 2}]
@ -2475,7 +2487,19 @@ proc repl::repl_process_data {inputchan type chunk stdinlines prompt_config} {
set commandstr "" set commandstr ""
#catch {puts stderr "zz2---->[rep $::arglej]"} #catch {puts stderr "zz2---->[rep $::arglej]"}
set lines [$editbuf lines]
set buf_has_data 0
foreach ln $lines {
if {[string trim $ln] ne ""} {
set buf_has_data 1
}
}
if {$buf_has_data} {
set editbufnext [punk::repl::class::class_editbuf new {}]
lappend editbuf_list $editbufnext
set editbuf_linenum_submitted 0
set editbuf $editbufnext
}
#editbuf #editbuf
} else { } else {

569
src/modules/textblock-999999.0a1.0.tm

@ -231,6 +231,7 @@ namespace eval textblock {
variable o_opts_table_defaults variable o_opts_table_defaults
variable o_opts_column_defaults variable o_opts_column_defaults
variable o_opts_row_defaults variable o_opts_row_defaults
variable TSUB
constructor {args} { constructor {args} {
#*** !doctools #*** !doctools
#[call class::table [method constructor] [arg args]] #[call class::table [method constructor] [arg args]]
@ -258,6 +259,7 @@ namespace eval textblock {
set o_rowdefs [dict create] ;#user requested row data e.g -minheight -maxheight set o_rowdefs [dict create] ;#user requested row data e.g -minheight -maxheight
set o_rowstates [dict create] ;#actual row data such as -minheight and -maxheight detected from supplied row data set o_rowstates [dict create] ;#actual row data such as -minheight and -maxheight detected from supplied row data
set TSUB \uF111 ;#should be BMP PUA code to show as either replacement char or nerdfont glyph. See FSUB for comments regarding choices.
} }
method Get_seps {} { method Get_seps {} {
set requested_seps [dict get $o_opts_table -show_seps] set requested_seps [dict get $o_opts_table -show_seps]
@ -594,6 +596,8 @@ namespace eval textblock {
-ansireset "\uFFEF"\ -ansireset "\uFFEF"\
-minwidth ""\ -minwidth ""\
-maxwidth ""\ -maxwidth ""\
-blockalign centre\
-textalign left\
] ]
#initialise -ansireset with replacement char so we can keep in appropriate dict position for initial configure and then treat as read-only #initialise -ansireset with replacement char so we can keep in appropriate dict position for initial configure and then treat as read-only
set o_opts_column_defaults $defaults set o_opts_column_defaults $defaults
@ -769,6 +773,16 @@ namespace eval textblock {
error "textblock::table::configure_column -ansireset is read-only. It is present only to prevent unwanted colourised output in configure commands" error "textblock::table::configure_column -ansireset is read-only. It is present only to prevent unwanted colourised output in configure commands"
} }
} }
-blockalign - -textalign {
switch -- $v {
left - right {
lappend checked_opts $k $v
}
centre - centre {
lappend checked_opts $k centre
}
}
}
default { default {
lappend checked_opts $k $v lappend checked_opts $k $v
} }
@ -1269,11 +1283,16 @@ namespace eval textblock {
set botseps_v [dict get $sep_elements_vertical bottom$opt_posn] set botseps_v [dict get $sep_elements_vertical bottom$opt_posn]
set onlyseps_v [dict get $sep_elements_vertical only$opt_posn] set onlyseps_v [dict get $sep_elements_vertical only$opt_posn]
#top should work for all header rows regarding vseps - as we only use it to reduce the box elements, and lower headers have same except for tlc which isn't present anyway
set headerseps_v [dict get $sep_elements_vertical top$opt_posn] set headerseps_v [dict get $sep_elements_vertical top$opt_posn]
lassign [my Get_seps] _h show_seps_h _v show_seps_v lassign [my Get_seps] _h show_seps_h _v show_seps_v
set return_headerheight 0 set return_headerheight 0
set return_headerwidth 0 set return_headerwidth 0
set cidx [lindex [dict keys $o_columndefs] $index_expression]
set colwidth [my column_width $cidx]
set col_blockalign [dict get $o_columndefs $cidx -blockalign]
if {$do_show_header} { if {$do_show_header} {
#puts "boxlimitsinfo header $opt_posn: -- boxlimits $header_boxlimits -- boxmap $hdrmap" #puts "boxlimitsinfo header $opt_posn: -- boxlimits $header_boxlimits -- boxmap $hdrmap"
set ansibase_header [dict get $o_opts_table -ansibase_header] ;#merged to single during configure set ansibase_header [dict get $o_opts_table -ansibase_header] ;#merged to single during configure
@ -1284,9 +1303,7 @@ namespace eval textblock {
} else { } else {
set ansiborder_final $ansibase_header$ansiborder_header set ansiborder_final $ansibase_header$ansiborder_header
} }
set cidx [lindex [dict keys $o_columndefs] $index_expression]
set RST [punk::ansi::a] set RST [punk::ansi::a]
set colwidth [my column_width $cidx]
set hcell_line_blank [string repeat " " $colwidth] set hcell_line_blank [string repeat " " $colwidth]
set h 0 set h 0
@ -1304,6 +1321,8 @@ namespace eval textblock {
set column_width_cache [dict create] set column_width_cache [dict create]
#used for colspan-zero header frames
set framesub_map [list hl $TSUB vll $TSUB vlr $TSUB tlc $TSUB blc $TSUB trc $TSUB brc $TSUB] ;# a debug test
foreach header $header_list { foreach header $header_list {
set headerspans [dict get $all_colspans $h] set headerspans [dict get $all_colspans $h]
@ -1361,21 +1380,33 @@ namespace eval textblock {
} }
} }
#supporting wrapping in headers might be a step too difficult for little payoff.
#we would need a flag to enable/disable - plus language-based wrapping alg (see tcllib)
#The interaction with colspans and width calculations makes it likely to have a heavy performance impact and make the code even more complex.
#May be better to require user to pre-wrap as needed
##set hval [textblock::renderspace -width $colwidth -wrap 1 "" $hval]
#review - contentwidth of hval can be greater than $colwidth+2 - if so frame function will detect and frame_cache will not be used #review - contentwidth of hval can be greater than $colwidth+2 - if so frame function will detect and frame_cache will not be used
set header_cell_startspan [textblock::frame -usecache 1 -width [expr {$colwidth+2}] -type [dict get $ftypes header]\ #This ellipsis 0 makes a difference (unwanted ellipsis at rhs of header column that starts span)
# -width is always +2 - as the boxlimits take into account show_vseps and show_edge
set header_cell_startspan [textblock::frame -ellipsis 0 -usecache 1 -width [expr {$colwidth+2}] -type [dict get $ftypes header]\
-ansibase $ansibase_header -ansiborder $ansiborder_final\ -ansibase $ansibase_header -ansiborder $ansiborder_final\
-boxlimits $hlims -boxmap $startmap -joins $header_joins $hval\ -boxlimits $hlims -boxmap $startmap -joins $header_joins $hval\
] ]
#JMN
#puts "===>\n$header_cell_startspan\n<==="
set spanned_parts [list $header_cell_startspan] set spanned_parts [list $header_cell_startspan]
if {$this_span ne "1"} { if {$this_span ne "1"} {
#more parts to append #assert this_span == "all" or >1 ie a header that spans other columns
#therefore more parts to append
#set remaining_cols [lrange [dict keys $o_columndefs] $cidx end] #set remaining_cols [lrange [dict keys $o_columndefs] $cidx end]
set remaining_spans [lrange $headerspans $cidx+1 end] set remaining_spans [lrange $headerspans $cidx+1 end]
#puts ">> remaining_spans: $remaining_spans" #puts ">> remaining_spans: $remaining_spans"
set spancol [expr {$cidx + 1}] set spancol [expr {$cidx + 1}]
set h_lines [lrepeat $rowh ""] set h_lines [lrepeat $rowh ""]
set hcell_blank [::join $h_lines \n] set hcell_blank [::join $h_lines \n] ;#todo - just use -height option of frame?
@ -1441,22 +1472,26 @@ namespace eval textblock {
dict set column_width_cache $spancol headerwidth $hwidth dict set column_width_cache $spancol headerwidth $hwidth
} else { } else {
set bwidth [dict get $column_width_cache $spancol bodywidth] set bwidth [dict get $column_width_cache $spancol bodywidth]
set hwidth [dict get $column_width_cache $spancol headerwidth]
} }
#subsequent headers may also span columns - so we will get too wide if we use the headers directly
#but if we don't take into account header widths - they may get truncated.
if {$next_posn eq "right"} {
#This is an unintuitive edge case - review #This is an unintuitive edge case - review
#spans at tail end are too long when edges are shown if we use bwidth+1 (vlr extends right beyond table) #spans at tail end are too long when edges are shown if we use bwidth (vlr extends right beyond table)
#spans at tail end are too short if edges are hidden and we use bwidth (short lower horizontal bar) #spans at tail end are too short if edges are hidden and we use bwidth-1 (short lower horizontal bar)
if {![dict get $o_opts_table -show_edge]} { #test JMN
set spanwidth [expr {$bwidth+1}] if {$next_posn eq "right" && [dict get $o_opts_table -show_edge]} {
set spanwidth [expr {$bwidth -1}]
} else { } else {
set spanwidth $bwidth set spanwidth [expr {$bwidth }]
}
} else {
set spanwidth [expr {$bwidth+1}]
} }
set header_cell [textblock::frame -width $spanwidth -type [dict get $ftypes header]\ #JMN - review
set framewidth $spanwidth
incr framewidth 1
set header_cell [textblock::frame -ellipsis 0 -width $framewidth -type [dict get $ftypes header]\
-ansibase $ansibase_header -ansiborder $ansiborder_final\ -ansibase $ansibase_header -ansiborder $ansiborder_final\
-boxlimits $hlims -boxmap $this_span_map -joins $header_joins $hcell_blank\ -boxlimits $hlims -boxmap $this_span_map -joins $header_joins $hcell_blank\
] ]
@ -1485,8 +1520,9 @@ namespace eval textblock {
set spacemap [list hl " " vl " " tlc " " blc " " trc " " brc " "] set spacemap [list hl " " vl " " tlc " " blc " " trc " " brc " "]
#set spacemap [list hl "\uFFFF" vl "\uFFFF" tlc "\uFFFF" blc "\uFFFF" trc "\uFFFF " brc "\uFFFF"] ;# a debug test #set spacemap [list hl "\uFFFF" vl "\uFFFF" tlc "\uFFFF" blc "\uFFFF" trc "\uFFFF " brc "\uFFFF"] ;# a debug test
#-usecache 1 ok #-usecache 1 ok
set hblock [textblock::frame -type $spacemap -boxlimits $hlims -ansibase $ansibase_header $hval] set hblock [textblock::frame -ellipsis 0 -type $spacemap -boxlimits $hlims -ansibase $ansibase_header $hval]
set spanned_frame [overtype::left -experimental test_mode -transparent 1 $spanned_frame $hblock] #set spanned_frame [overtype::renderspace -experimental test_mode -transparent 1 $spanned_frame $hblock]
set spanned_frame [overtype::block -blockalign left -transparent 1 $spanned_frame $hblock]
} }
@ -1495,49 +1531,50 @@ namespace eval textblock {
} else { } else {
#zero span header #zero span header
#JMN
if 0 {
#old version - sort of works
set h_lines [lrepeat $rowh ""]
set hcell_blank [join $h_lines \n]
set spacemap [list hl " " vl " " tlc " " blc " " trc " " brc " "]
set spacemap [list hl "\uFFFF" vl "\uFFFF" tlc "\uFFFF" blc "\uFFFF" trc "\uFFFF " brc "\uFFFF"] ;# a debug test
set header_frame [textblock::frame -width 0 -type [dict get $ftypes header]\
-ansibase $ansibase_header \
-boxlimits $hlims -boxmap $spacemap $hcell_blank\
]
append part_header $header_frame\n
} else {
#test version
set hw1 [dict get $o_columnstates $cidx maxwidthheaderseen] ;#headers may be masked by spans, or empty - width may depend more on spans than headers in current column
set hw2 [textblock::width $part_header] ;#widest so far
set hw3 [expr {max($hw1,$hw2)}]
set bw [dict get $o_columnstates $cidx maxwidthbodyseen]
set padwidth [expr {max($hw3,$bw)}]
if {[dict exists $column_width_cache $cidx]} {
set hwidth [dict get $column_width_cache $cidx headerwidth]
set padwidth [expr {max($padwidth,$hwidth)}]
}
#test hack - wider helps stop the breaks - but leaves junk spaces and ansiresets beyond the rhs border of table #test hack - wider helps stop the breaks - but leaves junk spaces and ansiresets beyond the rhs border of table
#print function overflow 0 fixes? #print function overflow 0 fixes?
set padwidth 20 #set padwidth 20
#This sort of works - but doesn't cater for colspans that don't strictly decrease in size as we go down the header list
#we end up with breaks in some situations
#we don't know the width here, because we would need to look-ahead to see the widest section of frame
#We will adjust the padding below.
#we need the column data width as a minimum or we'll cut lines above from earlier columns
set padwidth [my column_datawidth $cidx -headers 0 -data 1 -cached 1]
#set bline [string repeat \uFFFF $colwidth] #under assumption we are building table using L frame method and that horizontal borders are only ever 1 high
set bline [string repeat \uFFFF $padwidth] # - we can avoid using a frame - but we potentially need to manually adjust for show_hpos show_edge etc
#avoiding frame here is faster.. but not by much (<10%? on textblock::spantest2 )
if 0 {
#breaks -show_edge 0
if {$rowpos eq "top" && [dict get $o_opts_table -show_edge]} {
set padheight [expr {$rowh + 2}]
} else {
set padheight [expr {$rowh + 1}]
}
set bline [string repeat $TSUB [expr {$padwidth +1}]]
set h_lines [lrepeat $padheight $bline]
set hcell_blank [::join $h_lines \n]
set header_frame $hcell_blank
} else {
set bline [string repeat $TSUB $padwidth]
set h_lines [lrepeat $rowh $bline] set h_lines [lrepeat $rowh $bline]
set hcell_blank [::join $h_lines \n] set hcell_blank [::join $h_lines \n]
set spacemap [list hl "\uFFFF" vll "\uFFFF" vlr "\uFFFF" tlc "\uFFFF" blc "\uFFFF" trc "\uFFFF " brc "\uFFFF"] ;# a debug test
# -usecache 1 ok # -usecache 1 ok
set header_frame [textblock::frame -width [expr {$padwidth+2}] -type [dict get $ftypes header]\ #set header_frame [textblock::frame -ellipsis 0 -width [expr {$padwidth+2}] -type [dict get $ftypes header]\
-ansibase $ansibase_header \ # -ansibase $ansibase_header \
-boxlimits $hlims -boxmap $spacemap $hcell_blank\ # -boxlimits $hlims -boxmap $framesub_map $hcell_blank\
# ]
#frame borders will never display - so use the simplest frametype and don't apply any ansi
set header_frame [textblock::frame -ellipsis 0 -width [expr {$padwidth+2}] -type ascii\
-boxlimits $hlims -boxmap $framesub_map $hcell_blank\
] ]
append part_header $header_frame\n }
} append part_header $header_frame\n
} }
incr h incr h
} }
@ -1558,9 +1595,21 @@ namespace eval textblock {
] ]
append part_header $header_frame\n append part_header $header_frame\n
} }
set part_header [string trimright $part_header \n]
lassign [textblock::size $part_header] _w return_headerwidth _h return_headerheight lassign [textblock::size $part_header] _w return_headerwidth _h return_headerheight
set padline [string repeat $TSUB $return_headerwidth]
set adjusted_lines [list]
foreach ln [split $part_header \n] {
if {[string first $TSUB $ln] >=0} {
lappend adjusted_lines $padline
} else {
lappend adjusted_lines $ln
} }
append output $part_header }
set part_header [join $adjusted_lines \n]
}
append output $part_header \n
set r 0 set r 0
set rmax [expr {[llength $cells]-1}] set rmax [expr {[llength $cells]-1}]
@ -1589,10 +1638,10 @@ namespace eval textblock {
set colidx [lindex [dict keys $o_columndefs] $index_expression] ;#convert possible end-1,2+2 etc expression to >= 0 integer in dict range set colidx [lindex [dict keys $o_columndefs] $index_expression] ;#convert possible end-1,2+2 etc expression to >= 0 integer in dict range
set opt_col_ansibase [dict get $o_columndefs $colidx -ansibase] ;#ordinary merge of codes already done in configure_column set opt_col_ansibase [dict get $o_columndefs $colidx -ansibase] ;#ordinary merge of codes already done in configure_column
#set colwidth [my column_width $colidx]
set body_ansibase [dict get $o_opts_table -ansibase_body] set body_ansibase [dict get $o_opts_table -ansibase_body]
#set ansibase [punk::ansi::codetype::sgr_merge_singles [list $body_ansibase $opt_col_ansibase]] ;#allow col to override body #set ansibase [punk::ansi::codetype::sgr_merge_singles [list $body_ansibase $opt_col_ansibase]] ;#allow col to override body
set ansibase $body_ansibase$opt_col_ansibase
set body_ansiborder [dict get $o_opts_table -ansiborder_body] set body_ansiborder [dict get $o_opts_table -ansiborder_body]
if {[dict get $o_opts_table -frametype] eq "block"} { if {[dict get $o_opts_table -frametype] eq "block"} {
#block is the only style where bg colour can fill the frame content area exactly if the L-shaped border elements are styled #block is the only style where bg colour can fill the frame content area exactly if the L-shaped border elements are styled
@ -1603,44 +1652,65 @@ namespace eval textblock {
set border_ansi $body_ansibase$body_ansiborder set border_ansi $body_ansibase$body_ansiborder
} }
set r 0 set r 0
set ftblock [expr {[dict get $o_opts_table -frametype] eq "block"}]
foreach c $cells { foreach c $cells {
set ansibase $body_ansibase$opt_col_ansibase
set row_ansibase [dict get $o_rowdefs $r -ansibase] set row_ansibase [dict get $o_rowdefs $r -ansibase]
#todo - joinleft,joinright,joindown based on opts in args #todo - joinleft,joinright,joindown based on opts in args
#append output [textblock::frame -boxlimits {vll blc hlb} $c]\n #append output [textblock::frame -boxlimits {vll blc hlb} $c]\n
if {[dict get $o_opts_table -frametype] eq "block"} { set cell_ansibase ""
set row_ansibase [dict get $o_rowdefs $r -ansibase]
set row_bg "" set row_bg ""
if {$row_ansibase ne ""} { if {$row_ansibase ne ""} {
set row_bg [punk::ansi::codetype::sgr_merge_singles [list $row_ansibase] -filter_fg 1] set row_bg [punk::ansi::codetype::sgr_merge_singles [list $row_ansibase] -filter_fg 1]
} }
set ansiborder_body_col_row $border_ansi$row_bg set ansiborder_body_col_row $border_ansi$row_bg
set ansiborder_final $ansiborder_body_col_row set ansiborder_final $ansiborder_body_col_row
if 1 { #$c will always have ansi resets due to overtype behaviour ?
#$c will always have ansi resets due to overtype::left behaviour
#todo - review overtype #todo - review overtype
if {[punk::ansi::ta::detect $c]} { if {[punk::ansi::ta::detect $c]} {
#if {[textblock::widthtopline $c] == $colwidth} {}
#use only the last ansi sequence in the cell value #use only the last ansi sequence in the cell value
#Filter out foreground and use background for ansiborder override #Filter out foreground and use background for ansiborder override
set parts [punk::ansi::ta::split_codes_single $c] set parts [punk::ansi::ta::split_codes_single $c]
#we have detected ansi - so there will always be at least 3 parts beginning and ending with pt pt,ansi,pt,ansi...,pt #we have detected ansi - so there will always be at least 3 parts beginning and ending with pt pt,ansi,pt,ansi...,pt
set codes [list] set codes [list]
foreach {pt cd} $parts { foreach {pt cd} $parts {
if {$cd ne ""} {
lappend codes $cd lappend codes $cd
} }
}
#set takebg [lindex $parts end-1] #set takebg [lindex $parts end-1]
#set cell_bg [punk::ansi::codetype::sgr_merge_singles [list $takebg] -filter_fg 1] #set cell_bg [punk::ansi::codetype::sgr_merge_singles [list $takebg] -filter_fg 1]
set cell_bg [punk::ansi::codetype::sgr_merge_singles $codes -filter_fg 1] set cell_bg [punk::ansi::codetype::sgr_merge_singles $codes -filter_fg 1 -filter_reset 1]
#puts --->[ansistring VIEW $codes]
#puts "-->>> [ansistring VIEW $cell_bg] <<<--" if {[punk::ansi::codetype::is_sgr_reset [lindex $codes end]]} {
set ansiborder_final $ansiborder_body_col_row$cell_bg if {[punk::ansi::codetype::is_sgr_reset [lindex $codes end-1]]} {
#JMN #special case double reset at end of content
set cell_ansi_tail [punk::ansi::codetype::sgr_merge_singles $codes] ;#no filters
set ansibase ""
set row_ansibase ""
if {$ftblock} {
set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_body_col_row] -filter_bg 1]
} }
set cell_ansibase $cell_ansi_tail
} else {
#single trailing reset in content
set cell_ansibase "" ;#cell doesn't contribute to frame's ansibase
} }
} else { } else {
set ansiborder_body_col_row $border_ansi if {$ftblock} {
set ansiborder_final $ansiborder_body_col_row #no resets use cells bg to extend to the border - only for block frames
set ansiborder_final $ansiborder_body_col_row$cell_bg
}
set cell_ansibase $cell_bg
} }
set ansibase_final $ansibase$row_ansibase }
set ansibase_final $ansibase$row_ansibase$cell_ansibase
if {$r == 0} { if {$r == 0} {
if {$r == $rmax} { if {$r == $rmax} {
@ -1665,7 +1735,7 @@ namespace eval textblock {
set blims [struct::set difference $blims [dict get $::textblock::class::table_edge_parts top$opt_posn] ] set blims [struct::set difference $blims [dict get $::textblock::class::table_edge_parts top$opt_posn] ]
} }
} }
set rowframe [textblock::frame -type [dict get $ftypes body] -ansibase $ansibase_final -ansiborder $ansiborder_final -boxlimits $blims -boxmap $bmap -joins $joins $c] set rowframe [textblock::frame -type [dict get $ftypes body] -width [expr {$colwidth+2}] -blockalign $col_blockalign -ansibase $ansibase_final -ansiborder $ansiborder_final -boxlimits $blims -boxmap $bmap -joins $joins $c]
set return_bodywidth [textblock::width $rowframe] set return_bodywidth [textblock::width $rowframe]
append part_body $rowframe \n append part_body $rowframe \n
} else { } else {
@ -1683,7 +1753,7 @@ namespace eval textblock {
set blims [struct::set difference $blims [dict get $::textblock::class::table_edge_parts middle$opt_posn] ] set blims [struct::set difference $blims [dict get $::textblock::class::table_edge_parts middle$opt_posn] ]
} }
} }
append part_body [textblock::frame -type [dict get $ftypes body] -ansibase $ansibase_final -ansiborder $ansiborder_final -boxlimits $blims -boxmap $bmap -joins $joins $c]\n append part_body [textblock::frame -type [dict get $ftypes body] -width [expr {$colwidth+2}] -blockalign $col_blockalign -ansibase $ansibase_final -ansiborder $ansiborder_final -boxlimits $blims -boxmap $bmap -joins $joins $c]\n
} }
incr r incr r
} }
@ -1691,7 +1761,6 @@ namespace eval textblock {
if {![llength $cells]} { if {![llength $cells]} {
set joins [lremove $joins [lsearch $joins down*]] set joins [lremove $joins [lsearch $joins down*]]
#we need to know the width of the column to setup the empty cell properly #we need to know the width of the column to setup the empty cell properly
#(we didn't need it above because get_column_cells_by_index returned values of the correct width)
#even if no header displayed - we should take account of any defined column widths #even if no header displayed - we should take account of any defined column widths
set colwidth [my column_width $index_expression] set colwidth [my column_width $index_expression]
@ -1751,6 +1820,14 @@ namespace eval textblock {
set RST [punk::ansi::a] set RST [punk::ansi::a]
set ansibase_body [dict get $o_opts_table -ansibase_body] set ansibase_body [dict get $o_opts_table -ansibase_body]
set ansibase_col [dict get $cdef -ansibase] set ansibase_col [dict get $cdef -ansibase]
set textalign [dict get $cdef -textalign]
switch -- $textalign {
left {set pad right}
right {set pad left}
default {
set pad "centre" ;#todo?
}
}
set ansibase_header [dict get $o_opts_table -ansibase_header] set ansibase_header [dict get $o_opts_table -ansibase_header]
@ -1799,15 +1876,18 @@ namespace eval textblock {
set header_underlay [lrepeat $header_maxdataheight $hdr_line_blank] set header_underlay [lrepeat $header_maxdataheight $hdr_line_blank]
set header_underlay $ansibase_header[join $header_underlay \n] set header_underlay $ansibase_header[join $header_underlay \n]
if {$hdr ne ""} { if {$hdr ne ""} {
dict lappend output headers [overtype::left -experimental test_mode $header_underlay $ansibase_header$hdr] dict lappend output headers [overtype::renderspace -experimental test_mode $header_underlay $ansibase_header$hdr]
} else { } else {
dict lappend output headers $header_underlay dict lappend output headers $header_underlay
} }
} }
set colwidth [my column_width $cidx] #set colwidth [my column_width $cidx]
set cell_line_blank [string repeat " " $colwidth] #set cell_line_blank [string repeat " " $colwidth]
set datawidth [my column_datawidth $cidx -headers 0 -footers 0 -data 1 -cached 1]
set cell_line_blank [string repeat " " $datawidth]
set items [dict get $o_columndata $cidx] set items [dict get $o_columndata $cidx]
@ -1858,10 +1938,12 @@ namespace eval textblock {
set cval_block [join $cval_lines \n] set cval_block [join $cval_lines \n]
#TODO! fix overtype library #TODO! fix overtype library
#set cell [overtype::left -experimental test_mode $cell_ansibase$cell_blank$RST $cval_block] #set cell [overtype::renderspace -experimental test_mode $cell_ansibase$cell_blank$RST $cval_block]
#set cell [overtype::left -experimental test_mode $cell_blank $cval_block] #set cell [overtype::renderspace -experimental test_mode $cell_blank $cval_block]
set cell [textblock::pad $cval_block -width $colwidth -padchar " " -within_ansi 0 -which right] #set cell [textblock::pad $cval_block -width $colwidth -padchar " " -within_ansi 0 -which right]
set cell [textblock::pad $cval_block -width $datawidth -padchar " " -within_ansi 1 -which $pad]
#set cell [textblock::pad $cval_block -width $colwidth -padchar " " -within_ansi 0 -which left] #set cell [textblock::pad $cval_block -width $colwidth -padchar " " -within_ansi 0 -which left]
dict lappend output cells $cell dict lappend output cells $cell
@ -2077,10 +2159,11 @@ namespace eval textblock {
-headers 0\ -headers 0\
-footers 0\ -footers 0\
-data 1\ -data 1\
-cached 1\
] ]
dict for {k v} $args { dict for {k v} $args {
switch -- $k { switch -- $k {
-headers - -footers - -data {} -headers - -footers - -data - -cached {}
default { default {
error "column_datawidth unrecognised flag '$k'. Known flags: [dict keys $defaults]" error "column_datawidth unrecognised flag '$k'. Known flags: [dict keys $defaults]"
} }
@ -2092,6 +2175,24 @@ namespace eval textblock {
if {$cidx eq ""} { if {$cidx eq ""} {
return return
} }
if {[dict get $opts -cached]} {
set hwidest 0
set bwidest 0
set fwidest 0
if {[dict get $opts -headers]} {
set hwidest [dict get $o_columnstates $cidx maxwidthheaderseen]
}
if {[dict get $opts -data]} {
set bwidest [dict get $o_columnstates $cidx maxwidthbodyseen]
}
if {[dict get $opts -footers]} {
#TODO!
#set bwidest [dict get $o_columnstates $cidx maxwidthfooterseen]
}
return [expr {max($hwidest,$bwidest,$fwidest)}]
}
#assert cidx is >=0 integer in valid range of keys for o_columndefs #assert cidx is >=0 integer in valid range of keys for o_columndefs
set values [list] set values [list]
if {[dict get $opts -headers]} { if {[dict get $opts -headers]} {
@ -2213,12 +2314,12 @@ namespace eval textblock {
set table $nextcol set table $nextcol
set height [textblock::height $table] ;#only need to get height once at start set height [textblock::height $table] ;#only need to get height once at start
} else { } else {
set nextcol [textblock::join [textblock::block $padwidth $height "\uFFFF"] $nextcol] set nextcol [textblock::join [textblock::block $padwidth $height $TSUB] $nextcol]
set table [overtype::left -overflow 1 -experimental test_mode -transparent \uFFFF $table $nextcol] set table [overtype::renderspace -overflow 1 -experimental test_mode -transparent $TSUB $table $nextcol]
#JMN #JMN
#set nextcol [textblock::join [textblock::block $padwidth $height "\uFFFF"] $nextcol] #set nextcol [textblock::join [textblock::block $padwidth $height "\uFFFF"] $nextcol]
#set table [overtype::left -overflow 1 -experimental test_mode -transparent \uFFFF $table $nextcol] #set table [overtype::renderspace -overflow 1 -experimental test_mode -transparent \uFFFF $table $nextcol]
} }
incr padwidth $bodywidth incr padwidth $bodywidth
incr colposn incr colposn
@ -2266,6 +2367,21 @@ namespace eval textblock {
$t configure -show_header 1 -ansiborder_header [a+ cyan] $t configure -show_header 1 -ansiborder_header [a+ cyan]
return $t return $t
} }
#more complex colspans
proc spantest2 {} {
set t [list_as_table 5 {a b c d e aa bb cc dd ee X Y} -return object]
$t configure_column 0 -headers {span3 span4 span5/5 "span-all etc blah 123 hmmmmm" span2}
$t configure_column 0 -header_colspans {3 4 1 all 1}
$t configure_column 2 -headers {"" "" "" "" c2span2}
$t configure_column 1 -header_colspans {0 0 2 0 1}
$t configure_column 2 -header_colspans {0 0 0 0 2}
$t configure -show_header 1 -ansiborder_header [a+ cyan]
return $t
}
proc periodic {args} { proc periodic {args} {
#For an impressive interactive terminal app (javascript) #For an impressive interactive terminal app (javascript)
# see: https://github.com/spirometaxas/periodic-table-cli # see: https://github.com/spirometaxas/periodic-table-cli
@ -2607,6 +2723,19 @@ namespace eval textblock {
} }
return [punk::char::ansifreestring_width $textblock] return [punk::char::ansifreestring_width $textblock]
} }
#when we know the block is uniform in width - just examine topline
proc widthtopline {textblock} {
set firstnl [string first \n $textblock]
if {$firstnl >= 0} {
set tl [string range $textblock 0 $firstnl]
} else {
set tl $textblock
}
if {[punk::ansi::ta::detect $tl]} {
set tl [punk::ansi::stripansi $tl]
}
return [punk::char::ansifreestring_width $tl]
}
#uses tcl's string length on each line. Length will include chars in ansi codes etc - this is not a 'width' determining function. #uses tcl's string length on each line. Length will include chars in ansi codes etc - this is not a 'width' determining function.
proc string_length_line_max textblock { proc string_length_line_max textblock {
tcl::mathfunc::max {*}[lmap v [split $textblock \n] {string length $v}] tcl::mathfunc::max {*}[lmap v [split $textblock \n] {string length $v}]
@ -2945,7 +3074,7 @@ namespace eval textblock {
return $t return $t
} }
proc pad_test2 {blocklist} { proc pad_test_blocklist {blocklist} {
set b 0 set b 0
set blockinfo [dict create] set blockinfo [dict create]
foreach block $blocklist { foreach block $blocklist {
@ -2989,7 +3118,7 @@ namespace eval textblock {
set b2 "[a+ green bold][textblock::block 4 4 x]\n[a+ Green]" set b2 "[a+ green bold][textblock::block 4 4 x]\n[a+ Green]"
set b3 "[textblock::testblock 4 rainbow]\n[a]" set b3 "[textblock::testblock 4 rainbow]\n[a]"
set b4 "[textblock::testblock 4 rainbow]\n[a+ Green]" set b4 "[textblock::testblock 4 rainbow]\n[a+ Green]"
set t [textblock::pad_test2 [list $b1 $b2 $b3 $b4]] set t [textblock::pad_test_blocklist [list $b1 $b2 $b3 $b4]]
} }
@ -3039,6 +3168,7 @@ namespace eval textblock {
} }
#for joining 'rendered' blocks of plain or ansi text. Being 'rendered' means they are without ansi movement sequences as these have been processed #for joining 'rendered' blocks of plain or ansi text. Being 'rendered' means they are without ansi movement sequences as these have been processed
#they may however still be 'ragged' ie differing line lengths
proc ::textblock::join {args} { proc ::textblock::join {args} {
#lassign [punk::lib::opts_values { #lassign [punk::lib::opts_values {
# blocks -type string -multiple 1 # blocks -type string -multiple 1
@ -4396,9 +4526,27 @@ namespace eval textblock {
} }
variable frame_cache variable frame_cache
set out "" set out ""
if {[catch {
set termwidth [dict get [punk::console::get_size] columns]
}]} {
set termwidth 80
}
dict for {k v} $frame_cache { dict for {k v} $frame_cache {
lassign $v _f frame _used used lassign $v _f frame _used used
append out [textblock::join $k " " $frame " " $used]\n #set fwidth [textblock::widthtopline $frame]
#review - are cached frames uniform width lines?
set fwidth [textblock::width $frame]
set frameinfo "$k used:$used "
set allinone_width [expr {[string length $frameinfo] + $fwidth}]
if {$allinone_width >= $termwidth} {
#split across 2 lines
append out "$frameinfo\n"
append out $frame \n
} else {
append out [textblock::join $frameinfo $frame]\n
}
append out \n ;# frames used to build tables often have joins - keep a line in between for clarity
} }
if {$action eq "clear"} { if {$action eq "clear"} {
set frame_cache [dict create] set frame_cache [dict create]
@ -4455,16 +4603,18 @@ namespace eval textblock {
-height ""\ -height ""\
-ansiborder ""\ -ansiborder ""\
-ansibase ""\ -ansibase ""\
-align "left"\ -blockalign "centre"\
-textalign "left"\
-ellipsis 1\ -ellipsis 1\
-usecache 1\ -usecache 1\
-buildcache 1\ -buildcache 1\
] ]
#todo -blockalignbias -textalignbias?
#use -buildcache 1 with -usecache 0 for debugging cache issues so we can inspect using textblock::frame_cache #use -buildcache 1 with -usecache 0 for debugging cache issues so we can inspect using textblock::frame_cache
set opts [dict merge $defaults $arglist] set opts [dict merge $defaults $arglist]
foreach {k v} $opts { foreach {k v} $opts {
switch -- $k { switch -- $k {
-etabs - -type - -boxlimits - -boxmap - -joins - -title - -subtitle - -width - -height - -ansiborder - -ansibase - -align - -ellipsis - -usecache - -buildcache {} -etabs - -type - -boxlimits - -boxmap - -joins - -title - -subtitle - -width - -height - -ansiborder - -ansibase - -blockalign - -textalign - -ellipsis - -usecache - -buildcache {}
default { default {
error "frame option '$k' not understood. Valid options are [dict keys $defaults]" error "frame option '$k' not understood. Valid options are [dict keys $defaults]"
} }
@ -4584,16 +4734,24 @@ namespace eval textblock {
set opt_width [dict get $opts -width] set opt_width [dict get $opts -width]
set opt_height [dict get $opts -height] set opt_height [dict get $opts -height]
# -- --- --- --- --- --- # -- --- --- --- --- ---
set opt_align [dict get $opts -align] set opt_blockalign [dict get $opts -blockalign]
set opt_align [string tolower $opt_align] switch -- $opt_blockalign {
switch -- $opt_align {
left - right - centre - center {} left - right - centre - center {}
default { default {
error "frame option -align must be left|right|centre|center - received: $opt_align" error "frame option -blockalign must be left|right|centre|center - received: $opt_blockalign"
} }
} }
#these are all valid commands for overtype::<cmd> #these are all valid commands for overtype::<cmd>
# -- --- --- --- --- --- # -- --- --- --- --- ---
set opt_textalign [dict get $opts -textalign]
switch -- $opt_textalign {
left - right - centre - center {}
default {
error "frame option -textalign must be left|right|centre|center - received: $opt_textalign"
}
}
# -- --- --- --- --- ---
set opt_ansiborder [dict get $opts -ansiborder] set opt_ansiborder [dict get $opts -ansiborder]
set opt_ansibase [dict get $opts -ansibase] ;#experimental set opt_ansibase [dict get $opts -ansibase] ;#experimental
set opt_ellipsis [dict get $opts -ellipsis] set opt_ellipsis [dict get $opts -ellipsis]
@ -4611,7 +4769,7 @@ namespace eval textblock {
} }
} }
set contents [string map [list \r\n \n] $contents] set contents [string map [list \r\n \n] $contents]
set actual_contentwidth [textblock::width $contents] set actual_contentwidth [textblock::width $contents] ;#length of longest line in contents (contents can be ragged)
set actual_contentheight [textblock::height $contents] set actual_contentheight [textblock::height $contents]
} else { } else {
set actual_contentwidth 0 set actual_contentwidth 0
@ -4627,92 +4785,99 @@ namespace eval textblock {
} }
if {$opt_width eq ""} { if {$opt_width eq ""} {
set contentwidth $content_or_title_width set frame_inner_width $content_or_title_width
} else { } else {
set contentwidth [expr {max(0,$opt_width - 2)}] ;#default set frame_inner_width [expr {max(0,$opt_width - 2)}] ;#default
} }
if {$opt_height eq ""} { if {$opt_height eq ""} {
set contentheight $actual_contentheight set frame_inner_height $actual_contentheight
} else { } else {
set contentheight [expr {max(0,$opt_height -2)}] ;#default set frame_inner_height [expr {max(0,$opt_height -2)}] ;#default
} }
if {$contentheight == 0 && $contentwidth == 0} { if {$frame_inner_height == 0 && $frame_inner_width == 0} {
set has_contents 0 set has_contents 0
} }
#todo - render it with vertical overflow so we can process ansi moves? #todo - render it with vertical overflow so we can process ansi moves?
#set linecount [textblock::height $contents] #set linecount [textblock::height $contents]
set linecount $contentheight set linecount $frame_inner_height
# -- --- --- --- --- --- --- --- --- # -- --- --- --- --- --- --- --- ---
variable frame_cache variable frame_cache
#review - custom frame affects contentwidth - exclude from caching? #review - custom frame affects frame_inner_width - exclude from caching?
#set cache_key [concat $arglist $contentwidth $contentheight] #set cache_key [concat $arglist $frame_inner_width $frame_inner_height]
set hashables [concat $arglist $contentwidth $contentheight] set hashables [concat $arglist $frame_inner_width $frame_inner_height]
package require md5 package require md5
set hash [md5::md5 -hex $hashables] set hash [md5::md5 -hex $hashables]
set cache_key "$hash-$contentwidth-$contentheight-actualcontentwidth:$actual_contentwidth" set cache_key "$hash-$frame_inner_width-$frame_inner_height-actualcontentwidth:$actual_contentwidth"
set TSUB \u1FFF; #needs to be different to that used in table construction #should be in a unicode private range different to that used in table construction
#e.g BMP PUA U+E000 -> U+F8FF - although this is commonly used for example by nerdfonts
#also supplementary private use blocks
#however these display double wide on for example cmd terminal despite having wcswidth 1 (makes layout debugging difficult)
#U+F0000 -> U+FFFD
#U+100000 -> U+10FFFD
#FSUB options: \uf0ff \uF1FF \uf2ff (no nerdfont glyphs - should look like standard replacement char) \uF2DD (circular thingy)
#should be something someone is unlikely to use as part of a custom frame character.
#ideally a glyph that doesn't auto-expand into whitespace and is countable when in a string (narrower is better)
#As nerdfont glyphs tend to be mostly equal height & width - circular glyphs tend to be more distinguishable in a string
#terminal settings may need to be adjusted to stop auto glyph resizing - a rather annoying misfeature that some people seem to think they like.
#e.g in wezterm config: allow_square_glyphs_to_overflow_width = "Never"
#review - we could consider wasting a few cycles to check for a conflict and use a different FSUB
set FSUB \uF2DD
#this occurs commonly in table building with colspans - review #this occurs commonly in table building with colspans - review
if {$actual_contentwidth > $contentwidth || $actual_contentheight != $contentheight} { if {$actual_contentwidth > $frame_inner_width || $actual_contentheight != $frame_inner_height} {
set usecache 0 set usecache 0
#set buildcache 0 ;#comment out for debug/analysis so we can see #set buildcache 0 ;#comment out for debug/analysis so we can see
set cache_key [a+ Web-red]$cache_key[a] #puts "--->> frame_inner_width:$frame_inner_width actual_contentwidth:$actual_contentwidth contents: '$contents'"
set cache_key [a+ Web-red web-white]$cache_key[a]
} }
if {$buildcache && $actual_contentwidth < $contentwidth} { if {$buildcache && $actual_contentwidth < $frame_inner_width} {
#colourise cache_key to warn #colourise cache_key to warn
if {$actual_contentwidth == 0} { if {$actual_contentwidth == 0} {
#we can still substitue with right length #we can still substitute with right length
set cache_key [a+ Web-steelblue web-black]$cache_key[a] set cache_key [a+ Web-steelblue web-black]$cache_key[a]
} else { } else {
#actual_contentwidth is shorter - rather than choose an alignment and pad - we will opt out of caching #actual_contentwidth is narrower than frame - check template's patternwidth
if {[dict exists $frame_cache $cache_key]} {
set cache_patternwidth [dict get $frame_cache $cache_key patternwidth]
} else {
set cache_patternwidth [$actual_contentwidth]
}
if {$actual_contentwidth < $cache_patternwidth} {
set usecache 0 set usecache 0
set cache_key [a+ Web-orange web-black]$cache_key[a] set cache_key [a+ Web-orange web-black]$cache_key[a]
} elseif {$actual_contentwidth == $cache_patternwidth} {
#set usecache 1
} else {
#actual_contentwidth > pattern
set usecache 0
set cache_key [a+ Web-red web-black]$cache_key[a]
} }
} }
}
#JMN debug
#set usecache 0
set is_cached 0
if {$usecache && [dict exists $frame_cache $cache_key]} { if {$usecache && [dict exists $frame_cache $cache_key]} {
set cache_patternwidth [dict get $frame_cache $cache_key patternwidth]
set template [dict get $frame_cache $cache_key frame] set template [dict get $frame_cache $cache_key frame]
set used [dict get $frame_cache $cache_key used] set used [dict get $frame_cache $cache_key used]
dict set frame_cache $cache_key used [expr {$used+1}] dict set frame_cache $cache_key used [expr {$used+1}] ;#update existing record
set is_cached 1
set resultlines [list]
set overwritable [string repeat $TSUB $contentwidth]
set blankset [string repeat " " $contentwidth]
set contentindex 0
set clines [split $contents \n]
if {$actual_contentwidth == 0} {
foreach tline [split $template \n] {
if {[string first $TSUB $tline] >= 0} {
lappend resultlines [string map [list $overwritable $blankset] $tline]
incr contentindex
} else {
lappend resultlines $tline
}
}
} else {
foreach tline [split $template \n] {
if {[string first $TSUB $tline] >= 0} {
#set sublen [string length [lindex [regexp -inline "\[^$TSUB]*($TSUB*).*" $tline] 1]]
#set overwritable [string repeat $TSUB $sublen]
lappend resultlines [string map [list $overwritable [lindex $clines $contentindex]] $tline]
incr contentindex
} else {
lappend resultlines $tline
}
}
}
return [::join $resultlines \n]
} }
# -- --- --- --- --- --- --- --- --- # -- --- --- --- --- --- --- --- ---
if {!$is_cached} {
set rst [a] set rst [a]
#set column [string repeat " " $contentwidth] ;#default - may need to override for custom frame #set column [string repeat " " $frame_inner_width] ;#default - may need to override for custom frame
set underlayline [string repeat " " $contentwidth] set underlayline [string repeat " " $frame_inner_width]
set underlay [::join [lrepeat $linecount $underlayline] \n] set underlay [::join [lrepeat $linecount $underlayline] \n]
set cache_underlayline [string repeat $TSUB $contentwidth]
set cache_underlay [::join [lrepeat $linecount $cache_underlayline] \n]
set vll_width 1 ;#default for all except custom (printing width) set vll_width 1 ;#default for all except custom (printing width)
set vlr_width 1 set vlr_width 1
@ -4751,20 +4916,21 @@ namespace eval textblock {
set brc_width [punk::ansi::printing_length $brc] set brc_width [punk::ansi::printing_length $brc]
set framewidth [expr {$contentwidth + 2}] ;#reverse default assumption set framewidth [expr {$frame_inner_width + 2}] ;#reverse default assumption
if {$opt_width eq ""} { if {$opt_width eq ""} {
#width wasn't specified - so user is expecting frame to adapt to title/contents #width wasn't specified - so user is expecting frame to adapt to title/contents
#content shouldn't truncate because of extra wide frame #content shouldn't truncate because of extra wide frame
set contentwidth $content_or_title_width #review - punk::console::get_size ? wrapping? quite hard to support with colspans
set frame_inner_width $content_or_title_width
set tbarwidth [expr {$content_or_title_width + 2 - $tlc_width - $trc_width - 2 + $vll_width + $vlr_width}] ;#+/2's for difference between border element widths and standard element single-width set tbarwidth [expr {$content_or_title_width + 2 - $tlc_width - $trc_width - 2 + $vll_width + $vlr_width}] ;#+/2's for difference between border element widths and standard element single-width
set bbarwidth [expr {$content_or_title_width + 2 - $blc_width - $brc_width - 2 + $vll_width + $vlr_width}] set bbarwidth [expr {$content_or_title_width + 2 - $blc_width - $brc_width - 2 + $vll_width + $vlr_width}]
} else { } else {
set contentwidth [expr $opt_width - $vll_width - $vlr_width] ;#content may be truncated set frame_inner_width [expr $opt_width - $vll_width - $vlr_width] ;#content may be truncated
set tbarwidth [expr {$opt_width - $tlc_width - $trc_width}] set tbarwidth [expr {$opt_width - $tlc_width - $trc_width}]
set bbarwidth [expr {$opt_width - $blc_width - $brc_width}] set bbarwidth [expr {$opt_width - $blc_width - $brc_width}]
} }
#set column [string repeat " " $contentwidth] #set column [string repeat " " $frame_inner_width]
set underlayline [string repeat " " $contentwidth] set underlayline [string repeat " " $frame_inner_width]
set underlay [::join [lrepeat $linecount $underlayline] \n] set underlay [::join [lrepeat $linecount $underlayline] \n]
#cache? #cache?
@ -4797,14 +4963,14 @@ namespace eval textblock {
} }
} }
altg { altg {
set tbar [string repeat $hlt $contentwidth] set tbar [string repeat $hlt $frame_inner_width]
set tbar [cd::groptim $tbar] set tbar [cd::groptim $tbar]
set bbar [string repeat $hlb $contentwidth] set bbar [string repeat $hlb $frame_inner_width]
set bbar [cd::groptim $bbar] set bbar [cd::groptim $bbar]
} }
default { default {
set tbar [string repeat $hlt $contentwidth] set tbar [string repeat $hlt $frame_inner_width]
set bbar [string repeat $hlb $contentwidth] set bbar [string repeat $hlb $frame_inner_width]
} }
} }
@ -4932,6 +5098,7 @@ namespace eval textblock {
} }
set fs "" set fs ""
set fscached "" set fscached ""
set cache_patternwidth 0
#todo - output nothing except maybe newlines depending on if opt_height 0 and/or opt_width 0? #todo - output nothing except maybe newlines depending on if opt_height 0 and/or opt_width 0?
if {$topborder} { if {$topborder} {
if {$leftborder && $rightborder} { if {$leftborder && $rightborder} {
@ -4955,34 +5122,40 @@ namespace eval textblock {
append fs \n append fs \n
append fscached \n append fscached \n
} }
#set inner [overtype::$opt_align -ellipsis $opt_ellipsis $opt_ansibase$underlay$rstbase $opt_ansibase$contents$rstbase] switch -- $opt_textalign {
set inner [overtype::$opt_align -ellipsis $opt_ellipsis $opt_ansibase$underlay$rstbase $contents] right {set pad "left"}
left {set pad "right"}
default {set pad $opt_textalign}
}
#set textaligned_contents [textblock::pad $contents -width $actual_contentwidth -which $pad -within_ansi 1]
#set inner [overtype::block -blockalign $opt_blockalign -ellipsis $opt_ellipsis $opt_ansibase$underlay$rstbase $opt_ansibase$textaligned_contents]
#set cache_inner [overtype::$opt_align -ellipsis $opt_ellipsis $opt_ansibase$cache_underlay$rstbase $contents] set cache_contentline [string repeat $FSUB $actual_contentwidth]
#review set cache_patternwidth $actual_contentwidth
set cache_inner $opt_ansibase$cache_underlay$rstbase set cache_contentpattern [::join [lrepeat $linecount $cache_contentline] \n]
set cache_inner [overtype::block -blockalign $opt_blockalign -ellipsis $opt_ellipsis $opt_ansibase$underlay$rstbase $opt_ansibase$cache_contentpattern]
#after overtype::block - our actual patternwidth may be less
set cache_patternwidth [string length [lindex [regexp -inline "\[^$FSUB]*(\[$FSUB]*).*" $cache_inner] 1]]
if {$leftborder && $rightborder} { if {$leftborder && $rightborder} {
set bodyparts [list $lhs $opt_ansibase$inner$rstbase $rhs] #set bodyparts [list $lhs $inner $rhs]
set cache_bodyparts [list $lhs $opt_ansibase$cache_inner$rstbase $rhs] set cache_bodyparts [list $lhs $cache_inner $rhs]
} else { } else {
if {$leftborder} { if {$leftborder} {
set bodyparts [list $lhs $opt_ansibase$inner$rstbase] #set bodyparts [list $lhs $inner]
set cache_bodyparts [list $lhs $opt_ansibase$cache_inner$rstbase] set cache_bodyparts [list $lhs $cache_inner]
} elseif {$rightborder} { } elseif {$rightborder} {
set bodyparts [list $opt_ansibase$inner$rstbase $rhs] #set bodyparts [list $inner $rhs]
set cache_bodyparts [list $opt_ansibase$cache_inner$rstbase $rhs] set cache_bodyparts [list $cache_inner $rhs]
} else { } else {
set bodyparts [list $opt_ansibase$inner$rstbase] #set bodyparts [list $inner]
set cache_bodyparts [list $opt_ansibase$cache_inner$rstbase] set cache_bodyparts [list $cache_inner]
} }
} }
set body [textblock::join -- {*}$bodyparts] #set body [textblock::join -- {*}$bodyparts]
if {$buildcache} {
set cache_body [textblock::join -- {*}$cache_bodyparts] set cache_body [textblock::join -- {*}$cache_bodyparts]
append fscached $cache_body append fscached $cache_body
} #append fs $body
append fs $body
} }
if {$opt_height eq "" || $opt_height > 1} { if {$opt_height eq "" || $opt_height > 1} {
@ -4992,31 +5165,91 @@ namespace eval textblock {
} }
if {$bottomborder} { if {$bottomborder} {
if {($topborder & $fs ne "xx" ) || ($has_contents || $opt_height > 2)} { if {($topborder & $fs ne "xx" ) || ($has_contents || $opt_height > 2)} {
append fs \n #append fs \n
append fscached \n append fscached \n
} }
if {$leftborder && $rightborder} { if {$leftborder && $rightborder} {
append fs $blc$bottombar$brc #append fs $blc$bottombar$brc
append fscached $blc$bottombar$brc append fscached $blc$bottombar$brc
} else { } else {
if {$leftborder} { if {$leftborder} {
append fs $blc$bottombar #append fs $blc$bottombar
append fscached $blc$bottombar append fscached $blc$bottombar
} elseif {$rightborder} { } elseif {$rightborder} {
append fs $bottombar$brc #append fs $bottombar$brc
append fscached $bottombar$brc append fscached $bottombar$brc
} else { } else {
append fs $bottombar #append fs $bottombar
append fscached $bottombar append fscached $bottombar
} }
} }
} }
} }
set template $fscached
;#end !$is_cached
}
#use the same mechanism to build the final frame - whether from cache or template
if {$actual_contentwidth == 0} {
set fs [string map [list $FSUB " "] $template]
} else {
set resultlines [list]
set overwritable [string repeat $FSUB $cache_patternwidth]
set contentindex 0
switch -- $opt_textalign {
left {set pad right}
right {set pad left}
default {set pad $opt_textalign}
}
#review
if {[string is integer -strict $opt_height] && $actual_contentheight < ($opt_height -2)} {
set diff [expr {($opt_height -2) - $actual_contentheight}]
append contents [::join [lrepeat $diff \n] ""]
}
set paddedcontents [textblock::pad $contents -which $pad -within_ansi 1] ;#autowidth to width of content (should match cache_patternwidth)
set paddedwidth [textblock::widthtopline $paddedcontents]
#review - horizontal truncation
if {$paddedwidth > $cache_patternwidth} {
set paddedcontents [overtype::renderspace -width $cache_patternwidth "" $paddedcontents]
}
set contentblock [textblock::join $paddedcontents] ;#make sure each line has ansi replays
set tlines [split $template \n]
#we will need to strip off the leading reset on each line when stitching together with template lines so that ansibase can come into play too.
#after textblock::join the reset will be a separate code ie should be exactly ESC[0m
set R [a]
set rlen [string length $R]
set clines [split $contentblock \n]
foreach tline $tlines {
if {[string first $FSUB $tline] >= 0} {
set content_line [lindex $clines $contentindex]
if {[string first $R $content_line] == 0} {
set content_line [string range $content_line $rlen end]
}
#make sure to replay opt_ansibase to the right of the replacement
lappend resultlines [string map [list $overwritable $content_line$opt_ansibase] $tline]
incr contentindex
} else {
lappend resultlines $tline
}
}
set fs [::join $resultlines \n]
}
if {$is_cached} {
return $fs
} else {
if {$buildcache} { if {$buildcache} {
dict set frame_cache $cache_key [list frame $fscached used 0] dict set frame_cache $cache_key [list frame $template used 0 patternwidth $cache_patternwidth]
} }
return $fs return $fs
}
} }
proc gcross {{size 1} args} { proc gcross {{size 1} args} {
if {$size == 0} { if {$size == 0} {

Loading…
Cancel
Save