From aaaacfb29365ca4ef3582f3fbc5a591beb37863a Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Sun, 19 May 2024 06:19:18 +1000 Subject: [PATCH] initial textblock::frame_cache, a? ansi colourset displays --- src/modules/punk-0.1.tm | 1 + src/modules/punk/ansi-999999.0a1.0.tm | 595 ++++++++++++++++++-------- src/modules/punk/char-999999.0a1.0.tm | 20 +- src/modules/punk/lib-999999.0a1.0.tm | 141 ++++++ src/modules/punk/ns-999999.0a1.0.tm | 2 +- src/modules/textblock-999999.0a1.0.tm | 235 +++++++++- 6 files changed, 786 insertions(+), 208 deletions(-) diff --git a/src/modules/punk-0.1.tm b/src/modules/punk-0.1.tm index 2cb6c91..90e425b 100644 --- a/src/modules/punk-0.1.tm +++ b/src/modules/punk-0.1.tm @@ -6932,6 +6932,7 @@ namespace eval punk { set cmdinfo [list] lappend cmdinfo [list help "This help. To see available subitems type: help topics"] lappend cmdinfo [list deck "(ensemble command to make new projects/modules and to generate docs)"] + lappend cmdinfo [list a? "view ANSI colours"] lappend cmdinfo [list ./ "view/change directory"] lappend cmdinfo [list ../ "go up one directory"] lappend cmdinfo [list ./new "make new directory and switch to it"] diff --git a/src/modules/punk/ansi-999999.0a1.0.tm b/src/modules/punk/ansi-999999.0a1.0.tm index 3b40670..d2bd75b 100644 --- a/src/modules/punk/ansi-999999.0a1.0.tm +++ b/src/modules/punk/ansi-999999.0a1.0.tm @@ -761,19 +761,21 @@ namespace eval punk::ansi { #CSI m = SGR (Select Graphic Rendition) +#leave map unindented - used both as a dict and for direct display variable SGR_setting_map { - reset 0 bold 1 dim 2 italic 3 noitalic 23 - underline 4 doubleunderline 21 nounderline 24 blink 5 fastblink 6 noblink 25 - reverse 7 noreverse 27 hide 8 nohide 28 strike 9 nostrike 29 - normal 22 defaultfg 39 defaultbg 49 overline 53 nooverline 55 - frame 51 framecircle 52 noframe 54 underlinedefault 59 +reset 0 bold 1 dim 2 italic 3 noitalic 23 +underline 4 doubleunderline 21 nounderline 24 blink 5 fastblink 6 noblink 25 +reverse 7 noreverse 27 hide 8 nohide 28 strike 9 nostrike 29 +normal 22 defaultfg 39 defaultbg 49 overline 53 nooverline 55 +frame 51 framecircle 52 noframe 54 underlinedefault 59 } #unprefixed colours are (close to) the ansi-specified colour names (lower-cased and whitespace collapsed, with capitalisation of 1st letter given fg/bg meaning here) +#leave map unindented - used both as a dict and for direct display variable SGR_colour_map { - black 30 red 31 green 32 yellow 33 blue 34 purple 35 cyan 36 white 37 - Black 40 Red 41 Green 42 Yellow 43 Blue 44 Purple 45 Cyan 46 White 47 - brightblack 90 brightred 91 brightgreen 92 brightyellow 93 brightblue 94 brightpurple 95 brightcyan 96 brightwhite 97 - Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblue 104 Brightpurple 105 Brightcyan 106 Brightwhite 107 +black 30 red 31 green 32 yellow 33 blue 34 purple 35 cyan 36 white 37 +Black 40 Red 41 Green 42 Yellow 43 Blue 44 Purple 45 Cyan 46 White 47 +brightblack 90 brightred 91 brightgreen 92 brightyellow 93 brightblue 94 brightpurple 95 brightcyan 96 brightwhite 97 +Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblue 104 Brightpurple 105 Brightcyan 106 Brightwhite 107 } variable SGR_map ;#public - part of interface - review set SGR_map [dict merge $SGR_colour_map $SGR_setting_map] @@ -792,185 +794,211 @@ namespace eval punk::ansi { # -- --- --- #css 1-2.0 HTML 3.2-4 Basic colours eg web-silver for fg Web-silver for bg # - dict set WEB_colour_map white 255-255-255 ;# #FFFFFF - dict set WEB_colour_map silver 192-192-192 ;# #C0C0C0 - dict set WEB_colour_map gray 128-128-128 ;# #808080 - dict set WEB_colour_map black 0-0-0 ;# #000000 - dict set WEB_colour_map red 255-0-0 ;# #FF0000 - dict set WEB_colour_map maroon 128-0-0 ;# #800000 - dict set WEB_colour_map yellow 255-255-0 ;# #FFFF00 - dict set WEB_colour_map olive 128-128-0 ;# #808000 - dict set WEB_colour_map lime 0-255-0 ;# #00FF00 - dict set WEB_colour_map green 0-128-0 ;# #008000 - dict set WEB_colour_map aqua 0-255-255 ;# #00FFFF - dict set WEB_colour_map teal 0-128-128 ;# #008080 - dict set WEB_colour_map blue 0-0-255 ;# #0000FF - dict set WEB_colour_map navy 0-0-128 ;# #000080 - dict set WEB_colour_map fuchsia 255-0-255 ;# #FF00FF - dict set WEB_colour_map purple 128-0-128 ;# #800080 + variable WEB_colour_map_basic + dict set WEB_colour_map_basic white 255-255-255 ;# #FFFFFF + dict set WEB_colour_map_basic silver 192-192-192 ;# #C0C0C0 + dict set WEB_colour_map_basic gray 128-128-128 ;# #808080 + dict set WEB_colour_map_basic black 0-0-0 ;# #000000 + dict set WEB_colour_map_basic red 255-0-0 ;# #FF0000 + dict set WEB_colour_map_basic maroon 128-0-0 ;# #800000 + dict set WEB_colour_map_basic yellow 255-255-0 ;# #FFFF00 + dict set WEB_colour_map_basic olive 128-128-0 ;# #808000 + dict set WEB_colour_map_basic lime 0-255-0 ;# #00FF00 + dict set WEB_colour_map_basic green 0-128-0 ;# #008000 + dict set WEB_colour_map_basic aqua 0-255-255 ;# #00FFFF + dict set WEB_colour_map_basic teal 0-128-128 ;# #008080 + dict set WEB_colour_map_basic blue 0-0-255 ;# #0000FF + dict set WEB_colour_map_basic navy 0-0-128 ;# #000080 + dict set WEB_colour_map_basic fuchsia 255-0-255 ;# #FF00FF + dict set WEB_colour_map_basic purple 128-0-128 ;# #800080 # -- --- --- #Pink colours - dict set WEB_colour_map mediumvioletred 199-21-133 ;# #C71585 - dict set WEB_colour_map deeppink 255-20-147 ;# #FF1493 - dict set WEB_colour_map palevioletred 219-112-147 ;# #DB7093 - dict set WEB_colour_map hotpink 255-105-180 ;# #FF69B4 - dict set WEB_colour_map lightpink 255-182-193 ;# #FFB6C1 - dict set WEB_colour_map pink 255-192-203 ;# #FFCOCB + variable WEB_colour_map_pink + dict set WEB_colour_map_pink mediumvioletred 199-21-133 ;# #C71585 + dict set WEB_colour_map_pink deeppink 255-20-147 ;# #FF1493 + dict set WEB_colour_map_pink palevioletred 219-112-147 ;# #DB7093 + dict set WEB_colour_map_pink hotpink 255-105-180 ;# #FF69B4 + dict set WEB_colour_map_pink lightpink 255-182-193 ;# #FFB6C1 + dict set WEB_colour_map_pink pink 255-192-203 ;# #FFCOCB # -- --- --- #Red colours - dict set WEB_colour_map darkred 139-0-0 ;# #8B0000 - #red - as above - dict set WEB_colour_map firebrick 178-34-34 ;# #B22222 - dict set WEB_colour_map crimson 220-20-60 ;# #DC143C - dict set WEB_colour_map indianred 205-92-92 ;# #CD5C5C - dict set WEB_colour_map lightcoral 240-128-128 ;# #F08080 - dict set WEB_colour_map salmon 250-128-114 ;# #FA8072 - dict set WEB_colour_map darksalmon 233-150-122 ;# #E9967A - dict set WEB_colour_map lightsalmon 255-160-122 ;# #FFA07A + variable WEB_colour_map_red + dict set WEB_colour_map_red darkred 139-0-0 ;# #8B0000 + dict set WEB_colour_map_red red 255-0-0 ;# #FF0000 + dict set WEB_colour_map_red firebrick 178-34-34 ;# #B22222 + dict set WEB_colour_map_red crimson 220-20-60 ;# #DC143C + dict set WEB_colour_map_red indianred 205-92-92 ;# #CD5C5C + dict set WEB_colour_map_red lightcoral 240-128-128 ;# #F08080 + dict set WEB_colour_map_red salmon 250-128-114 ;# #FA8072 + dict set WEB_colour_map_red darksalmon 233-150-122 ;# #E9967A + dict set WEB_colour_map_red lightsalmon 255-160-122 ;# #FFA07A # -- --- --- #Orange colours - dict set WEB_colour_map orangered 255-69-0 ;# #FF4500 - dict set WEB_colour_map tomato 255-99-71 ;# #FF6347 - dict set WEB_colour_map darkorange 255-140-0 ;# #FF8C00 - dict set WEB_colour_map coral 255-127-80 ;# #FF7F50 - dict set WEB_colour_map orange 255-165-0 ;# #FFA500 + variable WEB_colour_map_orange + dict set WEB_colour_map_orange orangered 255-69-0 ;# #FF4500 + dict set WEB_colour_map_orange tomato 255-99-71 ;# #FF6347 + dict set WEB_colour_map_orange darkorange 255-140-0 ;# #FF8C00 + dict set WEB_colour_map_orange coral 255-127-80 ;# #FF7F50 + dict set WEB_colour_map_orange orange 255-165-0 ;# #FFA500 # -- --- --- #Yellow colours - dict set WEB_colour_map darkkhaki 189-183-107 ;# #BDB76B - dict set WEB_colour_map gold 255-215-0 ;# #FFD700 - dict set WEB_colour_map khaki 240-230-140 ;# #F0E68C - dict set WEB_colour_map peachpuff 255-218-185 ;# #FFDAB9 - #yellow - as above - dict set WEB_colour_map palegoldenrod 238-232-170 ;# #EEE8AA - dict set WEB_colour_map moccasin 255-228-181 ;# #FFE4B5 - dict set WEB_colour_map papayawhip 255-239-213 ;# #FFEFD5 - dict set WEB_colour_map lightgoldenrodyeallow 250-250-210 ;# #FAFAD2 - dict set WEB_colour_map lemonchiffon 255-250-205 ;# #FFFACD - dict set WEB_colour_map lightyellow 255-255-224 ;# #FFFFE0 + variable WEB_colour_map_yellow + dict set WEB_colour_map_yellow darkkhaki 189-183-107 ;# #BDB76B + dict set WEB_colour_map_yellow gold 255-215-0 ;# #FFD700 + dict set WEB_colour_map_yellow khaki 240-230-140 ;# #F0E68C + dict set WEB_colour_map_yellow peachpuff 255-218-185 ;# #FFDAB9 + dict set WEB_colour_map_yellow yellow 255-255-0 ;# #FFFF00 + dict set WEB_colour_map_yellow palegoldenrod 238-232-170 ;# #EEE8AA + dict set WEB_colour_map_yellow moccasin 255-228-181 ;# #FFE4B5 + dict set WEB_colour_map_yellow papayawhip 255-239-213 ;# #FFEFD5 + dict set WEB_colour_map_yellow lightgoldenrodyeallow 250-250-210 ;# #FAFAD2 + dict set WEB_colour_map_yellow lemonchiffon 255-250-205 ;# #FFFACD + dict set WEB_colour_map_yellow lightyellow 255-255-224 ;# #FFFFE0 # -- --- --- #Brown colours #maroon as above - dict set WEB_colour_map brown 165-42-42 ;# #A52A2A - dict set WEB_colour_map saddlebrown 139-69-19 ;# #8B4513 - dict set WEB_colour_map sienna 160-82-45 ;# #A0522D - dict set WEB_colour_map chocolate 210-105-30 ;# #D2691E - dict set WEB_colour_map darkgoldenrod 184-134-11 ;# #B8860B - dict set WEB_colour_map peru 205-133-63 ;# #CD853F - dict set WEB_colour_map rosybrown 188-143-143 ;# #BC8F8F - dict set WEB_colour_map goldenrod 218-165-32 ;# #DAA520 - dict set WEB_colour_map sandybrown 244-164-96 ;# #F4A460 - dict set WEB_colour_map tan 210-180-140 ;# #D2B48C - dict set WEB_colour_map burlywood 222-184-135 ;# #DEB887 - dict set WEB_colour_map wheat 245-222-179 ;# #F5DEB3 - dict set WEB_colour_map navajowhite 255-222-173 ;# #FFDEAD - dict set WEB_colour_map bisque 255-228-196 ;# #FFEfC4 - dict set WEB_colour_map blanchedalmond 255-228-196 ;# #FFEfC4 - dict set WEB_colour_map cornsilk 255-248-220 ;# #FFF8DC + variable WEB_colour_map_brown + dict set WEB_colour_map_brown brown 165-42-42 ;# #A52A2A + dict set WEB_colour_map_brown saddlebrown 139-69-19 ;# #8B4513 + dict set WEB_colour_map_brown sienna 160-82-45 ;# #A0522D + dict set WEB_colour_map_brown chocolate 210-105-30 ;# #D2691E + dict set WEB_colour_map_brown darkgoldenrod 184-134-11 ;# #B8860B + dict set WEB_colour_map_brown peru 205-133-63 ;# #CD853F + dict set WEB_colour_map_brown rosybrown 188-143-143 ;# #BC8F8F + dict set WEB_colour_map_brown goldenrod 218-165-32 ;# #DAA520 + dict set WEB_colour_map_brown sandybrown 244-164-96 ;# #F4A460 + dict set WEB_colour_map_brown tan 210-180-140 ;# #D2B48C + dict set WEB_colour_map_brown burlywood 222-184-135 ;# #DEB887 + dict set WEB_colour_map_brown wheat 245-222-179 ;# #F5DEB3 + dict set WEB_colour_map_brown navajowhite 255-222-173 ;# #FFDEAD + dict set WEB_colour_map_brown bisque 255-228-196 ;# #FFEfC4 + dict set WEB_colour_map_brown blanchedalmond 255-228-196 ;# #FFEfC4 + dict set WEB_colour_map_brown cornsilk 255-248-220 ;# #FFF8DC # -- --- --- #Purple, violet, and magenta colours - dict set WEB_colour_map indigo 75-0-130 ;# #4B0082 - #purple as above - dict set WEB_colour_map darkmagenta 139-0-139 ;# #8B008B - dict set WEB_colour_map darkviolet 148-0-211 ;# #9400D3 - dict set WEB_colour_map darkslateblue 72-61-139 ;# #9400D3 - dict set WEB_colour_map blueviolet 138-43-226 ;# #8A2BE2 - dict set WEB_colour_map darkorchid 153-50-204 ;# #9932CC - #fuchsia as above - dict set WEB_colour_map magenta 255-0-255 ;# #FF00FF - same as fuchsia - dict set WEB_colour_map slateblue 106-90-205 ;# #6A5ACD - dict set WEB_colour_map mediumslateblue 123-104-238 ;# #7B68EE - dict set WEB_colour_map mediumorchid 186-85-211 ;# #BA5503 - dict set WEB_colour_map mediumpurple 147-112-219 ;# #9370DB - dict set WEB_colour_map orchid 218-112-214 ;# #DA70D6 - dict set WEB_colour_map violet 238-130-238 ;# #EE82EE - dict set WEB_colour_map plum 221-160-221 ;# #DDA0DD - dict set WEB_colour_map thistle 216-191-216 ;# #D88FD8 - dict set WEB_colour_map lavender 230-230-150 ;# #E6E6FA + variable WEB_colour_map_purple + dict set WEB_colour_map_purple indigo 75-0-130 ;# #4B0082 + dict set WEB_colour_map_purple purple 128-0-128 ;# #800080 + dict set WEB_colour_map_purple darkmagenta 139-0-139 ;# #8B008B + dict set WEB_colour_map_purple darkviolet 148-0-211 ;# #9400D3 + dict set WEB_colour_map_purple darkslateblue 72-61-139 ;# #9400D3 + dict set WEB_colour_map_purple blueviolet 138-43-226 ;# #8A2BE2 + dict set WEB_colour_map_purple darkorchid 153-50-204 ;# #9932CC + dict set WEB_colour_map_purple fuchsia 255-0-255 ;# #FF00FF + dict set WEB_colour_map_purple magenta 255-0-255 ;# #FF00FF - same as fuchsia + dict set WEB_colour_map_purple slateblue 106-90-205 ;# #6A5ACD + dict set WEB_colour_map_purple mediumslateblue 123-104-238 ;# #7B68EE + dict set WEB_colour_map_purple mediumorchid 186-85-211 ;# #BA5503 + dict set WEB_colour_map_purple mediumpurple 147-112-219 ;# #9370DB + dict set WEB_colour_map_purple orchid 218-112-214 ;# #DA70D6 + dict set WEB_colour_map_purple violet 238-130-238 ;# #EE82EE + dict set WEB_colour_map_purple plum 221-160-221 ;# #DDA0DD + dict set WEB_colour_map_purple thistle 216-191-216 ;# #D88FD8 + dict set WEB_colour_map_purple lavender 230-230-150 ;# #E6E6FA # -- --- --- #Blue colours - dict set WEB_colour_map midnightblue 25-25-112 ;# #191970 - #navy as above - dict set WEB_colour_map darkblue 0-0-139 ;# #00008B - dict set WEB_colour_map mediumblue 0-0-205 ;# #0000CD - #blue as above - dict set WEB_colour_map royalblue 65-105-225 ;# #4169E1 - dict set WEB_colour_map steelblue 70-130-180 ;# #4682B4 - dict set WEB_colour_map dodgerblue 30-144-255 ;# #1E90FF - dict set WEB_colour_map deepskyblue 0-191-255 ;# #00BFFF - dict set WEB_colour_map cornflowerblue 100-149-237 ;# #6495ED - dict set WEB_colour_map skyblue 135-206-235 ;# #87CEEB - dict set WEB_colour_map lightskyblue 135-206-250 ;# #87CEFA - dict set WEB_colour_map lightsteelblue 176-196-222 ;# #B0C4DE - dict set WEB_colour_map lightblue 173-216-230 ;# #ADD8E6 - dict set WEB_colour_map powderblue 176-224-230 ;# #B0E0E6 + variable WEB_colour_map_blue + dict set WEB_colour_map_blue midnightblue 25-25-112 ;# #191970 + dict set WEB_colour_map_blue navy 0-0-128 ;# #000080 + dict set WEB_colour_map_blue darkblue 0-0-139 ;# #00008B + dict set WEB_colour_map_blue mediumblue 0-0-205 ;# #0000CD + dict set WEB_colour_map_blue blue 0-0-255 ;# #0000FF + dict set WEB_colour_map_blue royalblue 65-105-225 ;# #4169E1 + dict set WEB_colour_map_blue steelblue 70-130-180 ;# #4682B4 + dict set WEB_colour_map_blue dodgerblue 30-144-255 ;# #1E90FF + dict set WEB_colour_map_blue deepskyblue 0-191-255 ;# #00BFFF + dict set WEB_colour_map_blue cornflowerblue 100-149-237 ;# #6495ED + dict set WEB_colour_map_blue skyblue 135-206-235 ;# #87CEEB + dict set WEB_colour_map_blue lightskyblue 135-206-250 ;# #87CEFA + dict set WEB_colour_map_blue lightsteelblue 176-196-222 ;# #B0C4DE + dict set WEB_colour_map_blue lightblue 173-216-230 ;# #ADD8E6 + dict set WEB_colour_map_blue powderblue 176-224-230 ;# #B0E0E6 # -- --- --- #Cyan colours #teal as above - dict set WEB_colour_map darkcyan 0-139-139 ;# #008B8B - dict set WEB_colour_map lightseagreen 32-178-170 ;# #20B2AA - dict set WEB_colour_map cadetblue 95-158-160 ;# #5F9EA0 - dict set WEB_colour_map darkturquoise 0-206-209 ;# #00CED1 - dict set WEB_colour_map mediumturquoise 72-209-204 ;# #48D1CC - dict set WEB_colour_map turquoise 64-224-208 ;# #40E0D0 - #aqua as above - dict set WEB_colour_map cyan 0-255-255 ;# #00FFFF - same as aqua - dict set WEB_colour_map aquamarine 127-255-212 ;# #7FFFD4 - dict set WEB_colour_map paleturquoise 175-238-238 ;# #AFEEEE - dict set WEB_colour_map lightcyan 224-255-255 ;# #E0FFFF + variable WEB_colour_map_cyan + dict set WEB_colour_map_cyan darkcyan 0-139-139 ;# #008B8B + dict set WEB_colour_map_cyan lightseagreen 32-178-170 ;# #20B2AA + dict set WEB_colour_map_cyan cadetblue 95-158-160 ;# #5F9EA0 + dict set WEB_colour_map_cyan darkturquoise 0-206-209 ;# #00CED1 + dict set WEB_colour_map_cyan mediumturquoise 72-209-204 ;# #48D1CC + dict set WEB_colour_map_cyan turquoise 64-224-208 ;# #40E0D0 + dict set WEB_colour_map_cyan aqua 0-255-255 ;# #00FFFF + dict set WEB_colour_map_cyan cyan 0-255-255 ;# #00FFFF - same as aqua + dict set WEB_colour_map_cyan aquamarine 127-255-212 ;# #7FFFD4 + dict set WEB_colour_map_cyan paleturquoise 175-238-238 ;# #AFEEEE + dict set WEB_colour_map_cyan lightcyan 224-255-255 ;# #E0FFFF # -- --- --- #Green colours - dict set WEB_colour_map darkgreen 0-100-0 ;# #006400 - #green as above - dict set WEB_colour_map darkolivegreen 85-107-47 ;# #55682F - dict set WEB_colour_map forestgreen 34-139-34 ;# #228B22 - dict set WEB_colour_map seagrean 46-139-87 ;# #2E8B57 - #olive as above - dict set WEB_colour_map olivedrab 107-142-35 ;# #6B8E23 - dict set WEB_colour_map mediumseagreen 60-179-113 ;# #3CB371 - dict set WEB_colour_map limegreen 50-205-50 ;# #32CD32 - #lime as above - dict set WEB_colour_map springgreen 0-255-127 ;# #00FF7F - dict set WEB_colour_map mediumspringgreen 0-250-154 ;# #00FA9A - dict set WEB_colour_map darkseagreen 143-188-143 ;# #8FBC8F - dict set WEB_colour_map mediumaquamarine 102-205-170 ;# #66CDAA - dict set WEB_colour_map yellowgreen 154-205-50 ;# #9ACD32 - dict set WEB_colour_map lawngreen 124-252-0 ;# #7CFC00 - dict set WEB_colour_map chartreuse 127-255-0 ;# #7FFF00 - dict set WEB_colour_map lightgreen 144-238-144 ;# #90EE90 - dict set WEB_colour_map greenyellow 173-255-47 ;# #ADFF2F - dict set WEB_colour_map palegreen 152-251-152 ;# #98FB98 + variable WEB_colour_map_green + dict set WEB_colour_map_green darkgreen 0-100-0 ;# #006400 + dict set WEB_colour_map_green green 0-128-0 ;# #008000 + dict set WEB_colour_map_green darkolivegreen 85-107-47 ;# #55682F + dict set WEB_colour_map_green forestgreen 34-139-34 ;# #228B22 + dict set WEB_colour_map_green seagreen 46-139-87 ;# #2E8B57 + dict set WEB_colour_map_green olive 128-128-0 ;# #808000 + dict set WEB_colour_map_green olivedrab 107-142-35 ;# #6B8E23 + dict set WEB_colour_map_green mediumseagreen 60-179-113 ;# #3CB371 + dict set WEB_colour_map_green limegreen 50-205-50 ;# #32CD32 + dict set WEB_colour_map_green lime 0-255-0 ;# #00FF00 + dict set WEB_colour_map_green springgreen 0-255-127 ;# #00FF7F + dict set WEB_colour_map_green mediumspringgreen 0-250-154 ;# #00FA9A + dict set WEB_colour_map_green darkseagreen 143-188-143 ;# #8FBC8F + dict set WEB_colour_map_green mediumaquamarine 102-205-170 ;# #66CDAA + dict set WEB_colour_map_green yellowgreen 154-205-50 ;# #9ACD32 + dict set WEB_colour_map_green lawngreen 124-252-0 ;# #7CFC00 + dict set WEB_colour_map_green chartreuse 127-255-0 ;# #7FFF00 + dict set WEB_colour_map_green lightgreen 144-238-144 ;# #90EE90 + dict set WEB_colour_map_green greenyellow 173-255-47 ;# #ADFF2F + dict set WEB_colour_map_green palegreen 152-251-152 ;# #98FB98 # -- --- --- #White colours - dict set WEB_colour_map mistyrose 255-228-225 ;# #FFE4E1 - dict set WEB_colour_map antiquewhite 250-235-215 ;# #FAEBD7 - dict set WEB_colour_map linen 250-240-230 ;# #FAF0E6 - dict set WEB_colour_map beige 245-245-220 ;# #F5F5DC - dict set WEB_colour_map whitesmoke 245-245-245 ;# #F5F5F5 - dict set WEB_colour_map lavenderblush 255-240-245 ;# #FFF0F5 - dict set WEB_colour_map oldlace 253-245-230 ;# #FDF5E6 - dict set WEB_colour_map aliceblue 240-248-255 ;# #F0F8FF - dict set WEB_colour_map seashell 255-245-238 ;# #FFF5EE - dict set WEB_colour_map ghostwhite 248-248-255 ;# #F8F8FF - dict set WEB_colour_map honeydew 240-255-240 ;# #F0FFF0 - dict set WEB_colour_map floralwhite 255-250-240 ;# #FFFAF0 - dict set WEB_colour_map azure 240-255-255 ;# #F0FFFF - dict set WEB_colour_map mintcream 245-255-250 ;# #F5FFFA - dict set WEB_colour_map snow 255-250-250 ;# #FFFAFA - dict set WEB_colour_map ivory 255-255-240 ;# #FFFFF0 - #white as above + variable WEB_colour_map_white + dict set WEB_colour_map_white mistyrose 255-228-225 ;# #FFE4E1 + dict set WEB_colour_map_white antiquewhite 250-235-215 ;# #FAEBD7 + dict set WEB_colour_map_white linen 250-240-230 ;# #FAF0E6 + dict set WEB_colour_map_white beige 245-245-220 ;# #F5F5DC + dict set WEB_colour_map_white whitesmoke 245-245-245 ;# #F5F5F5 + dict set WEB_colour_map_white lavenderblush 255-240-245 ;# #FFF0F5 + dict set WEB_colour_map_white oldlace 253-245-230 ;# #FDF5E6 + dict set WEB_colour_map_white aliceblue 240-248-255 ;# #F0F8FF + dict set WEB_colour_map_white seashell 255-245-238 ;# #FFF5EE + dict set WEB_colour_map_white ghostwhite 248-248-255 ;# #F8F8FF + dict set WEB_colour_map_white honeydew 240-255-240 ;# #F0FFF0 + dict set WEB_colour_map_white floralwhite 255-250-240 ;# #FFFAF0 + dict set WEB_colour_map_white azure 240-255-255 ;# #F0FFFF + dict set WEB_colour_map_white mintcream 245-255-250 ;# #F5FFFA + dict set WEB_colour_map_white snow 255-250-250 ;# #FFFAFA + dict set WEB_colour_map_white ivory 255-255-240 ;# #FFFFF0 + dict set WEB_colour_map_white white 255-255-255 ;# #FFFFFF # -- --- --- #Gray and black colours - #black as above - dict set WEB_colour_map darkslategray 47-79-79 ;# #2F4F4F - dict set WEB_colour_map dimgray 105-105-105 ;# #696969 - dict set WEB_colour_map slategray 112-128-144 ;# #708090 - #gray as above - dict set WEB_colour_map lightslategray 119-136-153 ;# #778899 - dict set WEB_colour_map darkgray 169-169-169 ;# #A9A9A9 - dict set WEB_colour_map silver 192-192-192 ;# #C0C0C0 - dict set WEB_colour_map lightgray 211-211-211 ;# #D3D3D3 - dict set WEB_colour_map gainsboro 220-220-220 ;# #DCDCDC - + variable WEB_colour_map_gray + dict set WEB_colour_map_gray black 0-0-0 ;# #000000 + dict set WEB_colour_map_gray darkslategray 47-79-79 ;# #2F4F4F + dict set WEB_colour_map_gray dimgray 105-105-105 ;# #696969 + dict set WEB_colour_map_gray slategray 112-128-144 ;# #708090 + dict set WEB_colour_map_gray gray 128-128-128 ;# #808080 + dict set WEB_colour_map_gray lightslategray 119-136-153 ;# #778899 + dict set WEB_colour_map_gray darkgray 169-169-169 ;# #A9A9A9 + dict set WEB_colour_map_gray silver 192-192-192 ;# #C0C0C0 + dict set WEB_colour_map_gray lightgray 211-211-211 ;# #D3D3D3 + dict set WEB_colour_map_gray gainsboro 220-220-220 ;# #DCDCDC + + set WEB_colour_map [dict merge\ + $WEB_colour_map_basic\ + $WEB_colour_map_pink\ + $WEB_colour_map_red\ + $WEB_colour_map_orange\ + $WEB_colour_map_yellow\ + $WEB_colour_map_brown\ + $WEB_colour_map_purple\ + $WEB_colour_map_blue\ + $WEB_colour_map_cyan\ + $WEB_colour_map_green\ + $WEB_colour_map_white\ + $WEB_colour_map_gray\ + ] #we should be able to use WEB_colour_map as a base and override only the conflicts for X11 colours ? Review - check if this is true variable X11_colour_map @@ -1260,16 +1288,20 @@ namespace eval punk::ansi { ] variable TERM_colour_map set TERM_colour_map [dict create] + variable TERM_colour_map_reverse + set TERM_colour_map_reverse [dict create] set cidx 0 foreach cname $xterm_names { if {![dict exists $TERM_colour_map $cname]} { dict set TERM_colour_map $cname $cidx + dict set TERM_colour_map_reverse $cidx $cname } else { set did_rename 0 #start suffixes at '-b'. The base name could be considered the '-a' version - but we don't create it. foreach {suffix} {b c} { if {![dict exists $TERM_colour_map $cname-$suffix]} { dict set TERM_colour_map $cname-$suffix $cidx + dict set TERM_colour_map_reverse $cidx $cname-$suffix set did_rename 1 break } @@ -1284,7 +1316,7 @@ namespace eval punk::ansi { - #colour_hex2dec + #colour_hex2ansidec #conversion of hex to format directly pluggable to ansi rgb format (colon separated e.g for foreground we need "38;2;$r;$g;$b" so we return $r;$g;$b) #we want to support arbitrary rgb values specified in hex - so a table of 16M+ is probably not a great idea #hex zero-padded - canonically upper case but mixed or lower accepted @@ -1293,10 +1325,23 @@ namespace eval punk::ansi { # set webhex [::join [format %02X%02X%02X {*}$dectriple] ;# e.g 808080, FFFFFF, 000000 # dict set HEX_colour_map $webhex [join $dectriple {;}] #} - proc colour_hex2dec {hex6} { + proc colour_hex2ansidec {hex6} { return [join [::scan $hex6 %2X%2X%2X] {;}] } + #convert between hex and decimal as used in the a+ function + # eg dec-dec-dec <-> #HHHHHH + #allow hex to be specified with or without leading # + proc colour_hex2dec {hex6} { + set hex6 [string map [list # ""] $hex6] + return [join [::scan $hex6 %2X%2X%2X] {-}] + } + proc colour_dec2hex {decimalcolourstring} { + set dec [string map [list {;} - , -] $decimalcolourstring] + set declist [split $dec -] + set hex #[format %02X%02X%02X {*}$declist] + } + proc get_sgr_map {} { variable SGR_map return $SGR_map @@ -1318,7 +1363,12 @@ namespace eval punk::ansi { set bg [textblock::block 39 3 "[a+ $bgname] [a]"] set colourmap "" for {set i 8} {$i <= 15} {incr i} { - append colourmap "_[a+ black normal 48\;5\;$i] $i [a]" ;#black normal is blacker than black bold - which often displays as a grey + if {$i == 8} { + set fg "bold white" + } else { + set fg "black normal" ;#black normal is often blacker than black bold - which can display as a grey + } + append colourmap "_[a+ {*}$fg 48\;5\;$i] $i [a]" } set map2 [overtype::left -transparent _ $bg "\n$colourmap"] return $map2 @@ -1326,7 +1376,7 @@ namespace eval punk::ansi { proc colourtable_216 {} { package require textblock set clist [list] - set fg black + set fg "black" for {set i 16} {$i <=231} {incr i} { if {$i % 18 == 16} { if {$fg eq "black"} { @@ -1344,15 +1394,29 @@ namespace eval punk::ansi { return $t } + #1st 16 colours of 256 - match SGR colours + proc colourblock_16 {} { + set out "" + set fg "bold white" + for {set i 0} {$i <= 15} {incr i} { + #8 is black - so start black fg at 9 + if {$i > 8} { + set fg "web-black" + } + append out "[a+ {*}$fg Term$i][format %3s $i] " + } + return $out[a] + } + #216 colours of 256 proc colourblock_216 {} { set out "" - set fg black + set fg "web-black" for {set i 16} {$i <=231} {incr i} { if {$i % 18 == 16} { - if {$fg eq "black"} { - set fg "bold white" + if {$fg eq "web-black"} { + set fg "web-white" } else { - set fg "black" + set fg "web-black" } set br "\n" } else { @@ -1360,7 +1424,127 @@ namespace eval punk::ansi { } append out "$br[a+ {*}$fg Term$i][format %3s $i] " } - return $out + append out [a] + return [string trimleft $out \n] + } + proc colourtable_216_names {} { + set out "" + #use the reverse lookup dict - the original xterm_names list has duplicates - we want the disambiguated (potentially suffixed) names + variable TERM_colour_map_reverse + set rows [list] + set row [list] + set fg "web-black" + set t [textblock::class::table new] + $t configure -show_seps 0 -show_edge 0 + for {set i 16} {$i <=231} {incr i} { + if {$i % 9 == 7} { + if {$fg eq "web-black"} { + set fg "web-white" + } else { + set fg "web-black" + } + if {[llength $row]} { + lappend row "[a+ {*}$fg Term$i][format %3s $i] [dict get $TERM_colour_map_reverse $i]" + lappend rows $row + set row [list] + } + } else { + lappend row "[a+ {*}$fg Term$i][format %3s $i] [dict get $TERM_colour_map_reverse $i]" + } + #append out "$br[a+ {*}$fg Term$i][format %3s $i] " + } + foreach r $rows { + $t add_row $r + } + append out [$t print] + $t destroy + append out [a] + return [string trimleft $out \n] + } + #24 greys of 256 + proc colourblock_24 {} { + set out "" + set fg "bold white" + for {set i 232} {$i <= 255} {incr i} { + if {$i > 243} { + set fg "web-black" + } + append out "[a+ {*}$fg Term$i][format %3s $i] " + } + return $out[a] + + } + #set WEB_colour_map [dict merge\ + # $WEB_colour_map_basic\ + # $WEB_colour_map_pink\ + # $WEB_colour_map_red\ + # $WEB_colour_map_orange\ + # $WEB_colour_map_yellow\ + # $WEB_colour_map_brown\ + # $WEB_colour_map_purple\ + # $WEB_colour_map_blue\ + # $WEB_colour_map_cyan\ + # $WEB_colour_map_green\ + # $WEB_colour_map_white\ + # $WEB_colour_map_gray\ + #] + proc colourtable_web {{groups *}} { + set all_groupnames [list basic pink red orange yellow brown purple blue cyan green white gray] + switch -- $groups { + "" - * { + set show_groups $all_groupnames + } + ? { + return "Web group names: $all_groupnames" + } + default { + foreach g $groups { + if {$g ni $all_groupnames} { + error "colourtable_web group name '$g' not known. Known colour groups: $all_groupnames" + } + } + set show_groups $groups + } + } + set grouptables [list] + set white_fg_list [list\ + mediumvioletred deeppink\ + darkred red firebrick crimson indianred\ + orangered\ + maroon brown saddlebrown sienna\ + indigo purple darkmagenta darkviolet darkslateblue blueviolet darkorchid fuchsia magenta slateblue mediumslateblue\ + midnightblue navy darkblue mediumblue blue royalblue steelblue dodgerblue\ + teal darkcyan\ + darkgreen green darkolivegreen forestgreen seagreen olive olivedrab\ + black darkslategray dimgray slategray\ + ] + foreach g $show_groups { + #upvar WEB_colour_map_$g map_$g + variable WEB_colour_map_$g + set t [textblock::class::table new] + $t configure -show_edge 0 -show_seps 0 -show_header 1 + dict for {cname cdec} [set WEB_colour_map_$g] { + $t add_row [list "$cname " "[colour_dec2hex $cdec] " $cdec] + if {$cname in $white_fg_list} { + set fg "web-white" + } else { + set fg "web-black" + } + $t configure_row [expr {[$t row_count]-1}] -ansibase [a+ Rgb-$cdec $fg] + } + $t configure_column 0 -headers [list "[string totitle $g] colours"] + $t configure_column 0 -header_colspans [list all] + $t configure -ansibase_header [a+ Web-white web-black] + lappend grouptables [$t print] + $t destroy + } + #set displaytable [textblock::class::table new] + set displaytable [textblock::list_as_table 3 $grouptables -return object] + $displaytable configure -show_header 0 -show_vseps 0 + #return $displaytable + set result [$displaytable print] + $displaytable destroy + return $result } proc a? {args} { #*** !doctools @@ -1371,20 +1555,60 @@ namespace eval punk::ansi { if {![llength $args]} { set out "" - append out $SGR_setting_map \n - append out $SGR_colour_map \n - + set indent " " + set RST [a] + append out "[a+ web-white]Standard SGR colours and attributes [a]" \n + set settings_applied $SGR_setting_map + set strmap [list] + dict for {k v} $SGR_setting_map { + switch -- $k { + bold - dim - italic - doubleunderline - blink - fastblink - strike - overline - framecircle { + lappend strmap " $k " " [a+ $k]$k$RST " + } + underline - reverse - frame { + #1st coloumn - no leading space + lappend strmap "$k " "[a+ $k]$k$RST " + } + noreverse { + #undo mapping of 'reverse' within this string + lappend strmap "$k" "noreverse" + } + } + } + set settings_applied [string trim $SGR_setting_map \n] try { package require overtype ;# circular dependency - many components require overtype. Here we only need it for nice layout in the a? query proc - so we'll do a soft-dependency by only loading when needed and also wrapping in a try + package require textblock + + append out [textblock::join $indent [string map $strmap $settings_applied]] \n + append out [textblock::join $indent [string trim $SGR_colour_map \n]] \n + append out [textblock::join $indent "Example: \[a+ bold red White underline\]text\[a] -> [a+ bold red White underline]text[a]"] \n \n set bgname "White" set map1 [colourmap1 $bgname] set map1 [overtype::centre -transparent 1 $map1 "[a black $bgname]Standard colours[a]"] set map2 [colourmap2 $bgname] set map2 [overtype::centre -transparent 1 $map2 "[a black $bgname]High-intensity colours[a]"] - append out [textblock::join $map1 " " $map2] \n - #append out $map1[a] \n - #append out $map2[a] \n - append out [colourblock_216] + 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 [textblock::join $indent [colourblock_216]] \n + append out "[a+ web-white]24 Greyscale colours[a]" \n + append out [textblock::join $indent [colourblock_24]] \n + append out \n + append out [textblock::join $indent "Example: \[a+ Term-92 term-49\]text\[a] -> [a+ Term-92 term-49]text[a]"] \n + append out [textblock::join $indent "Example: \[a+ Term-lightsteelblue term-gold1\]text\[a] -> [a+ Term-lightsteelblue term-gold1]text[a]"] \n + append out [textblock::join $indent "Example: \[a+ term-lightsteelblue Term-gold1\]text\[a] -> [a+ term-lightsteelblue Term-gold1]text[a]"] \n + append out \n + append out "[a+ web-white]16 Million colours[a]" \n + #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#C71585\]text\[a] -> [a+ Rgb#C71585]text[a]"] \n + append out \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 specific colour groups use: a? web groupname1 groupname2..."] \n + append out [textblock::join $indent "Valid group names (can be listed in any order): basic pink red orange yellow brown purple blue cyan green white grey"] \n + append out \n + append out [textblock::join $indent "Example: \[a+ Web-springgreen web-crimson\]text\[a] -> [a+ Web-springgreen web-coral]text[a]"] \n } on error {result options} { @@ -1394,6 +1618,17 @@ namespace eval punk::ansi { return $out } } else { + switch -- [lindex $args 0] { + term { + return [colourtable_216_names] + } + web { + return [colourtable_web [lrange $args 1 end]] + } + x11 { + return "Display not implemented. Mostly same as web" + } + } set result [list] set map [dict merge $SGR_setting_map $SGR_colour_map] set rmap [lreverse $map] diff --git a/src/modules/punk/char-999999.0a1.0.tm b/src/modules/punk/char-999999.0a1.0.tm index 2dce356..f36a2db 100644 --- a/src/modules/punk/char-999999.0a1.0.tm +++ b/src/modules/punk/char-999999.0a1.0.tm @@ -1852,15 +1852,27 @@ namespace eval punk::char { #intended for single grapheme - but will work for multiple #cannot contain ansi or newlines #(a cache of ansifreestring_width calls - as these are quite regex heavy) - proc grapheme_width_cached {ch} { + #review - effective memory leak on longrunning programs if never cleared + #tradeoff in fragmenting cache and reducing efficiency vs ability to clear in a scoped manner + proc grapheme_width_cached {ch {key ""}} { variable grapheme_widths - if {[dict exists $grapheme_widths $ch]} { - return [dict get $grapheme_widths $ch] + #if key eq "*" - we won't be able to clear that cache individually. Perhaps that's ok + if {[dict exists $grapheme_widths $key $ch]} { + return [dict get $grapheme_widths $key $ch] } set width [punk::char::ansifreestring_width $ch] ;#review - can we provide faster version if we know it's a single grapheme rather than a string? (grapheme is still a string as it may have combiners/diacritics) - dict set grapheme_widths $ch $width + dict set grapheme_widths $key $ch $width return $width } + proc grapheme_width_cache_clear {key} { + variable grapheme_widths + if {$key eq "*} { + set grapheme_widths [dict create] + } else { + dict unset grapheme_widths $key + } + return + } #no char_width - use grapheme_width terminology to be clearer proc grapheme_width {char} { error "grapheme_width unimplemented - use ansifreestring_width" diff --git a/src/modules/punk/lib-999999.0a1.0.tm b/src/modules/punk/lib-999999.0a1.0.tm index 00218d0..53d586b 100644 --- a/src/modules/punk/lib-999999.0a1.0.tm +++ b/src/modules/punk/lib-999999.0a1.0.tm @@ -179,6 +179,54 @@ namespace eval punk::lib::compat { } + #slight isolation - varnames don't leak - but calling context vars can be affected + proc lmaptcl2 {varnames list script} { + set result [list] + set values [list] + foreach v $varnames { + lappend values "\$$v" + } + set linkvars [uplevel 1 [list info vars]] + set nscaller [uplevel 1 [list namespace current]] + + set apply_script "" + foreach vname $linkvars { + append apply_script [string map [list %vname% $vname]\ + {upvar 2 %vname% %vname%}\ + ] \n + } + append apply_script $script \n + + #puts "--> $apply_script" + foreach $varnames $list { + lappend result [apply\ + [list\ + $varnames\ + $apply_script\ + $nscaller\ + ] {*}[subst $values]\ + ] + } + return $result + } + + if {"::lmap" ne [info commands ::lmap]} { + #puts stderr "Warning - no built-in lpop" + interp alias {} lpop {} ::punk::lib::compat::lmaptcl + } + #lmap came in Tcl 8.6 - so probably not much need for a tcl forward compatibility version - but here it is anyway + proc lmaptcl {varnames list script} { + set result [list] + set varlist [list] + foreach varname $varnames { + upvar 1 $varname var_$varname ;#ensure no collisions with vars in this proc + lappend varlist var_$varname + } + foreach $varlist $list { + lappend result [uplevel 1 $script] + } + return $result + } #*** !doctools #[list_end] [comment {--- end definitions namespace punk::lib::compat ---}] @@ -196,6 +244,99 @@ namespace eval punk::lib { #[para] Core API functions for punk::lib #[list_begin definitions] + #The closure-like behaviour is *very* slow especially when called from a context such as the global namespace with lots of vars and large arrays such as ::env + proc lmapflat_closure {varnames list script} { + set result [list] + set values [list] + foreach v $varnames { + lappend values "\$$v" + } + # -- --- --- + #capture - use uplevel 1 or namespace eval depending on context + set capture [uplevel 1 { + apply { varnames { + set capturevars [dict create] + set capturearrs [dict create] + foreach fullv $varnames { + set v [namespace tail $fullv] + upvar 1 $v var + if {[info exists var]} { + if {(![array exists var])} { + dict set capturevars $v $var + } else { + dict set capturearrs capturedarray_$v [array get var] + } + } else { + #A variable can show in the results for 'info vars' but still not 'exist'. e.g a 'variable x' declaration in the namespace where the variable has never been set + } + } + return [dict create vars $capturevars arrs $capturearrs] + } } [info vars] + } ] + # -- --- --- + set cvars [dict get $capture vars] + set carrs [dict get $capture arrs] + set apply_script "" + foreach arrayalias [dict keys $carrs] { + set realname [string range $arrayalias [string first _ $arrayalias]+1 end] + append apply_script [string map [list %realname% $realname %arrayalias% $arrayalias] { + array set %realname% [set %arrayalias%][unset %arrayalias%] + }] + } + + append apply_script [string map [list %script% $script] { + #foreach arrayalias [info vars capturedarray_*] { + # set realname [string range $arrayalias [string first _ $arrayalias]+1 end] + # array set $realname [set $arrayalias][unset arrayalias] + #} + #return [eval %script%] + %script% + }] + #puts "--> $apply_script" + foreach $varnames $list { + lappend result {*}[apply\ + [list\ + [concat $varnames [dict keys $cvars] [dict keys $carrs] ]\ + $apply_script\ + ] {*}[subst $values] {*}[dict values $cvars] {*}[dict values $carrs] ] + } + return $result + } + #link version - can write to vars in calling context - but keeps varnames themselves isolated + #performance much better than capture version - but still a big price to pay for the isolation + proc lmapflat_link {varnames list script} { + set result [list] + set values [list] + foreach v $varnames { + lappend values "\$$v" + } + set linkvars [uplevel 1 [list info vars]] + set nscaller [uplevel 1 [list namespace current]] + + set apply_script "" + foreach vname $linkvars { + append apply_script [string map [list %vname% $vname]\ + {upvar 2 %vname% %vname%}\ + ] \n + } + append apply_script $script \n + + #puts "--> $apply_script" + foreach $varnames $list { + lappend result {*}[apply\ + [list\ + $varnames\ + $apply_script\ + $nscaller\ + ] {*}[subst $values]\ + ] + } + return $result + } + + proc lmapflat {varnames list script} { + concat {*}[uplevel 1 [list lmap $varnames $list $script]] + } proc dict_getdef {dictValue args} { if {[llength $args] < 1} { diff --git a/src/modules/punk/ns-999999.0a1.0.tm b/src/modules/punk/ns-999999.0a1.0.tm index 783f116..f42f3ed 100644 --- a/src/modules/punk/ns-999999.0a1.0.tm +++ b/src/modules/punk/ns-999999.0a1.0.tm @@ -1580,7 +1580,7 @@ namespace eval punk::ns { #review - upvar in apply within ns eval vs direct access of ${ns}::varname set capture [namespace eval $ns { apply { varnames { - while {"prev_args_[incr n]" in $varnames} {} + while {"prev_args[incr n]" in $varnames} {} set capturevars [dict create] set capturearrs [dict create] foreach fullv $varnames { diff --git a/src/modules/textblock-999999.0a1.0.tm b/src/modules/textblock-999999.0a1.0.tm index 590aa67..7dae99f 100644 --- a/src/modules/textblock-999999.0a1.0.tm +++ b/src/modules/textblock-999999.0a1.0.tm @@ -1294,7 +1294,7 @@ namespace eval textblock { tlc [dict get $fdef_header hlt]\ blc [dict get $fdef_header hlb]\ ] - set framedef_leftbox [textblock::framedef $ftype_header left] + set framedef_leftbox [textblock::framedef $ftype_header -joins left] set column_width_cache [dict create] @@ -1347,7 +1347,7 @@ namespace eval textblock { set next_spanlist [dict get $all_colspans [expr {$h+1}]] set spanbelow [lindex $next_spanlist $cidx] if {$spanbelow == 0} { - #we don't want a down-join for blc - retrieve a framedef with only left joins + #we don't want a down-join for blc - use a framedef with only left joins dict set startmap blc [dict get $framedef_leftbox blc] } } else { @@ -1355,8 +1355,8 @@ namespace eval textblock { } } - #todo - multiline header cells + multiple header lines (will be more useful when colspans implemented) - set header_cell_startspan [textblock::frame -width [expr {$colwidth+2}] -type [dict get $ftypes header]\ + #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]\ -ansibase $ansibase_header -ansiborder $ansiborder_final\ -boxlimits $hlims -boxmap $startmap -joins $header_joins $hval\ ] @@ -1407,12 +1407,12 @@ namespace eval textblock { if {[llength $next_spanlist]} { set spanbelow [lindex $next_spanlist $spancol] if {$spanbelow != 0} { - set downbox [textblock::framedef $ftype_header {down}] + set downbox [textblock::framedef $ftype_header -joins {down}] dict set this_span_map blc [dict get $downbox hlbj] ;#horizontal line bottom with down join - to same frametype } } else { #join to body - set downbox [textblock::framedef $ftype_header [list down-$fname_body]] + set downbox [textblock::framedef $ftype_header -joins [list down-$fname_body]] dict set this_span_map blc [dict get $downbox hlbj] ;#horizontal line bottom with down join - from header frametype to body frametype } } @@ -1478,6 +1478,7 @@ namespace eval textblock { } 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 + #-usecache 1 ok set hblock [textblock::frame -type $spacemap -boxlimits $hlims -ansibase $ansibase_header $hval] set spanned_frame [overtype::left -experimental test_mode -transparent 1 $spanned_frame $hblock] } @@ -1522,6 +1523,7 @@ namespace eval textblock { set h_lines [lrepeat $rowh $bline] 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 set header_frame [textblock::frame -width [expr {$padwidth+2}] -type [dict get $ftypes header]\ -ansibase $ansibase_header \ -boxlimits $hlims -boxmap $spacemap $hcell_blank\ @@ -2745,7 +2747,40 @@ namespace eval textblock { if {$width eq "auto"} { set width $datawidth } + set lines [list] + + if {$block eq ""} { + #we need to treat as a line + return [string repeat $padchar $width] + } + + #review - tcl format can only pad with zeros or spaces? + #experimental fallback to supposedly faster simpler 'format' but we still need to compensate for double-width or non-printing chars + if 0 { + #review - surprisingly, this doesn't seem to be a performance win + #No detectable diff on small blocks - slightly worse on large blocks + if {($padchar eq " " || $padchar eq "0") && ![punk::ansi::ta::detect $block]} { + #This will still be wrong for example with diacritics on terminals that don't collapse the space following a diacritic, and don't correctly report cursor position + #(i.e as at 2024 - lots of them) wezterm on windows at least does the right thing. + set block [string map [list \r\n \n] $block] + if {$which eq "l"} { + set fmt "%+${padchar}*s" + } else { + set fmt "%-${padchar}*s" + } + foreach ln [split $block \n] { + #set lnwidth [punk::char::ansifreestring_width $ln] + set lnwidth [punk::char::grapheme_width_cached $ln] + set lnlen [string length $ln] + set diff [expr $lnwidth - $lnlen] + set trickwidth [expr {$width - $diff}] ;#may 'subtract' a positive or negative int (ie will add to trickwidth if negative) + lappend lines [format $fmt $trickwidth $ln] + } + return [::join $lines \n] + } + } + set lnum 0 set parts [punk::ansi::ta::split_codes $block] set line_chunks [list] @@ -2763,7 +2798,8 @@ namespace eval textblock { set p 0 foreach pl $partlines { lappend line_chunks $pl - incr line_len [punk::char::ansifreestring_width $pl] + #incr line_len [punk::char::ansifreestring_width $pl] + incr line_len [punk::char::grapheme_width_cached $pl] ;#memleak if {$p != $last} { #do padding set missing [expr {$width - $line_len}] @@ -3032,7 +3068,8 @@ namespace eval textblock { set row "" foreach colidx $colindices { #we know we have a single line, no ansi in underlay and no overflow required - so we can use renderline directly - append row [overtype::renderline -width $w($colidx) -insert_mode 0 -info 0 $c($colidx) $v($colidx)] + #append row [overtype::renderline -width $w($colidx) -insert_mode 0 -info 0 $c($colidx) $v($colidx)] + append row [textblock::pad $v($colidx) -width $w($colidx) -which right -padchar " "] } lappend outlines $row } @@ -3185,11 +3222,33 @@ namespace eval textblock { return [dict create category predefined type $f] } } - proc framedef {f {joins ""}} { + variable framedef_cache [dict create] + proc framedef {f args} { #unicode box drawing only provides enough characters for seamless joining of unicode boxes light and heavy. #e.g with characters such as \u2539 Box Drawings Right Light and Left Up Heavy. #the double glyphs in box drawing can do a limited set of joins to light lines - but not enough for seamless table layouts. #the arc set can't even join to itself e.g with curved equivalents of T-like shapes + variable framedef_cache + set cache_key [concat $f $args] + if {[dict exists $framedef_cache $cache_key]} { + return [dict get $framedef_cache $cache_key] + } + set defaults [dict create\ + -joins ""\ + -boxonly 0\ + ] + dict for {k v} $args { + switch -- $k { + -joins - -boxonly {} + default { + error "framedef unknown option '$k'. Known options [dict keys $args]" + } + } + } + set opts [dict merge $defaults $args] + set joins [dict get $opts -joins] + set boxonly [dict get $opts -boxonly] + #sorted order down left right up #1 x choose 4 @@ -4297,16 +4356,49 @@ namespace eval textblock { set vlrj $vlr } } - return [dict create\ - tlc $tlc hlt $hlt trc $trc\ - vll $vll vlr $vlr\ - blc $blc hlb $hlb brc $brc\ - hltj $hltj\ - hlbj $hlbj\ - vllj $vllj\ - vlrj $vlrj\ - ] + if {$boxonly} { + set result [dict create\ + tlc $tlc hlt $hlt trc $trc\ + vll $vll vlr $vlr\ + blc $blc hlb $hlb brc $brc\ + ] + dict set framedef_cache $cache_key $result + return $result + } else { + set result [dict create\ + tlc $tlc hlt $hlt trc $trc\ + vll $vll vlr $vlr\ + blc $blc hlb $hlb brc $brc\ + hltj $hltj\ + hlbj $hlbj\ + vllj $vllj\ + vlrj $vlrj\ + ] + dict set framedef_cache $cache_key $result + return $result + } } + + variable frame_cache + set frame_cache [dict create] + proc frame_cache {{action ""}} { + if {$action ni [list clear ""]} { + error "frame_cache action '$action' not understood. Valid actions: clear" + } + variable frame_cache + set out "" + dict for {k v} $frame_cache { + lassign $v _f frame _used used + append out [textblock::join $k " " $frame " " $used]\n + } + if {$action eq "clear"} { + set frame_cache [dict create] + append out \nCLEARED + } + return $out + } + #options before content argument - which is allowed to be absent + #frame performance (noticeable with complex tables even of modest size) is improved significantly by frame_cache - but is still (2024) a fairly expensive operation. proc frame {args} { variable frametypes set expect_optval 0 @@ -4356,11 +4448,14 @@ namespace eval textblock { -ansibase ""\ -align "left"\ -ellipsis 1\ + -usecache 1\ + -buildcache 1\ ] + #use -buildcache 1 with -usecache 0 for debugging cache issues so we can inspect using textblock::frame_cache set opts [dict merge $defaults $arglist] foreach {k v} $opts { switch -- $k { - -etabs - -type - -boxlimits - -boxmap - -joins - -title - -subtitle - -width - -height - -ansiborder - -ansibase - -align - -ellipsis {} + -etabs - -type - -boxlimits - -boxmap - -joins - -title - -subtitle - -width - -height - -ansiborder - -ansibase - -align - -ellipsis - -usecache - -buildcache {} default { error "frame option '$k' not understood. Valid options are [dict keys $defaults]" } @@ -4372,6 +4467,10 @@ namespace eval textblock { set opt_boxlimits [dict get $opts -boxlimits] set opt_joins [dict get $opts -joins] set opt_boxmap [dict get $opts -boxmap] + set opt_usecache [dict get $opts -usecache] + set opt_buildcache [dict get $opts -buildcache] + set usecache $opt_usecache ;#may need to override + set buildcache $opt_buildcache set custom_keys [list hl hlt hlb vl vll vlr tlc trc blc brc] set known_frametypes $frametypes ;# light, heavey etc as defined in textblock::frametypes variable @@ -4481,7 +4580,7 @@ namespace eval textblock { switch -- $opt_align { left - right - centre - center {} default { - error "frame option -align must be left|right|centre|center - received: $$opt_align" + error "frame option -align must be left|right|centre|center - received: $opt_align" } } #these are all valid commands for overtype:: @@ -4532,19 +4631,84 @@ namespace eval textblock { if {$contentheight == 0 && $contentwidth == 0} { set has_contents 0 } - #todo - render it with vertical overflow so we can process ansi moves? #set linecount [textblock::height $contents] set linecount $contentheight + + # -- --- --- --- --- --- --- --- --- + variable frame_cache + #review - custom frame affects contentwidth - exclude from caching? + #set cache_key [concat $arglist $contentwidth $contentheight] + set hashables [concat $arglist $contentwidth $contentheight] + package require md5 + set hash [md5::md5 -hex $hashables] + set cache_key "$hash-$contentwidth-$contentheight-actualcontentwidth:$actual_contentwidth" + set TSUB \u1FFF; #needs to be different to that used in table construction + + #this occurs commonly in table building with colspans - review + if {$actual_contentwidth > $contentwidth || $actual_contentheight != $contentheight} { + set usecache 0 + #set buildcache 0 ;#comment out for debug/analysis so we can see + set cache_key [a+ Web-red]$cache_key[a] + } + if {$buildcache && $actual_contentwidth < $contentwidth} { + #colourise cache_key to warn + if {$actual_contentwidth == 0} { + #we can still substitue with right length + set cache_key [a+ Web-steelblue web-black]$cache_key[a] + } else { + #actual_contentwidth is shorter - rather than choose an alignment and pad - we will opt out of caching + set usecache 0 + set cache_key [a+ Web-orange web-black]$cache_key[a] + } + } + if {$usecache && [dict exists $frame_cache $cache_key]} { + set template [dict get $frame_cache $cache_key frame] + set used [dict get $frame_cache $cache_key used] + dict set frame_cache $cache_key used [expr {$used+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] + } + + # -- --- --- --- --- --- --- --- --- + set rst [a] #set column [string repeat " " $contentwidth] ;#default - may need to override for custom frame set underlayline [string repeat " " $contentwidth] 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 vlr_width 1 - set framedef [textblock::framedef $framedef $opt_joins] + set framedef [textblock::framedef $framedef -joins $opt_joins] dict with framedef {} ;#extract vll,hlt,tlc etc vars #puts "---> $opt_boxmap" @@ -4565,6 +4729,7 @@ namespace eval textblock { switch -- $frameset { custom { + #if no ansi, these widths are reasonable to maintain in grapheme_width_cached indefinitely set vll_width [punk::ansi::printing_length $vll] set hlb_width [punk::ansi::printing_length $hlb] set hlt_width [punk::ansi::printing_length $hlt] @@ -4592,6 +4757,7 @@ namespace eval textblock { #set column [string repeat " " $contentwidth] set underlayline [string repeat " " $contentwidth] set underlay [::join [lrepeat $linecount $underlayline] \n] + #cache? if {$hlt_width == 1} { set tbar [string repeat $hlt $tbarwidth] @@ -4756,6 +4922,7 @@ namespace eval textblock { set topborder 1 } set fs "" + set fscached "" #todo - output nothing except maybe newlines depending on if opt_height 0 and/or opt_width 0? if {$topborder} { if {$leftborder && $rightborder} { @@ -4770,27 +4937,42 @@ namespace eval textblock { } } } + append fscached $fs if {$has_contents || $opt_height > 2} { #if {$topborder && $fs ne "xx"} { # append fs \n #} if {$topborder} { append fs \n + append fscached \n } #set inner [overtype::$opt_align -ellipsis $opt_ellipsis $opt_ansibase$underlay$rstbase $opt_ansibase$contents$rstbase] set inner [overtype::$opt_align -ellipsis $opt_ellipsis $opt_ansibase$underlay$rstbase $contents] + + #set cache_inner [overtype::$opt_align -ellipsis $opt_ellipsis $opt_ansibase$cache_underlay$rstbase $contents] + #review + set cache_inner $opt_ansibase$cache_underlay$rstbase + if {$leftborder && $rightborder} { set bodyparts [list $lhs $opt_ansibase$inner$rstbase $rhs] + set cache_bodyparts [list $lhs $opt_ansibase$cache_inner$rstbase $rhs] } else { if {$leftborder} { set bodyparts [list $lhs $opt_ansibase$inner$rstbase] + set cache_bodyparts [list $lhs $opt_ansibase$cache_inner$rstbase] } elseif {$rightborder} { set bodyparts [list $opt_ansibase$inner$rstbase $rhs] + set cache_bodyparts [list $opt_ansibase$cache_inner$rstbase $rhs] } else { set bodyparts [list $opt_ansibase$inner$rstbase] + set cache_bodyparts [list $opt_ansibase$cache_inner$rstbase] } } set body [textblock::join -- {*}$bodyparts] + if {$buildcache} { + set cache_body [textblock::join -- {*}$cache_bodyparts] + append fscached $cache_body + } append fs $body } @@ -4802,21 +4984,28 @@ namespace eval textblock { if {$bottomborder} { if {($topborder & $fs ne "xx" ) || ($has_contents || $opt_height > 2)} { append fs \n + append fscached \n } if {$leftborder && $rightborder} { append fs $blc$bottombar$brc + append fscached $blc$bottombar$brc } else { if {$leftborder} { append fs $blc$bottombar + append fscached $blc$bottombar } elseif {$rightborder} { append fs $bottombar$brc + append fscached $bottombar$brc } else { append fs $bottombar + append fscached $bottombar } } } } - + if {$buildcache} { + dict set frame_cache $cache_key [list frame $fscached used 0] + } return $fs }