diff --git a/src/bootsupport/modules/natsort-0.1.1.6.tm b/src/bootsupport/modules/natsort-0.1.1.6.tm index 9d4f8a9..1d91b53 100644 --- a/src/bootsupport/modules/natsort-0.1.1.6.tm +++ b/src/bootsupport/modules/natsort-0.1.1.6.tm @@ -242,7 +242,7 @@ namespace eval natsort { proc hex2dec {largeHex} { #todo - use punk::lib::hex2dec - (scan supports ll so can do larger hex values directly) set res 0 - set largeHex [string map [list _ ""] $largeHex] + set largeHex [string map {_ {}} $largeHex] if {[string length $largeHex] <=7} { #scan can process up to FFFFFFF and does so quickly return [scan $largeHex %x] @@ -392,7 +392,7 @@ namespace eval natsort { proc get_char_count {str char} { #faster than lsearch on split for str of a few K - expr {[string length $str]-[string length [string map [list $char {}] $str]]} + expr {[tcl::string::length $str]-[tcl::string::length [tcl::string::map "$char {}" $str]]} } proc build_key {chunk splitchars topdict tagconfig debug} { @@ -856,10 +856,39 @@ namespace eval natsort { return [csv::join $line {*}$opts] } #---------------------------------------- + variable sort_flagspecs + set sort_flagspecs [dict create\ + -caller natsort::sort \ + -return supplied|defaults \ + -defaults [list -collate nocase \ + -winlike 0 \ + -splits "\uFFFF" \ + -topchars {. _} \ + -showsplits 1 \ + -sortmethod ascii \ + -collate "\uFFFF" \ + -inputformat raw \ + -inputformatapply {index data} \ + -inputformatoptions "" \ + -outputformat raw \ + -outputformatoptions "" \ + -cols "\uFFFF" \ + -debug 0 -db "" -stacktrace 0 -splits "\uFFFF" -showsplits 0] \ + -required {all} \ + -extras {none} \ + -commandprocessors {}\ + ] + proc sort {stringlist args} { #puts stdout "natsort::sort args: $args" variable debug + variable sort_flagspecs if {![llength $stringlist]} return + if {[llength $stringlist] == 1} { + if {"-inputformat" ni $args && "-outputformat" ni $args} { + return $stringlist + } + } #allow pass through of the check_flags flag -debugargs so it can be set by the caller set debugargs 0 @@ -874,49 +903,43 @@ namespace eval natsort { #-return flagged|defaults doesn't work Review. #flagfilter global processor/allocator not working 2023-08 - set args [check_flags \ - -caller natsort::sort \ - -return supplied|defaults \ - -debugargs $debugargs \ - -defaults [list -collate nocase \ - -winlike 0 \ - -splits "\uFFFF" \ - -topchars {. _} \ - -showsplits 1 \ - -sortmethod ascii \ - -collate "\uFFFF" \ - -inputformat raw \ - -inputformatapply {index data} \ - -inputformatoptions "" \ - -outputformat raw \ - -outputformatoptions "" \ - -cols "\uFFFF" \ - -debug 0 -db "" -stacktrace 0 -splits "\uFFFF" -showsplits 0] \ - -required {all} \ - -extras {none} \ - -commandprocessors {} \ - -values $args] - #csv unimplemented - - set winlike [dict get $args -winlike] - set topchars [dict get $args -topchars] - set cols [dict get $args -cols] - set debug [dict get $args -debug] - set stacktrace [dict get $args -stacktrace] - set showsplits [dict get $args -showsplits] - set splits [dict get $args -splits] - set sortmethod [dict get $args -sortmethod] - set opt_collate [dict get $args -collate] - set opt_inputformat [dict get $args -inputformat] - set opt_inputformatapply [dict get $args -inputformatapply] - set opt_inputformatoptions [dict get $args -inputformatoptions] - set opt_outputformat [dict get $args -outputformat] - set opt_outputformatoptions [dict get $args -outputformatoptions] - dict unset args -showsplits - dict unset args -splits + set opts [check_flags {*}$sort_flagspecs -debugargs $debugargs -values $args] + + #we can only shortcircuit input list of single element at this point if there aren't non-default -inputformat or -outputformat transformations + if {[llength $stringlist] == 1} { + set is_basic 1 + foreach fname [list -inputformat -outputformat] { + if {[dict get $sort_flagspecs -defaults $fname] ne [dict get $opts $fname]} { + set is_basic 0 + break + } + } + if {$is_basic} { + return $stringlist + } + } + + + set winlike [dict get $opts -winlike] + set topchars [dict get $opts -topchars] + set cols [dict get $opts -cols] + set debug [dict get $opts -debug] + set stacktrace [dict get $opts -stacktrace] + set showsplits [dict get $opts -showsplits] + set splits [dict get $opts -splits] + set sortmethod [dict get $opts -sortmethod] + set opt_collate [dict get $opts -collate] + set opt_inputformat [dict get $opts -inputformat] + set opt_inputformatapply [dict get $opts -inputformatapply] + set opt_inputformatoptions [dict get $opts -inputformatoptions] + set opt_outputformat [dict get $opts -outputformat] + set opt_outputformatoptions [dict get $opts -outputformatoptions] + if {$debug} { - puts stdout "natsort::sort processed_args: $args" + #dict unset opts -showsplits + #dict unset opts -splits + puts stdout "natsort::sort processed_args: $opts" if {$debug == 1} { puts stdout "natsort::sort - try also -debug 2, -debug 3" } @@ -1427,24 +1450,27 @@ namespace eval natsort { return 0 } } - - set is_namematch [called_directly_namematch] - set is_inodematch [called_directly_inodematch] - #### - #review - reliability of mechanisms to determine direct calls - # we don't want application being called when being used as a library, but we need it to run if called directly or from symlinks etc - #-- choose a policy and leave the others commented. - #set is_called_directly $is_namematch - #set is_called_directly $is_inodematch - set is_called_directly [expr {$is_namematch || $is_inodematch}] - #set is_called_directly [expr {$is_namematch && $is_inodematch}] - ### - - - #puts stdout "called_directly_name: [called_directly_namematch] called_directly_inode: [called_directly_inodematch]" + if {![interp issafe]} { + set is_namematch [called_directly_namematch] + set is_inodematch [called_directly_inodematch] + #### + #review - reliability of mechanisms to determine direct calls + # we don't want application being called when being used as a library, but we need it to run if called directly or from symlinks etc + #-- choose a policy and leave the others commented. + #set is_called_directly $is_namematch + #set is_called_directly $is_inodematch + set is_called_directly [expr {$is_namematch || $is_inodematch}] + #set is_called_directly [expr {$is_namematch && $is_inodematch}] + ### + + + #puts stdout "called_directly_name: [called_directly_namematch] called_directly_inode: [called_directly_inodematch]" + } else { + #safe interp + set is_called_directly 0 + } - # proc test_pass_fail_message {pass {additional ""}} { @@ -1709,9 +1735,9 @@ namespace eval natsort { set debug [dict get $args -debug] - set collate [dict get $args -collate] - set db [dict get $args -db] - set winlike [dict get $args -winlike] + set collate [dict get $args -collate] + set db [dict get $args -db] + set winlike [dict get $args -winlike] set topchars [dict get $args -topchars] diff --git a/src/vendormodules/overtype-1.6.2.tm b/src/bootsupport/modules/overtype-1.6.4.tm similarity index 83% rename from src/vendormodules/overtype-1.6.2.tm rename to src/bootsupport/modules/overtype-1.6.4.tm index c254a53..4287632 100644 --- a/src/vendormodules/overtype-1.6.2.tm +++ b/src/bootsupport/modules/overtype-1.6.4.tm @@ -7,7 +7,7 @@ # (C) Julian Noble 2003-2023 # # @@ Meta Begin -# Application overtype 1.6.2 +# Application overtype 1.6.4 # Meta platform tcl # Meta license BSD # @@ Meta End @@ -17,7 +17,7 @@ # doctools header # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ #*** !doctools -#[manpage_begin overtype_module_overtype 0 1.6.2] +#[manpage_begin overtype_module_overtype 0 1.6.4] #[copyright "2024"] #[titledesc {overtype text layout - ansi aware}] [comment {-- Name section and table of contents description --}] #[moddesc {overtype text layout}] [comment {-- Description at end of page heading --}] @@ -65,7 +65,15 @@ package require punk::assertion #*** !doctools #[list_end] - +#PERFORMANCE notes +#overtype is very performance sensitive - used in ansi output all over the place ie needs to be optimised +#NOTE use of tcl::dict::for tcl::string::range etc instead of ensemble versions. This is for the many tcl 8.6/8.7 interps which don't compile ensemble commands when in safe interps +#similar for tcl::namespace::eval - but this is at least on some versions of Tcl - faster even in a normal interp. Review to see if that holds for Tcl 9. +#for string map: when there are exactly 2 elements - it is faster to use a literal which has a special case optimisation in the c code +#ie use tcl::string::map {\n ""} ... instead of tcl::string::map [list \n ""] ... +#note that we can use unicode (e.g \uFF31) and other escapes such as \t within these curly braces - we don't have to use double quotes +#generally using 'list' is preferred for the map as less error prone. +#can also use: tcl::string::map "token $var" .. but be careful regarding quoting and whitespace in var. This should be used sparingly if at all. # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ @@ -78,10 +86,10 @@ package require punk::assertion # #todo - ellipsis truncation indicator for center,right -#v1.4 2023-07 - naive ansi color handling - todo - fix string range +#v1.4 2023-07 - naive ansi color handling - todo - fix tcl::string::range # - need to extract and replace ansi codes? -namespace eval overtype { +tcl::namespace::eval overtype { namespace import ::punk::assertion::assert punk::assertion::active true @@ -90,7 +98,7 @@ namespace eval overtype { namespace export * variable default_ellipsis_horizontal "..." ;#fallback variable default_ellipsis_vertical "..." - namespace eval priv { + tcl::namespace::eval priv { proc _init {} { upvar ::overtype::default_ellipsis_horizontal e_h upvar ::overtype::default_ellipsis_vertical e_v @@ -112,18 +120,18 @@ proc overtype::about {} { return "Simple text formatting. Author JMN. BSD-License" } -namespace eval overtype { - variable grapheme_widths [dict create] +tcl::namespace::eval overtype { + variable grapheme_widths [tcl::dict::create] variable escape_terminals #single "final byte" in the range 0x40–0x7E (ASCII @A–Z[\]^_`a–z{|}~). - dict set escape_terminals CSI [list @ \\ ^ _ ` | ~ a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z "\{" "\}"] - #dict set escape_terminals CSI [list J K m n A B C D E F G s u] ;#basic - dict set escape_terminals OSC [list \007 \033\\] ;#note mix of 1 and 2-byte terminals + tcl::dict::set escape_terminals CSI [list @ \\ ^ _ ` | ~ a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z "\{" "\}"] + #tcl::dict::set escape_terminals CSI [list J K m n A B C D E F G s u] ;#basic + tcl::dict::set escape_terminals OSC [list \007 \033\\] ;#note mix of 1 and 2-byte terminals #self-contained 2 byte ansi escape sequences - review more? variable ansi_2byte_codes_dict - set ansi_2byte_codes_dict [dict create\ + set ansi_2byte_codes_dict [tcl::dict::create\ "reset_terminal" "\u001bc"\ "save_cursor_posn" "\u001b7"\ "restore_cursor_posn" "\u001b8"\ @@ -166,7 +174,7 @@ namespace eval overtype { # #2nd byte - done. # set in_escapesequence 0 # } elseif {$in_escapesequence != 0} { -# set escseq [dict get $escape_terminals $in_escapesequence] +# set escseq [tcl::dict::get $escape_terminals $in_escapesequence] # if {$u in $escseq} { # set in_escapesequence 0 # } elseif {$uv in $escseq} { @@ -206,7 +214,7 @@ proc overtype::string_columns {text} { #These have similar algorithms/requirements - and should be refactored to be argument-wrappers over a function called something like overtype::renderblock #overtype::renderblock could render the input to a defined (possibly overflowing in x or y) rectangle overlapping the underlay. #(i.e not even necessariy having it's top left within the underlay) -namespace eval overtype::priv { +tcl::namespace::eval overtype::priv { } #could return larger than colwidth @@ -232,7 +240,7 @@ proc _get_row_append_column {row} { } } -namespace eval overtype { +tcl::namespace::eval overtype { #*** !doctools #[subsection {Namespace overtype}] #[para] Core API functions for overtype @@ -240,16 +248,16 @@ namespace eval overtype { - #string range should generally be avoided for both undertext and overtext which contain ansi escapes and other cursor affecting chars such as \b and \r + #tcl::string::range should generally be avoided for both undertext and overtext which contain ansi escapes and other cursor affecting chars such as \b and \r #render onto an already-rendered (ansi already processed) 'underlay' string, a possibly ansi-laden 'overlay' string. #The underlay and overlay can be multiline blocks of text of varying line lengths. #The overlay may just be an ansi-colourised block - or may contain ansi cursor movements and cursor save/restore calls - in which case the apparent length and width of the overlay can't be determined as if it was a block of text. #This is a single-shot rendering of strings - ie there is no way to chain another call containing a cursor-restore to previously rendered output and have it know about any cursor-saves in the first call. # a cursor start position other than top-left is a possible addition to consider. #see editbuf in punk::repl for a more stateful ansi-processor. Both systems use loops over overtype::renderline - proc left {args} { + proc renderspace {args} { #*** !doctools - #[call [fun overtype::left] [arg args] ] + #[call [fun overtype::renderspace] [arg args] ] #[para] usage: ?-transparent [lb]0|1[rb]? ?-overflow [lb]1|0[rb]? ?-ellipsis [lb]1|0[rb]? ?-ellipsistext ...? undertext overtext # @c overtype starting at left (overstrike) @@ -260,10 +268,11 @@ namespace eval overtype { error {usage: ?-transparent [0|1]? ?-overflow [1|0]? ?-ellipsis [1|0]? ?-ellipsistext ...? undertext overtext} } lassign [lrange $args end-1 end] underblock overblock - set defaults [dict create\ + set opts [tcl::dict::create\ -bias ignored\ -width \uFFEF\ -height \uFFEF\ + -startcolumn 1\ -wrap 0\ -ellipsis 0\ -ellipsistext $default_ellipsis_horizontal\ @@ -278,31 +287,33 @@ namespace eval overtype { ] #-ellipsis args not used if -wrap is true set argsflags [lrange $args 0 end-2] - dict for {k v} $argsflags { + foreach {k v} $argsflags { switch -- $k { - -looplimit - -width - -height - -bias - -wrap - -ellipsis - -ellipsistext - -ellipsiswhitespace - -overflow - -appendlines - -transparent - -exposed1 - -exposed2 - -experimental {} + -looplimit - -width - -height - -startcolumn - -bias - -wrap - -ellipsis - -ellipsistext - -ellipsiswhitespace - -overflow - -appendlines - -transparent - -exposed1 - -exposed2 - -experimental { + tcl::dict::set opts $k $v + } default { - set known_opts [dict keys $defaults] - error "overtype::left unknown option '$k'. Known options: $known_opts" + error "overtype::renderspace unknown option '$k'. Known options: [tcl::dict::keys $opts]" } } } - set opts [dict merge $defaults $argsflags] + #set opts [tcl::dict::merge $defaults $argsflags] # -- --- --- --- --- --- - set opt_overflow [dict get $opts -overflow] + set opt_overflow [tcl::dict::get $opts -overflow] ##### # review -wrap should map onto DECAWM terminal mode - the wrap 2 idea may not fit in with this?. - set opt_wrap [dict get $opts -wrap] ;#wrap 1 is hard wrap cutting word at exact column, or 1 column early for 2w-glyph, wrap 2 is for language-based word-wrap algorithm (todo) + set opt_wrap [tcl::dict::get $opts -wrap] ;#wrap 1 is hard wrap cutting word at exact column, or 1 column early for 2w-glyph, wrap 2 is for language-based word-wrap algorithm (todo) ##### #for repl - standard output line indicator is a dash - todo, add a different indicator for a continued line. - set opt_width [dict get $opts -width] - set opt_height [dict get $opts -height] - set opt_appendlines [dict get $opts -appendlines] - set opt_transparent [dict get $opts -transparent] - set opt_ellipsistext [dict get $opts -ellipsistext] - set opt_ellipsiswhitespace [dict get $opts -ellipsiswhitespace] - set opt_exposed1 [dict get $opts -exposed1] ;#widechar_exposed_left - todo - set opt_exposed2 [dict get $opts -exposed2] ;#widechar_exposed_right - todo + set opt_width [tcl::dict::get $opts -width] + set opt_height [tcl::dict::get $opts -height] + set opt_startcolumn [tcl::dict::get $opts -startcolumn] + set opt_appendlines [tcl::dict::get $opts -appendlines] + set opt_transparent [tcl::dict::get $opts -transparent] + set opt_ellipsistext [tcl::dict::get $opts -ellipsistext] + set opt_ellipsiswhitespace [tcl::dict::get $opts -ellipsiswhitespace] + set opt_exposed1 [tcl::dict::get $opts -exposed1] ;#widechar_exposed_left - todo + set opt_exposed2 [tcl::dict::get $opts -exposed2] ;#widechar_exposed_right - todo # -- --- --- --- --- --- # ---------------------------- @@ -312,7 +323,7 @@ namespace eval overtype { set test_mode 1 set info_mode 0 set edit_mode 0 - set opt_experimental [dict get $opts -experimental] + set opt_experimental [tcl::dict::get $opts -experimental] foreach o $opt_experimental { switch -- $o { test_mode { @@ -334,6 +345,7 @@ namespace eval overtype { } } } + set test_mode 1 ;#try to eliminate # ---------------------------- #modes @@ -342,9 +354,8 @@ namespace eval overtype { set reverse_mode 0 - set norm [list \r\n \n] - set underblock [string map $norm $underblock] - set overblock [string map $norm $overblock] + set underblock [tcl::string::map {\r\n \n} $underblock] + set overblock [tcl::string::map {\r\n \n} $overblock] #set underlines [split $underblock \n] @@ -353,19 +364,35 @@ namespace eval overtype { #colwidth & colheight were originally named with reference to rendering into a 'column' of output e.g a table column - before cursor row/col was implemented. #The naming is now confusing. It should be something like renderwidth renderheight ?? review - if {$opt_width eq "\uFFEF"} { + if {$opt_width eq "\uFFEF" || $opt_height eq "\uFFEF"} { lassign [blocksize $underblock] _w colwidth _h colheight + if {$opt_width ne "\uFFEF"} { + set colwidth $opt_width + } + if {$opt_height ne "\uFFEF"} { + set colheight $opt_height + } } else { set colwidth $opt_width set colheight $opt_height } + + # -- --- --- --- + #REVIEW - do we need ansi resets in the underblock? if {$underblock eq ""} { - set blank "\x1b\[0m\x1b\[0m" - #set underlines [list "\x1b\[0m\x1b\[0m"] - set underlines [lrepeat $colheight $blank] + set underlines [lrepeat $colheight ""] } else { - set underlines [lines_as_list -ansiresets 1 $underblock] + set underlines [split $underblock \n] } + #if {$underblock eq ""} { + # set blank "\x1b\[0m\x1b\[0m" + # #set underlines [list "\x1b\[0m\x1b\[0m"] + # set underlines [lrepeat $colheight $blank] + #} else { + # #lines_as_list -ansiresets 1 will do nothing if -ansiresets 1 isn't specified - REVIEW + # set underlines [lines_as_list -ansiresets 1 $underblock] + #} + # -- --- --- --- #todo - reconsider the 'line' as the natural chunking mechanism for the overlay. #In practice an overlay ANSI stream can be a single line with ansi moves/restores etc - or even have no moves or newlines, just relying on wrapping at the output colwidth @@ -376,11 +403,11 @@ namespace eval overtype { #a hack until we work out how to avoid infinite loops... # - set looplimit [dict get $opts -looplimit] + set looplimit [tcl::dict::get $opts -looplimit] if {$looplimit eq "\uFFEF"} { #looping for each char is worst case (all newlines?) - anything over that is an indication of something broken? #do we need any margin above the length? (telnet mapscii.me test) - set looplimit [expr {[string length $overblock] + 10}] + set looplimit [expr {[tcl::string::length $overblock] + 10}] } if {!$test_mode} { @@ -407,7 +434,7 @@ namespace eval overtype { } set sequence_split [punk::ansi::ta::split_codes_single $ln] ;#use split_codes Not split_codes_single? set lastpt [lindex $inputchunks end] - lset inputchunks end [string cat $lastpt [lindex $sequence_split 0]] + lset inputchunks end [tcl::string::cat $lastpt [lindex $sequence_split 0]] lappend inputchunks {*}[lrange $sequence_split 1 end] incr i } @@ -421,7 +448,7 @@ namespace eval overtype { lappend lflines $ln } if {[llength $lflines]} { - lset lflines end [string range [lindex $lflines end] 0 end-1] + lset lflines end [tcl::string::range [lindex $lflines end] 0 end-1] } set inputchunks $lflines[unset lflines] @@ -434,11 +461,11 @@ namespace eval overtype { #lassign [blocksize $overblock] _w overblock_width _h overblock_height - set replay_codes_underlay [dict create 1 ""] + set replay_codes_underlay [tcl::dict::create 1 ""] #lappend replay_codes_overlay "" set replay_codes_overlay "" set unapplied "" - set cursor_saved_position [dict create] + set cursor_saved_position [tcl::dict::create] set cursor_saved_attributes "" @@ -450,10 +477,10 @@ namespace eval overtype { if {$data_mode} { set col [_get_row_append_column $row] } else { - set col 1 + set col $opt_startcolumn } - set instruction_stats [dict create] + set instruction_stats [tcl::dict::create] set loop 0 #while {$overidx < [llength $inputchunks]} { } @@ -461,7 +488,7 @@ namespace eval overtype { while {[llength $inputchunks]} { #set overtext [lindex $inputchunks $overidx]; lset inputchunks $overidx "" set overtext [lpop inputchunks 0] - if {![string length $overtext]} { + if {![tcl::string::length $overtext]} { incr loop continue } @@ -472,10 +499,10 @@ namespace eval overtype { #renderline pads each underaly line to width with spaces and should track where end of data is - #set overtext [string cat [lindex $replay_codes_overlay $overidx] $overtext] - set overtext [string cat $replay_codes_overlay $overtext] - if {[dict exists $replay_codes_underlay $row]} { - set undertext [string cat [dict get $replay_codes_underlay $row] $undertext] + #set overtext [tcl::string::cat [lindex $replay_codes_overlay $overidx] $overtext] + set overtext [tcl::string::cat $replay_codes_overlay $overtext] + if {[tcl::dict::exists $replay_codes_underlay $row]} { + set undertext [tcl::string::cat [tcl::dict::get $replay_codes_underlay $row] $undertext] } #review insert_mode. As an 'overtype' function whose main function is not interactive keystrokes - insert is secondary - #but even if we didn't want it as an option to the function call - to process ansi adequately we need to support IRM (insertion-replacement mode) ESC [ 4 h|l @@ -495,37 +522,37 @@ namespace eval overtype { $undertext\ $overtext\ ] - set instruction [dict get $rinfo instruction] - set insert_mode [dict get $rinfo insert_mode] - set autowrap_mode [dict get $rinfo autowrap_mode] ;# - #set reverse_mode [dict get $rinfo reverse_mode];#how to support in rendered linelist? we need to examine all pt/code blocks and flip each SGR stack? - set rendered [dict get $rinfo result] - set overflow_right [dict get $rinfo overflow_right] - set overflow_right_column [dict get $rinfo overflow_right_column] - set unapplied [dict get $rinfo unapplied] - set unapplied_list [dict get $rinfo unapplied_list] - set post_render_col [dict get $rinfo cursor_column] - set post_render_row [dict get $rinfo cursor_row] - set c_saved_pos [dict get $rinfo cursor_saved_position] - set c_saved_attributes [dict get $rinfo cursor_saved_attributes] - set visualwidth [dict get $rinfo visualwidth] - set insert_lines_above [dict get $rinfo insert_lines_above] - set insert_lines_below [dict get $rinfo insert_lines_below] - dict set replay_codes_underlay [expr {$renderedrow+1}] [dict get $rinfo replay_codes_underlay] - #lset replay_codes_overlay [expr $overidx+1] [dict get $rinfo replay_codes_overlay] - set replay_codes_overlay [dict get $rinfo replay_codes_overlay] + set instruction [tcl::dict::get $rinfo instruction] + set insert_mode [tcl::dict::get $rinfo insert_mode] + set autowrap_mode [tcl::dict::get $rinfo autowrap_mode] ;# + #set reverse_mode [tcl::dict::get $rinfo reverse_mode];#how to support in rendered linelist? we need to examine all pt/code blocks and flip each SGR stack? + set rendered [tcl::dict::get $rinfo result] + set overflow_right [tcl::dict::get $rinfo overflow_right] + set overflow_right_column [tcl::dict::get $rinfo overflow_right_column] + set unapplied [tcl::dict::get $rinfo unapplied] + set unapplied_list [tcl::dict::get $rinfo unapplied_list] + set post_render_col [tcl::dict::get $rinfo cursor_column] + set post_render_row [tcl::dict::get $rinfo cursor_row] + set c_saved_pos [tcl::dict::get $rinfo cursor_saved_position] + set c_saved_attributes [tcl::dict::get $rinfo cursor_saved_attributes] + set visualwidth [tcl::dict::get $rinfo visualwidth] + set insert_lines_above [tcl::dict::get $rinfo insert_lines_above] + set insert_lines_below [tcl::dict::get $rinfo insert_lines_below] + tcl::dict::set replay_codes_underlay [expr {$renderedrow+1}] [tcl::dict::get $rinfo replay_codes_underlay] + #lset replay_codes_overlay [expr $overidx+1] [tcl::dict::get $rinfo replay_codes_overlay] + set replay_codes_overlay [tcl::dict::get $rinfo replay_codes_overlay] #-- todo - detect looping properly if {$row > 1 && $overtext ne "" && $unapplied eq $overtext && $post_render_row == $row && $instruction eq ""} { - puts stderr "overtype::left loop?" + puts stderr "overtype::renderspace loop?" puts [ansistring VIEW $rinfo] break } #-- - if {[dict size $c_saved_pos] >= 1} { + if {[tcl::dict::size $c_saved_pos] >= 1} { set cursor_saved_position $c_saved_pos set cursor_saved_attributes $c_saved_attributes } @@ -540,7 +567,7 @@ namespace eval overtype { #todo - handle potential insertion mode as above for cursor restore? #keeping separate branches for debugging - review and merge as appropriate when stable - dict incr instruction_stats $instruction + tcl::dict::incr instruction_stats $instruction switch -- $instruction { {} { if {$test_mode == 0} { @@ -640,21 +667,21 @@ namespace eval overtype { #testfile belinda.ans uses this #puts stdout "[a+ blue bold]CURSOR_RESTORE[a]" - if {[dict exists $cursor_saved_position row]} { - set row [dict get $cursor_saved_position row] - set col [dict get $cursor_saved_position column] + if {[tcl::dict::exists $cursor_saved_position row]} { + set row [tcl::dict::get $cursor_saved_position row] + set col [tcl::dict::get $cursor_saved_position column] #puts stdout "restoring: row $row col $col [ansistring VIEW $cursor_saved_attributes] [a] unapplied [ansistring VIEWCODES $unapplied]" #set nextprefix $cursor_saved_attributes #lset replay_codes_overlay [expr $overidx+1] $cursor_saved_attributes - set replay_codes_overlay [dict get $rinfo replay_codes_overlay]$cursor_saved_attributes + set replay_codes_overlay [tcl::dict::get $rinfo replay_codes_overlay]$cursor_saved_attributes #set replay_codes_overlay $cursor_saved_attributes - set cursor_saved_position [dict create] + set cursor_saved_position [tcl::dict::create] set cursor_saved_attributes "" } else { #TODO #?restore without save? #should move to home position and reset ansi SGR? - #puts stderr "overtype::left cursor_restore without save data available" + #puts stderr "overtype::renderspace cursor_restore without save data available" } #If we were inserting prior to hitting the cursor_restore - there could be overflow_right data - generally the overtype functions aren't for inserting - but ansi can enable it #if we were already in overflow when cursor_restore was hit - it shouldn't have been processed as an action - just stored. @@ -667,10 +694,10 @@ namespace eval overtype { puts stdout ">>>[a+ red bold]overflow_right during restore_cursor[a]" - set sub_info [overtype::renderline -info 1 -width $colwidth -insert_mode $insert_mode -autowrap_mode $autowrap_mode -overflow [dict get $opts -overflow] "" $overflow_right] - set foldline [dict get $sub_info result] - set insert_mode [dict get $sub_info insert_mode] ;#probably not needed.. - set autowrap_mode [dict get $sub_info autowrap_mode] ;#nor this.. + set sub_info [overtype::renderline -info 1 -width $colwidth -insert_mode $insert_mode -autowrap_mode $autowrap_mode -overflow [tcl::dict::get $opts -overflow] "" $overflow_right] + set foldline [tcl::dict::get $sub_info result] + set insert_mode [tcl::dict::get $sub_info insert_mode] ;#probably not needed.. + set autowrap_mode [tcl::dict::get $sub_info autowrap_mode] ;#nor this.. linsert outputlines $renderedrow $foldline #review - row & col set by restore - but not if there was no save.. } @@ -709,7 +736,7 @@ namespace eval overtype { if {$row > [llength $outputlines]} { lappend outputlines "" } - set col 1 + set col $opt_startcolumn # ---------------------- } lf_mid { @@ -737,7 +764,7 @@ namespace eval overtype { set row $renderedrow - set col 1 + set col $opt_startcolumn incr row #only add newline if we're at the bottom if {$row > [llength $outputlines]} { @@ -751,7 +778,7 @@ namespace eval overtype { set unapplied "" set row $post_render_row #set col $post_render_col - set col 1 + set col $opt_startcolumn if {$row > [llength $outputlines]} { lappend outputlines {*}[lrepeat 1 ""] } @@ -759,7 +786,7 @@ namespace eval overtype { append rendered $overflow_right set overflow_right "" set row $post_render_row - set col 1 + set col $opt_startcolumn if {$row > [llength $outputlines]} { lappend outputlines {*}[lrepeat 1 ""] } @@ -787,7 +814,7 @@ namespace eval overtype { if {$row > [llength $outputlines]} { lappend outputlines {*}[lrepeat 1 ""] } - set col 1 + set col $opt_startcolumn } newlines_above { @@ -818,7 +845,7 @@ namespace eval overtype { set row $renderedrow set outputlines [linsert $outputlines [expr {$renderedrow }] {*}[lrepeat $insert_lines_below ""]] ;#note - linsert can add to end too incr row $insert_lines_below - set col 1 + set col $opt_startcolumn } else { #set lhs [overtype::left -width 40 -wrap 1 "" [ansistring VIEWSTYLE -lf 1 -vt 1 $rendered]] #set lhs [textblock::frame -title rendered -subtitle "row-$renderedrow" $lhs] @@ -841,7 +868,7 @@ namespace eval overtype { lappend outputlines {*}[lrepeat $insert_lines_below ""] } incr row $insert_lines_below - set col 1 + set col $opt_startcolumn @@ -884,7 +911,7 @@ namespace eval overtype { lappend outputlines "" } } - set c 1 + set c $opt_startcolumn } else { incr c } @@ -935,12 +962,12 @@ namespace eval overtype { set row $post_render_row ;#renderline will not advance row when reporting overflow char if {$autowrap_mode} { incr row - set col 1 ;#whether wrap or not - next data is at column 1 ?? + set col $opt_startcolumn ;#whether wrap or not - next data is at column 1 ?? } else { #this works for test_mode (which should become the default) - but could give a bad result otherwise - review - add tests fix. set col $post_render_col #set unapplied "" ;#this seems wrong? - #set unapplied [string range $unapplied 1 end] + #set unapplied [tcl::string::range $unapplied 1 end] #The overflow can only be triggered by a grapheme (todo cluster?) - but our unapplied could contain SGR codes prior to the grapheme that triggered overflow - so we need to skip beyond any SGRs #There may be more than one, because although the stack leading up to overflow may have been merged - codes between the last column and the overflowing grapheme will remain separate #We don't expect any movement or other ANSI codes - as if they came before the grapheme, they would have triggered a different instruction to 'overflow' @@ -981,7 +1008,7 @@ namespace eval overtype { } set unapplied [join [lreplace $unapplied_list $triggering_grapheme_index $triggering_grapheme_index $opt_exposed1] ""] } else { - set col 1 + set col $opt_startcolumn incr row } } else { @@ -1009,7 +1036,7 @@ namespace eval overtype { set col $post_render_col } default { - puts stderr "overtype::left unhandled renderline instruction '$instruction'" + puts stderr "overtype::renderspace unhandled renderline instruction '$instruction'" } } @@ -1017,7 +1044,7 @@ namespace eval overtype { if {!$opt_overflow && !$autowrap_mode} { #not allowed to overflow column or wrap therefore we get overflow data to truncate - if {[dict get $opts -ellipsis]} { + if {[tcl::dict::get $opts -ellipsis]} { set show_ellipsis 1 if {!$opt_ellipsiswhitespace} { #we don't want ellipsis if only whitespace was lost @@ -1028,11 +1055,11 @@ namespace eval overtype { if {$unapplied ne ""} { append lostdata $unapplied } - if {[string trim $lostdata] eq ""} { + if {[tcl::string::trim $lostdata] eq ""} { set show_ellipsis 0 } - #set lostdata [string range $overtext end-[expr {$overflowlength-1}] end] - if {[string trim [punk::ansi::stripansi $lostdata]] eq ""} { + #set lostdata [tcl::string::range $overtext end-[expr {$overflowlength-1}] end] + if {[tcl::string::trim [punk::ansi::stripansi $lostdata]] eq ""} { set show_ellipsis 0 } } @@ -1085,7 +1112,7 @@ namespace eval overtype { incr overidx incr loop if {$loop >= $looplimit} { - puts stderr "overtype::left looplimit reached ($looplimit)" + puts stderr "overtype::renderspace looplimit reached ($looplimit)" lappend outputlines "[a+ red bold] - looplimit $looplimit reached[a]" set Y [a+ yellow bold] set RST [a] @@ -1096,9 +1123,9 @@ namespace eval overtype { append debugmsg "test_mode:$test_mode\n" append debugmsg "data_mode:$data_mode\n" append debugmsg "opt_appendlines:$opt_appendlines\n" - append debugmsg "prev_row :[dict get $LASTCALL -cursor_row]\n" - append debugmsg "prev_col :[dict get $LASTCALL -cursor_column]\n" - dict for {k v} $rinfo { + append debugmsg "prev_row :[tcl::dict::get $LASTCALL -cursor_row]\n" + append debugmsg "prev_col :[tcl::dict::get $LASTCALL -cursor_column]\n" + tcl::dict::for {k v} $rinfo { append debugmsg "${Y}$k [ansistring VIEW -lf 1 -vt 1 $v]$RST" \n } append debugmsg "${Y}[string repeat - [string length $sep_header]]$RST" \n @@ -1131,7 +1158,7 @@ namespace eval overtype { foreach {underblock overblock} [lrange $args end-1 end] break #todo - vertical vs horizontal overflow for blocks - set defaults [dict create\ + set opts [tcl::dict::create\ -bias left\ -ellipsis 0\ -ellipsistext $default_ellipsis_horizontal\ @@ -1142,29 +1169,30 @@ namespace eval overtype { -exposed2 \uFFFD\ ] set argsflags [lrange $args 0 end-2] - dict for {k v} $argsflags { + foreach {k v} $argsflags { switch -- $k { - -bias - -ellipsis - -ellipsistext - -ellipsiswhitespace - -overflow - -transparent - -exposed1 - -exposed2 {} + -bias - -ellipsis - -ellipsistext - -ellipsiswhitespace - -overflow - -transparent - -exposed1 - -exposed2 { + tcl::dict::set opts $k $v + } default { - set known_opts [dict keys $defaults] + set known_opts [tcl::dict::keys $opts] error "overtype::centre unknown option '$k'. Known options: $known_opts" } } } - set opts [dict merge $defaults $argsflags] + #set opts [tcl::dict::merge $defaults $argsflags] # -- --- --- --- --- --- - set opt_transparent [dict get $opts -transparent] - set opt_ellipsis [dict get $opts -ellipsis] - set opt_ellipsistext [dict get $opts -ellipsistext] - set opt_ellipsiswhitespace [dict get $opts -ellipsiswhitespace] - set opt_exposed1 [dict get $opts -exposed1] - set opt_exposed2 [dict get $opts -exposed2] + set opt_transparent [tcl::dict::get $opts -transparent] + set opt_ellipsis [tcl::dict::get $opts -ellipsis] + set opt_ellipsistext [tcl::dict::get $opts -ellipsistext] + set opt_ellipsiswhitespace [tcl::dict::get $opts -ellipsiswhitespace] + set opt_exposed1 [tcl::dict::get $opts -exposed1] + set opt_exposed2 [tcl::dict::get $opts -exposed2] # -- --- --- --- --- --- - set norm [list \r\n \n] - set underblock [string map $norm $underblock] - set overblock [string map $norm $overblock] + set underblock [tcl::string::map {\r\n \n} $underblock] + set overblock [tcl::string::map {\r\n \n} $overblock] set underlines [split $underblock \n] #set colwidth [tcl::mathfunc::max {*}[lmap v $underlines {punk::ansi::printing_length $v}]] @@ -1180,7 +1208,7 @@ namespace eval overtype { set left_exposed [expr {$under_exposed_max / 2}] } else { set beforehalf [expr {$under_exposed_max / 2}] ;#1 less than half due to integer division - if {[string tolower [dict get $opts -bias]] eq "left"} { + if {[tcl::string::tolower [tcl::dict::get $opts -bias]] eq "left"} { set left_exposed $beforehalf } else { #bias to the right @@ -1206,30 +1234,30 @@ namespace eval overtype { set udiff [expr {$colwidth - $ulen}] set undertext "$undertext[string repeat { } $udiff]" } - set undertext [string cat $replay_codes_underlay $undertext] - set overtext [string cat $replay_codes_overlay $overtext] + set undertext [tcl::string::cat $replay_codes_underlay $undertext] + set overtext [tcl::string::cat $replay_codes_overlay $overtext] set overflowlength [expr {$overtext_datalen - $colwidth}] #review - right-to-left langs should elide on left! - extra option required if {$overflowlength > 0} { #overlay line wider or equal - set rinfo [renderline -info 1 -insert_mode 0 -transparent $opt_transparent -overflow [dict get $opts -overflow] -exposed1 $opt_exposed1 -exposed2 $opt_exposed2 $undertext $overtext] - set rendered [dict get $rinfo result] - set overflow_right [dict get $rinfo overflow_right] - set unapplied [dict get $rinfo unapplied] + set rinfo [renderline -info 1 -insert_mode 0 -transparent $opt_transparent -overflow [tcl::dict::get $opts -overflow] -exposed1 $opt_exposed1 -exposed2 $opt_exposed2 $undertext $overtext] + set rendered [tcl::dict::get $rinfo result] + set overflow_right [tcl::dict::get $rinfo overflow_right] + set unapplied [tcl::dict::get $rinfo unapplied] #todo - get replay_codes from overflow_right instead of wherever it was truncated? #overlay line data is wider - trim if overflow not specified in opts - and overtype an ellipsis at right if it was specified - if {![dict get $opts -overflow]} { - #lappend outputlines [string range $overtext 0 [expr {$colwidth - 1}]] - #set overtext [string range $overtext 0 $colwidth-1 ] + if {![tcl::dict::get $opts -overflow]} { + #lappend outputlines [tcl::string::range $overtext 0 [expr {$colwidth - 1}]] + #set overtext [tcl::string::range $overtext 0 $colwidth-1 ] if {$opt_ellipsis} { set show_ellipsis 1 if {!$opt_ellipsiswhitespace} { #we don't want ellipsis if only whitespace was lost - #don't use string range on ANSI data - #set lostdata [string range $overtext end-[expr {$overflowlength-1}] end] + #don't use tcl::string::range on ANSI data + #set lostdata [tcl::string::range $overtext end-[expr {$overflowlength-1}] end] set lostdata "" if {$overflow_right ne ""} { append lostdata $overflow_right @@ -1237,7 +1265,7 @@ namespace eval overtype { if {$unapplied ne ""} { append lostdata $unapplied } - if {[string trim $lostdata] eq ""} { + if {[tcl::string::trim $lostdata] eq ""} { set show_ellipsis 0 } } @@ -1252,10 +1280,10 @@ namespace eval overtype { #background block is wider than or equal to data for this line #lappend outputlines [renderline -insert_mode 0 -startcolumn [expr {$left_exposed + 1}] -transparent $opt_transparent -exposed1 $opt_exposed1 -exposed2 $opt_exposed2 $undertext $overtext] set rinfo [renderline -info 1 -insert_mode 0 -startcolumn [expr {$left_exposed + 1}] -transparent $opt_transparent -exposed1 $opt_exposed1 -exposed2 $opt_exposed2 $undertext $overtext] - lappend outputlines [dict get $rinfo result] + lappend outputlines [tcl::dict::get $rinfo result] } - set replay_codes_underlay [dict get $rinfo replay_codes_underlay] - set replay_codes_overlay [dict get $rinfo replay_codes_overlay] + set replay_codes_underlay [tcl::dict::get $rinfo replay_codes_underlay] + set replay_codes_overlay [tcl::dict::get $rinfo replay_codes_overlay] } return [join $outputlines \n] } @@ -1263,7 +1291,6 @@ namespace eval overtype { #overtype::right is for a rendered ragged underblock and a rendered ragged overblock #ie we can determine the block width for bost by examining the lines and picking the longest. # - #todo - rename overtype::left - which has morphed in capability and usage so that it bears less resemblance to overtype::right and overtype::centre proc right {args} { #NOT the same as align-right - which should be done to the overblock first if required variable default_ellipsis_horizontal @@ -1274,7 +1301,7 @@ namespace eval overtype { } foreach {underblock overblock} [lrange $args end-1 end] break - set defaults [dict create\ + set opts [tcl::dict::create\ -bias ignored\ -ellipsis 0\ -ellipsistext $default_ellipsis_horizontal\ @@ -1286,30 +1313,31 @@ namespace eval overtype { -align "left"\ ] set argsflags [lrange $args 0 end-2] - dict for {k v} $argsflags { + tcl::dict::for {k v} $argsflags { switch -- $k { - -bias - -ellipsis - -ellipsistext - -ellipsiswhitespace - -overflow - -transparent - -exposed1 - -exposed2 - -align {} + -bias - -ellipsis - -ellipsistext - -ellipsiswhitespace - -overflow - -transparent - -exposed1 - -exposed2 - -align { + tcl::dict::set opts $k $v + } default { - set known_opts [dict keys $defaults] + set known_opts [tcl::dict::keys $opts] error "overtype::centre unknown option '$k'. Known options: $known_opts" } } } - set opts [dict merge $defaults $argsflags] + #set opts [tcl::dict::merge $defaults $argsflags] # -- --- --- --- --- --- - set opt_transparent [dict get $opts -transparent] - set opt_ellipsis [dict get $opts -ellipsis] - set opt_ellipsistext [dict get $opts -ellipsistext] - set opt_ellipsiswhitespace [dict get $opts -ellipsiswhitespace] - set opt_overflow [dict get $opts -overflow] - set opt_exposed1 [dict get $opts -exposed1] - set opt_exposed2 [dict get $opts -exposed2] - set opt_align [dict get $opts -align] + set opt_transparent [tcl::dict::get $opts -transparent] + set opt_ellipsis [tcl::dict::get $opts -ellipsis] + set opt_ellipsistext [tcl::dict::get $opts -ellipsistext] + set opt_ellipsiswhitespace [tcl::dict::get $opts -ellipsiswhitespace] + set opt_overflow [tcl::dict::get $opts -overflow] + set opt_exposed1 [tcl::dict::get $opts -exposed1] + set opt_exposed2 [tcl::dict::get $opts -exposed2] + set opt_align [tcl::dict::get $opts -align] # -- --- --- --- --- --- - set norm [list \r\n \n] - set underblock [string map $norm $underblock] - set overblock [string map $norm $overblock] + set underblock [tcl::string::map {\r\n \n} $underblock] + set overblock [tcl::string::map {\r\n \n} $overblock] set underlines [split $underblock \n] #set colwidth [tcl::mathfunc::max {*}[lmap v $underlines {punk::ansi::printing_length $v}]] @@ -1359,27 +1387,27 @@ namespace eval overtype { set startoffset 0 ;#negative? } - set undertext [string cat $replay_codes_underlay $undertext] - set overtext [string cat $replay_codes_overlay $overtext] + set undertext [tcl::string::cat $replay_codes_underlay $undertext] + set overtext [tcl::string::cat $replay_codes_overlay $overtext] set overflowlength [expr {$overtext_datalen - $colwidth}] if {$overflowlength > 0} { #raw overtext wider than undertext column set rinfo [renderline -info 1 -insert_mode 0 -transparent $opt_transparent -exposed1 $opt_exposed1 -exposed2 $opt_exposed2 -overflow $opt_overflow -startcolumn [expr {1 + $startoffset}] $undertext $overtext] - set replay_codes [dict get $rinfo replay_codes] - set rendered [dict get $rinfo result] + set replay_codes [tcl::dict::get $rinfo replay_codes] + set rendered [tcl::dict::get $rinfo result] if {!$opt_overflow} { if {$opt_ellipsis} { set show_ellipsis 1 if {!$opt_ellipsiswhitespace} { #we don't want ellipsis if only whitespace was lost - set lostdata [string range $overtext end-[expr {$overflowlength-1}] end] - if {[string trim $lostdata] eq ""} { + set lostdata [tcl::string::range $overtext end-[expr {$overflowlength-1}] end] + if {[tcl::string::trim $lostdata] eq ""} { set show_ellipsis 0 } } if {$show_ellipsis} { - set ellipsis [string cat $replay_codes $opt_ellipsistext] + set ellipsis [tcl::string::cat $replay_codes $opt_ellipsistext] #todo - overflow on left if allign = right?? set rendered [overtype::right $rendered $ellipsis] } @@ -1391,16 +1419,218 @@ namespace eval overtype { #lappend outputlines [renderline -insert_mode 0 -transparent $opt_transparent -startcolumn [expr {$left_exposed + 1}] $undertext $overtext] #Note - we still need overflow here - as although the overtext is short - it may oveflow due to the startoffset set rinfo [renderline -info 1 -insert_mode 0 -transparent $opt_transparent -overflow $opt_overflow -startcolumn [expr {$left_exposed + 1 + $startoffset}] $undertext $overtext] - lappend outputlines [dict get $rinfo result] + lappend outputlines [tcl::dict::get $rinfo result] } - set replay_codes [dict get $rinfo replay_codes] - set replay_codes_underlay [dict get $rinfo replay_codes_underlay] - set replay_codes_overlay [dict get $rinfo replay_codes_overlay] + set replay_codes [tcl::dict::get $rinfo replay_codes] + set replay_codes_underlay [tcl::dict::get $rinfo replay_codes_underlay] + set replay_codes_overlay [tcl::dict::get $rinfo replay_codes_overlay] } return [join $outputlines \n] } + proc left {args} { + overtype::block -blockalign left {*}$args + } + #overtype a (possibly ragged) underblock with a (possibly ragged) overblock + proc block {args} { + variable default_ellipsis_horizontal + # @d !todo - implement overflow, length checks etc + + if {[llength $args] < 2} { + error {usage: ?-blockalign left|centre|right? ?-textalign left|centre|right? ?-overflow [1|0]? ?-transparent 0|? undertext overtext} + } + #foreach {underblock overblock} [lrange $args end-1 end] break + lassign [lrange $args end-1 end] underblock overblock + + set opts [tcl::dict::create\ + -ellipsis 0\ + -ellipsistext $default_ellipsis_horizontal\ + -ellipsiswhitespace 0\ + -overflow 0\ + -transparent 0\ + -exposed1 \uFFFD\ + -exposed2 \uFFFD\ + -textalign "left"\ + -textvertical "top"\ + -blockalign "left"\ + -blockalignbias left\ + -blockvertical "top"\ + ] + set argsflags [lrange $args 0 end-2] + tcl::dict::for {k v} $argsflags { + switch -- $k { + -blockalignbias - -ellipsis - -ellipsistext - -ellipsiswhitespace - -overflow - -transparent - -exposed1 - -exposed2 - -textalign - -blockalign - -blockvertical { + tcl::dict::set opts $k $v + } + default { + error "overtype::block unknown option '$k'. Known options: [tcl::dict::keys $opts]" + } + } + } + # -- --- --- --- --- --- + set opt_transparent [tcl::dict::get $opts -transparent] + set opt_ellipsis [tcl::dict::get $opts -ellipsis] + set opt_ellipsistext [tcl::dict::get $opts -ellipsistext] + set opt_ellipsiswhitespace [tcl::dict::get $opts -ellipsiswhitespace] + set opt_overflow [tcl::dict::get $opts -overflow] + set opt_exposed1 [tcl::dict::get $opts -exposed1] + set opt_exposed2 [tcl::dict::get $opts -exposed2] + set opt_textalign [tcl::dict::get $opts -textalign] + set opt_blockalign [tcl::dict::get $opts -blockalign] + if {$opt_blockalign eq "center"} { + set opt_blockalign "centre" + } + # -- --- --- --- --- --- + + set underblock [tcl::string::map {\r\n \n} $underblock] + set overblock [tcl::string::map {\r\n \n} $overblock] + + set underlines [split $underblock \n] + #set colwidth [tcl::mathfunc::max {*}[lmap v $underlines {punk::ansi::printing_length $v}]] + lassign [blocksize $underblock] _w colwidth _h colheight + set overlines [split $overblock \n] + #set overblock_width [tcl::mathfunc::max {*}[lmap v $overlines {punk::ansi::printing_length $v}]] + lassign [blocksize $overblock] _w overblock_width _h overblock_height + set under_exposed_max [expr {max(0,$colwidth - $overblock_width)}] + + switch -- $opt_blockalign { + left { + set left_exposed 0 + } + right { + set left_exposed $under_exposed_max + } + centre { + if {$under_exposed_max > 0} { + #background block is wider + if {$under_exposed_max % 2 == 0} { + #even left/right exposure + set left_exposed [expr {$under_exposed_max / 2}] + } else { + set beforehalf [expr {$under_exposed_max / 2}] ;#1 less than half due to integer division + if {[tcl::string::tolower [tcl::dict::get $opts -blockalignbias]] eq "left"} { + set left_exposed $beforehalf + } else { + #bias to the right + set left_exposed [expr {$beforehalf + 1}] + } + } + } else { + set left_exposed 0 + } + } + default { + set left_exposed 0 + } + } + + + + set outputlines [list] + if {[punk::ansi::ta::detect_sgr [lindex $overlines 0]]} { + set replay_codes "[punk::ansi::a]" + } else { + set replay_codes "" + } + set replay_codes_underlay "" + set replay_codes_overlay "" + foreach undertext $underlines overtext $overlines { + set overtext_datalen [punk::ansi::printing_length $overtext] + set ulen [punk::ansi::printing_length $undertext] + if {$ulen < $colwidth} { + set udiff [expr {$colwidth - $ulen}] + #puts xxx + append undertext [string repeat { } $udiff] + } + if {$overtext_datalen < $overblock_width} { + set odiff [expr {$overblock_width - $overtext_datalen}] + switch -- $opt_textalign { + left { + set startoffset 0 + } + right { + set startoffset $odiff + } + default { + set half [expr {$odiff / 2}] + #set lhs [string repeat { } $half] + #set righthalf [expr {$odiff - $half}] ;#remainder - may be one more - so we are biased left + #set rhs [string repeat { } $righthalf] + set startoffset $half + } + } + } else { + set startoffset 0 ;#negative? + } + + set undertext [tcl::string::cat $replay_codes_underlay $undertext] + set overtext [tcl::string::cat $replay_codes_overlay $overtext] + + set overflowlength [expr {$overtext_datalen - $colwidth}] + if {$overflowlength > 0} { + #raw overtext wider than undertext column + set rinfo [renderline -info 1 -insert_mode 0 -transparent $opt_transparent -exposed1 $opt_exposed1 -exposed2 $opt_exposed2 -overflow $opt_overflow -startcolumn [expr {1 + $startoffset}] $undertext $overtext] + set replay_codes [tcl::dict::get $rinfo replay_codes] + set rendered [tcl::dict::get $rinfo result] + set overflow_right [tcl::dict::get $rinfo overflow_right] + set unapplied [tcl::dict::get $rinfo unapplied] + if {!$opt_overflow} { + if {$opt_ellipsis} { + set show_ellipsis 1 + if {!$opt_ellipsiswhitespace} { + #we don't want ellipsis if only whitespace was lost + #don't use tcl::string::range on ANSI data + #set lostdata [tcl::string::range $overtext end-[expr {$overflowlength-1}] end] + set lostdata "" + if {$overflow_right ne ""} { + append lostdata $overflow_right + } + if {$unapplied ne ""} { + append lostdata $unapplied + } + if {[tcl::string::trim $lostdata] eq ""} { + set show_ellipsis 0 + } + } + if {$show_ellipsis} { + set rendered [overtype::block -blockalign right $rendered $opt_ellipsistext] + } + } + + #if {$opt_ellipsis} { + # set show_ellipsis 1 + # if {!$opt_ellipsiswhitespace} { + # #we don't want ellipsis if only whitespace was lost + # set lostdata [tcl::string::range $overtext end-[expr {$overflowlength-1}] end] + # if {[tcl::string::trim $lostdata] eq ""} { + # set show_ellipsis 0 + # } + # } + # if {$show_ellipsis} { + # set ellipsis [tcl::string::cat $replay_codes $opt_ellipsistext] + # #todo - overflow on left if allign = right?? + # set rendered [overtype::right $rendered $ellipsis] + # } + #} + } + lappend outputlines $rendered + } else { + #padded overtext + #lappend outputlines [renderline -insert_mode 0 -transparent $opt_transparent -startcolumn [expr {$left_exposed + 1}] $undertext $overtext] + #Note - we still need overflow here - as although the overtext is short - it may oveflow due to the startoffset + set rinfo [renderline -info 1 -insert_mode 0 -transparent $opt_transparent -overflow $opt_overflow -startcolumn [expr {$left_exposed + 1 + $startoffset}] $undertext $overtext] + set overflow_right [tcl::dict::get $rinfo overflow_right] + set unapplied [tcl::dict::get $rinfo unapplied] + lappend outputlines [tcl::dict::get $rinfo result] + } + set replay_codes [tcl::dict::get $rinfo replay_codes] + set replay_codes_underlay [tcl::dict::get $rinfo replay_codes_underlay] + set replay_codes_overlay [tcl::dict::get $rinfo replay_codes_overlay] + } + + return [join $outputlines \n] + } # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### # renderline written from a left-right line orientation perspective as a first-shot at getting something useful. # ultimately right-to-left, top-to-bottom and bottom-to-top are probably needed. @@ -1443,7 +1673,8 @@ namespace eval overtype { # error "overtype::renderline not allowed to contain newlines" #} - set defaults [dict create\ + #generally faster to create a new dict in the proc than to use a namespace variable to create dict once and link to variable (2024 8.6/8.7) + set opts [tcl::dict::create\ -etabs 0\ -width \uFFEF\ -overflow 0\ @@ -1458,6 +1689,7 @@ namespace eval overtype { -exposed1 \uFFFD\ -exposed2 \uFFFD\ -cursor_restore_attributes ""\ + -cp437 0\ -experimental {}\ ] #-cursor_restore_attributes only - for replay stack - position and actual setting/restoring handled by throwback to caller @@ -1469,41 +1701,41 @@ namespace eval overtype { #todo - return info about such grapheme 'cuts' in -info structure and/or create option to raise an error set argsflags [lrange $args 0 end-2] - dict for {k v} $argsflags { + tcl::dict::for {k v} $argsflags { switch -- $k { - -experimental - -width - -overflow - -transparent - -startcolumn - -cursor_column - -cursor_row - -insert_mode - -autowrap_mode - -reverse_mode - -info - -exposed1 - -exposed2 - -cursor_restore_attributes {} + -experimental - -cp437 - -width - -overflow - -transparent - -startcolumn - -cursor_column - -cursor_row - -insert_mode - -autowrap_mode - -reverse_mode - -info - -exposed1 - -exposed2 - -cursor_restore_attributes { + tcl::dict::set opts $k $v + } default { - set known_opts [dict keys $defaults] - error "overtype::renderline unknown option '$k'. Known options: $known_opts" + error "overtype::renderline unknown option '$k'. Known options: [tcl::dict::keys $opts]" } } } - set opts [dict merge $defaults $argsflags] # -- --- --- --- --- --- --- --- --- --- --- --- - set opt_width [dict get $opts -width] - set opt_etabs [dict get $opts -etabs] - set opt_overflow [dict get $opts -overflow] - set opt_colstart [dict get $opts -startcolumn] ;#lhs limit for overlay - an offset to cursor_column - first visible column is 1. 0 or < 0 are before the start of the underlay - set opt_colcursor [dict get $opts -cursor_column];#start cursor column relative to overlay - set opt_row_context [dict get $opts -cursor_row] + set opt_width [tcl::dict::get $opts -width] + set opt_etabs [tcl::dict::get $opts -etabs] + set opt_overflow [tcl::dict::get $opts -overflow] + set opt_colstart [tcl::dict::get $opts -startcolumn] ;#lhs limit for overlay - an offset to cursor_column - first visible column is 1. 0 or < 0 are before the start of the underlay + set opt_colcursor [tcl::dict::get $opts -cursor_column];#start cursor column relative to overlay + set opt_row_context [tcl::dict::get $opts -cursor_row] if {[string length $opt_row_context]} { - if {![string is integer -strict $opt_row_context] || $opt_row_context <1 } { + if {![tcl::string::is integer -strict $opt_row_context] || $opt_row_context <1 } { error "overtype::renderline -cursor_row must be empty for unspecified/unknown or a non-zero positive integer. received: '$opt_row_context'" } } # -- --- --- --- --- --- --- --- --- --- --- --- #The _mode flags correspond to terminal modes that can be set/reset via escape sequences (e.g DECAWM wraparound mode) - set opt_insert_mode [dict get $opts -insert_mode];#should usually be 1 for each new line in editor mode but must be initialised to 1 externally (review) + set opt_insert_mode [tcl::dict::get $opts -insert_mode];#should usually be 1 for each new line in editor mode but must be initialised to 1 externally (review) #default is for overtype # -- --- --- --- --- --- --- --- --- --- --- --- - set opt_autowrap_mode [dict get $opts -autowrap_mode] ;#DECAWM - char or movement can go beyond leftmost/rightmost col to prev/next line - set opt_reverse_mode [dict get $opts -reverse_mode] ;#DECSNM + set opt_autowrap_mode [tcl::dict::get $opts -autowrap_mode] ;#DECAWM - char or movement can go beyond leftmost/rightmost col to prev/next line + set opt_reverse_mode [tcl::dict::get $opts -reverse_mode] ;#DECSNM # -- --- --- --- --- --- --- --- --- --- --- --- - set temp_cursor_saved [dict get $opts -cursor_restore_attributes] + set temp_cursor_saved [tcl::dict::get $opts -cursor_restore_attributes] set test_mode 0 - set cp437_glyphs 0 - foreach e [dict get $opts -experimental] { + set cp437_glyphs [tcl::dict::get $opts -cp437] + foreach e [tcl::dict::get $opts -experimental] { switch -- $e { test_mode { set test_mode 1 @@ -1511,16 +1743,17 @@ namespace eval overtype { } } } - set cp437_map [dict create] + set test_mode 1 ;#try to elminate + set cp437_map [tcl::dict::create] if {$cp437_glyphs} { set cp437_map [set ::punk::ansi::cp437_map] #for cp437 images we need to map these *after* splitting ansi #some old files might use newline for its glyph.. but we can't easily support that. #Not sure how old files did it.. maybe cr lf in sequence was newline and any lone cr or lf were displayed as glyphs? - dict unset cp437_map \n + tcl::dict::unset cp437_map \n } - set opt_transparent [dict get $opts -transparent] + set opt_transparent [tcl::dict::get $opts -transparent] if {$opt_transparent eq "0"} { set do_transparency 0 } else { @@ -1530,10 +1763,10 @@ namespace eval overtype { } } # -- --- --- --- --- --- --- --- --- --- --- --- - set opt_returnextra [dict get $opts -info] + set opt_returnextra [tcl::dict::get $opts -info] # -- --- --- --- --- --- --- --- --- --- --- --- - set opt_exposed1 [dict get $opts -exposed1] - set opt_exposed2 [dict get $opts -exposed2] + set opt_exposed1 [tcl::dict::get $opts -exposed1] + set opt_exposed2 [tcl::dict::get $opts -exposed2] # -- --- --- --- --- --- --- --- --- --- --- --- if {$opt_row_context eq ""} { @@ -1548,6 +1781,8 @@ namespace eval overtype { if {[info exists punk::console::tabwidth]} { #punk console is updated if punk::console::set_tabstop_width is used or rep is started/restarted #It is way too slow to test the current width by querying the terminal here - so it could conceivably get out of sync + #todo - we also need to handle the new threaded repl where console config is in a different thread. + # - also - concept of sub-regions being mini-consoles with their own settings - 'id' for console, or use in/out channels as id? set tw $::punk::console::tabwidth } else { set tw 8 @@ -1576,7 +1811,12 @@ namespace eval overtype { # -- --- --- --- --- --- --- --- if {$under ne ""} { - set undermap [punk::ansi::ta::split_codes_single $under] + if {[punk::ansi::ta::detect $under]} { + set undermap [punk::ansi::ta::split_codes_single $under] + } else { + #single plaintext part + set undermap [list $under] + } } else { set undermap [list] } @@ -1594,7 +1834,7 @@ namespace eval overtype { #pt = plain text #append pt_underchars $pt if {$cp437_glyphs} { - set pt [string map $cp437_map $pt] + set pt [tcl::string::map $cp437_map $pt] } foreach grapheme [punk::char::grapheme_split $pt] { #an ugly hack to serve *some* common case ascii quickly with byte-compiled literal switch - feels dirty. @@ -1648,23 +1888,24 @@ namespace eval overtype { #only stack SGR (graphics rendition) codes - not title sets, cursor moves etc if {$code ne ""} { - set c1c2 [string range $code 0 1] - set leadernorm [string range [string map [list\ - \x1b\[ 7CSI\ - \x9b 8CSI\ - \x1b\( 7GFX\ - ] $c1c2] 0 3] ;#leadernorm is 1st 2 chars mapped to 4char normalised indicator - or is original 2 chars + set c1c2 [tcl::string::range $code 0 1] + + set leadernorm [tcl::string::range [tcl::string::map [list\ + \x1b\[ 7CSI\ + \x9b 8CSI\ + \x1b\( 7GFX\ + ] $c1c2] 0 3];# leadernorm is 1st 2 chars mapped to normalised indicator - or is original 2 chars switch -- $leadernorm { 7CSI - 8CSI { #need to exclude certain leaders after the lb e.g < for SGR 1006 mouse #REVIEW - what else could end in m but be mistaken as a normal SGR code here? set maybemouse "" - if {[string index $c1c2 0] eq "\x1b"} { - set maybemouse [string index $code 2] + if {[tcl::string::index $c1c2 0] eq "\x1b"} { + set maybemouse [tcl::string::index $code 2] } - if {$maybemouse ne "<" && [string index $code end] eq "m"} { + if {$maybemouse ne "<" && [tcl::string::index $code end] eq "m"} { if {[punk::ansi::codetype::is_sgr_reset $code]} { set u_codestack [list "\x1b\[m"] } elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} { @@ -1678,7 +1919,7 @@ namespace eval overtype { } } 7GFX { - switch -- [string index $code 2] { + switch -- [tcl::string::index $code 2] { "0" { set u_gx_stack [list gx0_on] ;#we'd better use a placeholder - or debugging will probably get into a big mess } @@ -1788,7 +2029,17 @@ namespace eval overtype { #this will be processed as transparent - and handle doublewidth underlay characters appropriately set startpad_overlay [string repeat " " [expr {$opt_colstart -1}]] append startpad_overlay $overdata ;#overdata with left padding spaces based on col-start under will show through for left-padding portion regardless of -transparency - set overmap [punk::ansi::ta::split_codes_single $startpad_overlay] + if {$startpad_overlay ne ""} { + if {[punk::ansi::ta::detect $startpad_overlay]} { + set overmap [punk::ansi::ta::split_codes_single $startpad_overlay] + } else { + #single plaintext part + set overmap [list $startpad_overlay] + } + } else { + set overmap [list] + } + #set overmap [punk::ansi::ta::split_codes_single $startpad_overlay] #### #??? @@ -1815,7 +2066,7 @@ namespace eval overtype { #todo - wrap in test for empty pt (we used split_codes_single - and it may be common for sgr sequences to be unmerged and so have empty pts between) if {$cp437_glyphs} { - set pt [string map $cp437_map $pt] + set pt [tcl::string::map $cp437_map $pt] } append pt_overchars $pt #will get empty pt between adjacent codes @@ -1879,8 +2130,8 @@ namespace eval overtype { #set replay_codes_overlay [join $o_codestack ""] set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}$o_codestack] - #if {[dict exists $overstacks $max_overlay_grapheme_index]} { - # set replay_codes_overlay [join [dict get $overstacks $max_overlay_grapheme_index] ""] + #if {[tcl::dict::exists $overstacks $max_overlay_grapheme_index]} { + # set replay_codes_overlay [join [tcl::dict::get $overstacks $max_overlay_grapheme_index] ""] #} else { # set replay_codes_overlay "" #} @@ -1941,6 +2192,17 @@ namespace eval overtype { set insert_mode $opt_insert_mode ;#default 1 set autowrap_mode $opt_autowrap_mode ;#default 1 + #set re_mode {\x1b\[\?([0-9]*)(h|l)} ;#e.g DECAWM + #set re_col_move {\x1b\[([0-9]*)(C|D|G)$} + #set re_row_move {\x1b\[([0-9]*)(A|B)$} + #set re_both_move {\x1b\[([0-9]*)(?:;){0,1}([0-9]*)H$} ;# or "f" ? + #set re_vt_sequence {\x1b\[([0-9]*)(?:;){0,1}([0-9]*)~$} + #set re_cursor_save {\x1b\[s$} ;#note probable incompatibility with DECSLRM (set left right margin)! + #set re_cursor_restore {\x1b\[u$} + #set re_cursor_save_dec {\x1b7$} + #set re_cursor_restore_dec {\x1b8$} + #set re_other_single {\x1b(D|M|E)$} + #set re_decstbm {\x1b\[([0-9]*)(?:;){0,1}([0-9]*)r$} ;#DECSTBM set top and bottom margins #puts "-->$overlay_grapheme_control_list<--" #puts "-->overflow_idx: $overflow_idx" @@ -1968,7 +2230,7 @@ namespace eval overtype { #This is hard to process in any standard manner - but I think the Hyper behaviour of doing what it was intended is perhaps most reasonable #We will map it to the same behaviour as lf here for now... but we need also to consider the equivalent ANSI sequence: \x1bE - set chtest [string map [list \n \x85 \b \r \v \x7f ] $ch] + set chtest [tcl::string::map [list \n \x85 \b \r \v \x7f ] $ch] #puts --->chtest:$chtest #specials - each shoud have it's own test of what to do if it happens after overflow_idx reached switch -- $chtest { @@ -2056,7 +2318,7 @@ namespace eval overtype { #review split 2w overflow? #we don't want to make the decision here to split a 2w into replacement characters at end of line and beginning of next line #better to consider the overlay char as unable to be applied to the line - #render empty string to column(?) - and reduce overlay grapheme index by one so that the current ch goes into unapplied + #render empty column(?) - and reduce overlay grapheme index by one so that the current ch goes into unapplied #throwing back to caller with instruction complicates its job - but is necessary to avoid making decsions for it here. priv::render_addchar $idx "" [lindex $overstacks $idx_over] [lindex $overstacks_gx $idx_over] $insert_mode #change the overflow_idx @@ -2095,7 +2357,7 @@ namespace eval overtype { #pre opt_colstart is effectively transparent (we have applied padding of required number of columns to left of overlay) if {$idx > [llength $outcols]-1} { lappend outcols " " - #dict set understacks $idx [list] ;#review - use idx-1 codestack? + #tcl::dict::set understacks $idx [list] ;#review - use idx-1 codestack? lset understacks $idx [list] incr idx incr cursor_column @@ -2123,7 +2385,7 @@ namespace eval overtype { if {[grapheme_width_cached $ch] == 1} { if {!$insert_mode} { #normal singlewide transparent overlay onto double-wide underlay - set next_pt_overchar [string index $pt_overchars $idx_over+1] ;#lookahead of next plain-text char in overlay + set next_pt_overchar [tcl::string::index $pt_overchars $idx_over+1] ;#lookahead of next plain-text char in overlay if {$next_pt_overchar eq ""} { #special-case trailing transparent - no next_pt_overchar incr idx @@ -2134,7 +2396,7 @@ namespace eval overtype { incr cursor_column } else { #next overlay char is not transparent.. first-half of underlying 2wide char is exposed - #priv::render_addchar $idx $opt_exposed1 [dict get $overstacks $idx_over] [dict get $overstacks_gx $idx_over] $insert_mode + #priv::render_addchar $idx $opt_exposed1 [tcl::dict::get $overstacks $idx_over] [tcl::dict::get $overstacks_gx $idx_over] $insert_mode priv::render_addchar $idx $opt_exposed1 [lindex $overstacks $idx_over] [lindex $overstacks_gx $idx_over] $insert_mode incr idx incr cursor_column @@ -2277,35 +2539,23 @@ namespace eval overtype { } other { - set code $item + #todo - consider CSI s DECSLRM vs ansi.sys \x1b\[s - we need \x1b\[s for oldschool ansi art - but may have to enable only for that. + #we should probably therefore reverse this mapping so that x1b7 x1b8 are the primary codes for save/restore + set code [tcl::string::map [list \x1b7 \x1b\[s \x1b8 \x1b\[u ] $item] #since this element isn't a grapheme - advance idx_over to next grapheme overlay when about to fill 'unapplied' - set re_mode {\x1b\[\?([0-9]*)(h|l)} ;#e.g DECAWM - set re_col_move {\x1b\[([0-9]*)(C|D|G)$} - set re_row_move {\x1b\[([0-9]*)(A|B)$} - set re_both_move {\x1b\[([0-9]*)(?:;){0,1}([0-9]*)H$} ;# or "f" ? - set re_vt_sequence {\x1b\[([0-9]*)(?:;){0,1}([0-9]*)~$} - set re_cursor_save {\x1b\[s$} ;#note probable incompatibility with DECSLRM (set left right margin)! - set re_cursor_restore {\x1b\[u$} - set re_cursor_save_dec {\x1b7$} - set re_cursor_restore_dec {\x1b8$} - set re_other_single {\x1b(D|M|E)$} - set re_decstbm {\x1b\[([0-9]*)(?:;){0,1}([0-9]*)r$} ;#DECSTBM set top and bottom margins set matchinfo [list] #remap of DEC cursor_save/cursor_restore from ESC sequence to equivalent CSI #probably not ideal - consider putting cursor_save/cursor_restore in functions so they can be called from the appropriate switch branch instead of using this mapping #review - cost/benefit of function calls within these switch-arms instead of inline code? - #todo - consider CSI s DECSLRM vs ansi.sys \x1b\[s - we need \x1b\[s for oldschool ansi art - but may have to enable only for that. - #we should probably therefore reverse this mapping so that x1b7 x1b8 are the primary codes for save/restore - set code [string map [list \x1b7 \x1b\[s \x1b8 \x1b\[u ] $code] - - - set c1 [string index $code 0] - set c1c2c3 [string range $code 0 2] + set c1 [tcl::string::index $code 0] + set c1c2c3 [tcl::string::range $code 0 2] #set re_ST_open {(?:\033P|\u0090|\033X|\u0098|\033\^|\u009e|\033_|\u009f)} - set leadernorm [string range [string map [list\ + #tcl 8.7 - faster to use inline list than to store it in a local var outside of loop. + #(surprising - but presumably ) + set leadernorm [tcl::string::range [tcl::string::map [list\ \x1b\[< 1006\ \x1b\[ 7CSI\ \x9b 8CSI\ @@ -2319,16 +2569,16 @@ namespace eval overtype { 1006 { #https://invisible-island.net/xterm/ctlseqs/ctlseqs.html #SGR (1006) CSI < followed by colon separated encoded-button-value,px,py ordinates and final M for button press m for button release - set codenorm [string cat $leadernorm [string range $code 3 end]] + set codenorm [tcl::string::cat $leadernorm [tcl::string::range $code 3 end]] } 7CSI - 7OSC { - set codenorm [string cat $leadernorm [string range $code 2 end]] + set codenorm [tcl::string::cat $leadernorm [tcl::string::range $code 2 end]] } 7ESC { - set codenorm [string cat $leadernorm [string range $code 1 end]] + set codenorm [tcl::string::cat $leadernorm [tcl::string::range $code 1 end]] } 8CSI - 8OSC { - set codenorm [string cat $leadernorm [string range $code 1 end]] + set codenorm [tcl::string::cat $leadernorm [tcl::string::range $code 1 end]] } default { #we haven't made a mapping for this @@ -2341,7 +2591,7 @@ namespace eval overtype { 1006 { #TODO # - switch -- [string index $codenorm end] { + switch -- [tcl::string::index $codenorm end] { M { puts stderr "mousedown $codenorm" } @@ -2352,9 +2602,9 @@ namespace eval overtype { } {7CSI} - {8CSI} { - set param [string range $codenorm 4 end-1] - #puts stdout "--> CSI [string index $leadernorm 0] bit param:$param" - switch -- [string index $codenorm end] { + set param [tcl::string::range $codenorm 4 end-1] + #puts stdout "--> CSI [tcl::string::index $leadernorm 0] bit param:$param" + switch -- [tcl::string::index $codenorm end] { D { #Col move #puts stdout "<-back" @@ -2459,8 +2709,8 @@ namespace eval overtype { #puts "idxstart:$idxstart idxend:$idxend outcols[llength $outcols] undercols:[llength $undercols]" incr idx $moveend incr cursor_column $moveend - #if {[dict exists $understacks $idx]} { - # set stackinfo [dict get $understacks $idx] ;#use understack at end - which may or may not have already been replaced by stack from overtext + #if {[tcl::dict::exists $understacks $idx]} { + # set stackinfo [tcl::dict::get $understacks $idx] ;#use understack at end - which may or may not have already been replaced by stack from overtext #} else { # set stackinfo [list] #} @@ -2470,7 +2720,7 @@ namespace eval overtype { set stackinfo [list] } if {$idx < [llength $understacks_gx]} { - #set gxstackinfo [dict get $understacks_gx $idx] + #set gxstackinfo [tcl::dict::get $understacks_gx $idx] set gxstackinfo [lindex $understacks_gx $idx] } else { set gxstackinfo [list] @@ -2780,8 +3030,8 @@ namespace eval overtype { #$re_mode if first after CSI is "?" #some docs mention ESC=h|l - not seen on windows terminals.. review #e.g https://www2.math.upenn.edu/~kazdan/210/computer/ansi.html - if {[string index $codenorm 4] eq "?"} { - set num [string range $codenorm 5 end-1] ;#param between ? and h|l + if {[tcl::string::index $codenorm 4] eq "?"} { + set num [tcl::string::range $codenorm 5 end-1] ;#param between ? and h|l #lassign $matchinfo _match num type switch -- $num { 5 { @@ -2842,7 +3092,7 @@ namespace eval overtype { } 7ESC { #$re_other_single - switch -- [string index $codenorm end] { + switch -- [tcl::string::index $codenorm end] { D { #\x84 #index (IND) @@ -2938,7 +3188,7 @@ namespace eval overtype { set gxleader "" if {$i < [llength $understacks_gx]} { - #set g0 [dict get $understacks_gx $i] + #set g0 [tcl::dict::get $understacks_gx $i] set g0 [lindex $understacks_gx $i] if {$g0 ne $prev_g0} { if {$g0 eq [list "gx0_on"]} { @@ -2954,7 +3204,7 @@ namespace eval overtype { set sgrleader "" if {$i < [llength $understacks]} { - #set cstack [dict get $understacks $i] + #set cstack [tcl::dict::get $understacks $i] set cstack [lindex $understacks $i] if {$cstack ne $prevstack} { if {[llength $prevstack] && ![llength $cstack]} { @@ -3008,7 +3258,7 @@ namespace eval overtype { append outstring $gxleader append outstring $sgrleader if {$idx+1 < $cursor_column} { - append outstring [string map [list "\u0000" " "] $ch] + append outstring [tcl::string::map {\u0000 " "} $ch] } else { append outstring $ch } @@ -3017,16 +3267,16 @@ namespace eval overtype { } #flower.ans good test for null handling - reverse line building if {![ansistring length $overflow_right]} { - set outstring [string trimright $outstring "\u0000"] + set outstring [tcl::string::trimright $outstring "\u0000"] } - set outstring [string map [list "\u0000" " "] $outstring] - set overflow_right [string trimright $overflow_right "\u0000"] - set overflow_right [string map [list "\u0000" " "] $overflow_right] + set outstring [tcl::string::map {\u0000 " "} $outstring] + set overflow_right [tcl::string::trimright $overflow_right "\u0000"] + set overflow_right [tcl::string::map {\u0000 " "} $overflow_right] set replay_codes "" if {[llength $understacks] > 0} { if {$overflow_idx == -1} { - #set tail_idx [dict size $understacks] + #set tail_idx [tcl::dict::size $understacks] set tail_idx [llength $understacks] } else { set tail_idx [llength $undercols] @@ -3069,7 +3319,7 @@ namespace eval overtype { } else { set overflow_right_column [expr {$overflow_idx+1}] } - set result [dict create\ + set result [tcl::dict::create\ result $outstring\ visualwidth [punk::ansi::printing_length $outstring]\ instruction $instruction\ @@ -3105,14 +3355,14 @@ namespace eval overtype { set viewop VIEWSTYLE ;#ansi colorise the characters within the output with preceding codes, stacking codes only within each dict value - may not be same SGR effect as the effect in-situ. } } - dict set result result [ansistring $viewop -lf 1 -vt 1 [dict get $result result]] - dict set result overflow_right [ansistring VIEW -lf 1 -vt 1 [dict get $result overflow_right]] - dict set result unapplied [ansistring VIEW -lf 1 -vt 1 [dict get $result unapplied]] - dict set result unapplied_list [ansistring VIEW -lf 1 -vt 1 [dict get $result unapplied_list]] - dict set result replay_codes [ansistring $viewop -lf 1 -vt 1 [dict get $result replay_codes]] - dict set result replay_codes_underlay [ansistring $viewop -lf 1 -vt 1 [dict get $result replay_codes_underlay]] - dict set result replay_codes_overlay [ansistring $viewop -lf 1 -vt 1 [dict get $result replay_codes_overlay]] - dict set result cursor_saved_attributes [ansistring $viewop -lf 1 -vt 1 [dict get $result cursor_saved_attributes]] + tcl::dict::set result result [ansistring $viewop -lf 1 -vt 1 [tcl::dict::get $result result]] + tcl::dict::set result overflow_right [ansistring VIEW -lf 1 -vt 1 [tcl::dict::get $result overflow_right]] + tcl::dict::set result unapplied [ansistring VIEW -lf 1 -vt 1 [tcl::dict::get $result unapplied]] + tcl::dict::set result unapplied_list [ansistring VIEW -lf 1 -vt 1 [tcl::dict::get $result unapplied_list]] + tcl::dict::set result replay_codes [ansistring $viewop -lf 1 -vt 1 [tcl::dict::get $result replay_codes]] + tcl::dict::set result replay_codes_underlay [ansistring $viewop -lf 1 -vt 1 [tcl::dict::get $result replay_codes_underlay]] + tcl::dict::set result replay_codes_overlay [ansistring $viewop -lf 1 -vt 1 [tcl::dict::get $result replay_codes_overlay]] + tcl::dict::set result cursor_saved_attributes [ansistring $viewop -lf 1 -vt 1 [tcl::dict::get $result cursor_saved_attributes]] return $result } } else { @@ -3125,7 +3375,7 @@ namespace eval overtype { #[list_end] [comment {--- end definitions namespace overtype ---}] } -namespace eval overtype::piper { +tcl::namespace::eval overtype::piper { proc overcentre {args} { if {[llength $args] < 2} { error {usage: ?-bias left|right? ?-transparent [0|1|]? ?-exposed1 ? ?-exposed2 ? ?-overflow [1|0]? overtext pipelinedata} @@ -3149,19 +3399,19 @@ namespace eval overtype::piper { proc overtype::transparentline {args} { foreach {under over} [lrange $args end-1 end] break set argsflags [lrange $args 0 end-2] - set defaults [dict create\ + set defaults [tcl::dict::create\ -transparent 1\ -exposed 1 " "\ -exposed 2 " "\ ] - set newargs [dict merge $defaults $argsflags] + set newargs [tcl::dict::merge $defaults $argsflags] tailcall overtype::renderline {*}$newargs $under $over } #renderline may not make sense as it is in the long run for blocks of text - but is handy in the single-line-handling form anyway. # We are trying to handle ansi codes in a block of text which is acting like a mini-terminal in some sense. #We can process standard cursor moves such as \b \r - but no way to respond to other cursor movements e.g moving to other lines. # -namespace eval overtype::piper { +tcl::namespace::eval overtype::piper { proc renderline {args} { if {[llength $args] < 2} { error {usage: ?-start ? ?-transparent [0|1|]? ?-overflow [1|0]? overtext pipelinedata} @@ -3178,11 +3428,11 @@ interp alias "" piper_renderline "" overtype::piper::renderline #(a cache of ansifreestring_width calls - as these are quite regex heavy) proc overtype::grapheme_width_cached {ch} { variable grapheme_widths - if {[dict exists $grapheme_widths $ch]} { - return [dict get $grapheme_widths $ch] + if {[tcl::dict::exists $grapheme_widths $ch]} { + return [tcl::dict::get $grapheme_widths $ch] } set width [punk::char::ansifreestring_width $ch] - dict set grapheme_widths $ch $width + tcl::dict::set grapheme_widths $ch $width return $width } @@ -3200,9 +3450,9 @@ proc overtype::test_renderline {} { #block width and height can be tricky. e.g \v handled differently on different terminal emulators and can affect both proc overtype::blocksize {textblock} { if {$textblock eq ""} { - return [dict create width 0 height 1] ;#no such thing as zero-height block - for consistency with non-empty strings having no line-endings + return [tcl::dict::create width 0 height 1] ;#no such thing as zero-height block - for consistency with non-empty strings having no line-endings } - if {[string first \t $textblock] >= 0} { + if {[tcl::string::first \t $textblock] >= 0} { if {[info exists punk::console::tabwidth]} { set tw $::punk::console::tabwidth } else { @@ -3214,8 +3464,8 @@ proc overtype::blocksize {textblock} { if {[punk::ansi::ta::detect $textblock]} { set textblock [punk::ansi::stripansi $textblock] } - if {[string first \n $textblock] >= 0} { - set num_le [expr {[string length $textblock]-[string length [string map [list \n {}] $textblock]]}] ;#faster than splitting into single-char list + if {[tcl::string::last \n $textblock] >= 0} { + set num_le [expr {[tcl::string::length $textblock]-[tcl::string::length [tcl::string::map {\n {}} $textblock]]}] ;#faster than splitting into single-char list set width [tcl::mathfunc::max {*}[lmap v [split $textblock \n] {::punk::char::ansifreestring_width $v}]] } else { set num_le 0 @@ -3224,22 +3474,22 @@ proc overtype::blocksize {textblock} { #our concept of block-height is likely to be different to other line-counting mechanisms set height [expr {$num_le + 1}] ;# one line if no le - 2 if there is one trailing le even if no data follows le - return [dict create width $width height $height] ;#maintain order in 'image processing' standard width then height - caller may use lassign [dict values [blocksize ]] width height + return [tcl::dict::create width $width height $height] ;#maintain order in 'image processing' standard width then height - caller may use lassign [dict values [blocksize ]] width height } -namespace eval overtype::priv { - variable cache_is_sgr [dict create] +tcl::namespace::eval overtype::priv { + variable cache_is_sgr [tcl::dict::create] #we are likely to be asking the same question of the same ansi codes repeatedly #caching the answer saves some regex expense - possibly a few uS to lookup vs under 1uS #todo - test if still worthwhile after a large cache is built up. (limit cache size?) proc is_sgr {code} { variable cache_is_sgr - if {[dict exists $cache_is_sgr $code]} { - return [dict get $cache_is_sgr $code] + if {[tcl::dict::exists $cache_is_sgr $code]} { + return [tcl::dict::get $cache_is_sgr $code] } set answer [punk::ansi::codetype::is_sgr $code] - dict set cache_is_sgr $code $answer + tcl::dict::set cache_is_sgr $code $answer return $answer } proc render_unapplied {overlay_grapheme_control_list gci} { @@ -3344,7 +3594,7 @@ namespace eval overtype::priv { upvar understacks_gx gxstacks #ECH clears character attributes from erased character positions #ECH accepts 0 or empty parameter, which is equivalent to 1. Caller should do that mapping and only supply 1 or greater. - if {![string is integer -strict $count] || $count < 1} { + if {![tcl::string::is integer -strict $count] || $count < 1} { error "render_erasechar count must be integer >= 1" } set start $i @@ -3419,15 +3669,15 @@ namespace eval overtype::priv { # -- --- --- --- --- --- --- --- --- --- --- -namespace eval overtype { +tcl::namespace::eval overtype { interp alias {} ::overtype::center {} ::overtype::centre } # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ ## Ready -package provide overtype [namespace eval overtype { +package provide overtype [tcl::namespace::eval overtype { variable version - set version 1.6.2 + set version 1.6.4 }] return diff --git a/src/bootsupport/modules/punk/ansi-0.1.1.tm b/src/bootsupport/modules/punk/ansi-0.1.1.tm index 647da08..fd14bca 100644 --- a/src/bootsupport/modules/punk/ansi-0.1.1.tm +++ b/src/bootsupport/modules/punk/ansi-0.1.1.tm @@ -68,8 +68,8 @@ package require punk::assertion #[section API] -namespace eval punk::ansi::class { - if {![llength [info commands class_ansi]]} { +tcl::namespace::eval punk::ansi::class { + if {![llength [tcl::info::commands class_ansi]]} { oo::class create class_ansi { variable o_ansistringobj @@ -141,27 +141,27 @@ namespace eval punk::ansi::class { if {[llength $arglist] %2 != 0} { puts stderr "render_to_input_line usage: ?-dimensions WxH? ?-minus charcount? x" } - set opts [dict create\ + set opts [tcl::dict::create\ -dimensions 80x24\ -minus 0\ ] - dict for {k v} $arglist { + foreach {k v} $arglist { switch -- $k { -dimensions - -minus { - dict set opts $k $v + tcl::dict::set opts $k $v } default { puts stderr "render_to_input_line unexpected argument '$k' usage: ?-dimensions WxH? ?-minus charcount? x" } } } - set opt_dimensions [dict get $opts -dimensions] - set opt_minus [dict get $opts -minus] + set opt_dimensions [tcl::dict::get $opts -dimensions] + set opt_minus [tcl::dict::get $opts -minus] lassign [split $opt_dimensions x] w h - if {![string is integer -strict $w] || ![string is integer -strict $h] || $w < 1 || $h < 1} { + if {![tcl::string::is integer -strict $w] || ![tcl::string::is integer -strict $h] || $w < 1 || $h < 1} { puts stderr "render_to_input_line WxH width & height must be positive integer values usage: ?-dimensions WxH? ?-minus charcount? x" } - if {![string is integer -strict $opt_minus]} { + if {![tcl::string::is integer -strict $opt_minus]} { puts stderr "render_to_input_line -minus must be positive integer value representing number of chars to exclude from end. usage: ?-dimensions WxH? ?-minus charcount? x" } @@ -174,7 +174,7 @@ namespace eval punk::ansi::class { set chunk [::join $rlines \n] append chunk \n if {$opt_minus ne "0"} { - set chunk [string range $chunk 0 end-$opt_minus] + set chunk [tcl::string::range $chunk 0 end-$opt_minus] } set rendered [overtype::renderspace -experimental {test_mode} -overflow 0 -wrap 1 -width $w -height $h -appendlines 1 "" $chunk] set marker "" @@ -190,12 +190,12 @@ namespace eval punk::ansi::class { ::append rendered \n $marker set xline [lindex $rlines $x]\n set xlinev [ansistring VIEWSTYLE $xline] - set xlinev [string map $maplf $xlinev] + set xlinev [tcl::string::map $maplf $xlinev] set xlinedisplay [overtype::renderspace -wrap 1 -width $w -height 1 "" $xlinev] ::append rendered \n $xlinedisplay set chunk [ansistring VIEWSTYLE $chunk] - set chunk [string map $maplf $chunk] + set chunk [tcl::string::map $maplf $chunk] #keep chunkdisplay narrower - leave at 80 or it will get unwieldy for larger image widths set chunkdisplay [overtype::renderspace -wrap 1 -width 80 -height 1 "" $chunk] set renderheight [llength [split $rendered \n]] @@ -203,7 +203,7 @@ namespace eval punk::ansi::class { set chunkdisplay_tail [lrange $chunkdisplay_lines end-$renderheight end] set chunkdisplay_block [join $chunkdisplay_tail \n] #the input chunk lines are often much longer than the output.. resulting in main content being way up the screen. It's often impractical to view more than the tail of the chunkdisplay. - textblock::join $rendered $chunkdisplay_block + textblock::join -- $rendered $chunkdisplay_block } method checksum {} { @@ -226,23 +226,23 @@ namespace eval punk::ansi::class { foreach {k v} $args { switch -- $k { -lf - -vt - -width { - dict set opts $k $v + tcl::dict::set opts $k $v } default { - error "viewcodes unrecognised option '$k'. Known options [dict keys $defaults]" + error "viewcodes unrecognised option '$k'. Known options [tcl::dict::keys $defaults]" } } } - set opts_lf [dict get $opts -lf] - set opts_vt [dict get $opts -vt] - set opts_width [dict get $opts -width] + set opts_lf [tcl::dict::get $opts -lf] + set opts_vt [tcl::dict::get $opts -vt] + set opts_width [tcl::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} { + } elseif {[tcl::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" @@ -256,21 +256,21 @@ namespace eval punk::ansi::class { foreach {k v} $args { switch -- $k { -width { - dict set opts $k $v + tcl::dict::set opts $k $v } default { - error "viewchars unrecognised option '$k'. Known options [dict keys $defaults]" + error "viewchars unrecognised option '$k'. Known options [tcl::dict::keys $defaults]" } } } - set opts_width [dict get $opts -width] + set opts_width [tcl::dict::get $opts -width] if {$opts_width eq ""} { 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} { + } elseif {[tcl::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" @@ -284,21 +284,21 @@ namespace eval punk::ansi::class { foreach {k v} $args { switch -- $k { -width { - dict set opts $k $v + tcl::dict::set opts $k $v } default { - error "viewstyle unrecognised option '$k'. Known options [dict keys $defaults]" + error "viewstyle unrecognised option '$k'. Known options [tcl::dict::keys $defaults]" } } } - set opts_width [dict get $opts -width] + set opts_width [tcl::dict::get $opts -width] if {$opts_width eq ""} { 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} { + } elseif {[tcl::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" @@ -321,7 +321,7 @@ namespace eval punk::ansi::class { # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -namespace eval punk::ansi { +tcl::namespace::eval punk::ansi { #*** !doctools #[subsection {Namespace punk::ansi}] #[para] Core API functions for punk::ansi @@ -336,81 +336,81 @@ namespace eval punk::ansi { #by mapping these we can display regardless. #nul char - no cp437 image but commonly used as space in ansi graphics. #(This is a potential conflict because we use nul as a filler to mean empty column in overtype rendering) REVIEW - dict set cp437_map \u0000 " " ;#space - dict set cp437_map \u0001 \u263A ;#smiley - dict set cp437_map \u0002 \u263B ;#smiley-filled - dict set cp437_map \u0003 \u2665 ;#heart - dict set cp437_map \u0004 \u2666 ;#diamond - dict set cp437_map \u0005 \u2663 ;#club - dict set cp437_map \u0006 \u2660 ;#spade - dict set cp437_map \u0007 \u2022 ;#dot - dict set cp437_map \u0008 \u25D8 ;#square hollow dot - dict set cp437_map \u0009 \u25CB ;#hollow dot - dict set cp437_map \u000A \u25D9 ;#square and dot (\n) - dict set cp437_map \u000B \u2642 ;#male - dict set cp437_map \u000C \u2640 ;#female - dict set cp437_map \u000D \u266A ;#note1 (\r) - dict set cp437_map \u000E \u266B ;#note2 - dict set cp437_map \u000F \u263C ;#sun - dict set cp437_map \u0010 \u25BA ;#right arrow triangle - dict set cp437_map \u0011 \u25CA ;#left arrow triangle - dict set cp437_map \u0012 \u2195 ;#updown arrow - dict set cp437_map \u0013 \u203C ;#double bang - dict set cp437_map \u0014 \u00B6 ;#pilcrow (paragraph mark / blind P) - dict set cp437_map \u0015 \u00A7 ;#Section Sign - dict set cp437_map \u0016 \u25AC ;#Heavy horizontal? - dict set cp437_map \u0017 \u21A8 ;#updown arrow 2 ? - dict set cp437_map \u0018 \u2191 ;#up arrow - dict set cp437_map \u0019 \u2193 ;#down arrow - dict set cp437_map \u001A \u2192 ;#right arrow - dict set cp437_map \u001B \u2190 ;#left arrow - dict set cp437_map \u001C \u221F ;#bottom left corner - dict set cp437_map \u001D \u2194 ;#left-right arrow - dict set cp437_map \u001E \u25B2 ;#up arrow triangle - dict set cp437_map \u001F \u25BC ;#down arrow triangle + tcl::dict::set cp437_map \u0000 " " ;#space + tcl::dict::set cp437_map \u0001 \u263A ;#smiley + tcl::dict::set cp437_map \u0002 \u263B ;#smiley-filled + tcl::dict::set cp437_map \u0003 \u2665 ;#heart + tcl::dict::set cp437_map \u0004 \u2666 ;#diamond + tcl::dict::set cp437_map \u0005 \u2663 ;#club + tcl::dict::set cp437_map \u0006 \u2660 ;#spade + tcl::dict::set cp437_map \u0007 \u2022 ;#dot + tcl::dict::set cp437_map \u0008 \u25D8 ;#square hollow dot + tcl::dict::set cp437_map \u0009 \u25CB ;#hollow dot + tcl::dict::set cp437_map \u000A \u25D9 ;#square and dot (\n) + tcl::dict::set cp437_map \u000B \u2642 ;#male + tcl::dict::set cp437_map \u000C \u2640 ;#female + tcl::dict::set cp437_map \u000D \u266A ;#note1 (\r) + tcl::dict::set cp437_map \u000E \u266B ;#note2 + tcl::dict::set cp437_map \u000F \u263C ;#sun + tcl::dict::set cp437_map \u0010 \u25BA ;#right arrow triangle + tcl::dict::set cp437_map \u0011 \u25CA ;#left arrow triangle + tcl::dict::set cp437_map \u0012 \u2195 ;#updown arrow + tcl::dict::set cp437_map \u0013 \u203C ;#double bang + tcl::dict::set cp437_map \u0014 \u00B6 ;#pilcrow (paragraph mark / blind P) + tcl::dict::set cp437_map \u0015 \u00A7 ;#Section Sign + tcl::dict::set cp437_map \u0016 \u25AC ;#Heavy horizontal? + tcl::dict::set cp437_map \u0017 \u21A8 ;#updown arrow 2 ? + tcl::dict::set cp437_map \u0018 \u2191 ;#up arrow + tcl::dict::set cp437_map \u0019 \u2193 ;#down arrow + tcl::dict::set cp437_map \u001A \u2192 ;#right arrow + tcl::dict::set cp437_map \u001B \u2190 ;#left arrow + tcl::dict::set cp437_map \u001C \u221F ;#bottom left corner + tcl::dict::set cp437_map \u001D \u2194 ;#left-right arrow + tcl::dict::set cp437_map \u001E \u25B2 ;#up arrow triangle + tcl::dict::set cp437_map \u001F \u25BC ;#down arrow triangle variable map_special_graphics #DEC Special Graphics set https://en.wikipedia.org/wiki/DEC_Special_Graphics #AKA IBM Code page 1090 - dict set map_special_graphics _ \u00a0 ;#no-break space - dict set map_special_graphics "`" \u25c6 ;#black diamond - dict set map_special_graphics a \u2592 ;#shaded block (checkerboard stipple), medium shade - Block Elements - dict set map_special_graphics b \u2409 ;#symbol for HT - dict set map_special_graphics c \u240c ;#symbol for FF - dict set map_special_graphics d \u240d ;#symbol for CR - dict set map_special_graphics e \u240a ;#symbol for LF - dict set map_special_graphics f \u00b0 ;#degree sign - dict set map_special_graphics g \u00b1 ;#plus-minus sign - dict set map_special_graphics h \u2424 ;#symbol for NL - dict set map_special_graphics i \u240b ;#symbol for VT - dict set map_special_graphics j \u2518 ;#brc, light up and left - box drawing - dict set map_special_graphics k \u2510 ;#trc, light down and left - box drawing - dict set map_special_graphics l \u250c ;#tlc, light down and right - box drawing - dict set map_special_graphics m \u2514 ;#blc, light up and right - box drawing - dict set map_special_graphics n \u253c ;#light vertical and horizontal - box drawing - dict set map_special_graphics o \u23ba ;#horizontal scan line-1 - dict set map_special_graphics p \u23bb ;#horizontal scan line-3 - dict set map_special_graphics q \u2500 ;#light horizontal - box drawing - dict set map_special_graphics r \u23bc ;#horizontal scan line-7 - dict set map_special_graphics s \u23bd ;#horizontal scan line-9 - dict set map_special_graphics t \u251c ;#light vertical and right - box drawing - dict set map_special_graphics u \u2524 ;#light vertical and left - box drawing - dict set map_special_graphics v \u2534 ;#light up and horizontal - box drawing - dict set map_special_graphics w \u252c ;#light down and horizontal - box drawing - dict set map_special_graphics x \u2502 ;#light vertical - box drawing - dict set map_special_graphics y \u2264 ;#less than or equal - dict set map_special_graphics z \u2265 ;#greater than or equal - dict set map_special_graphics "\{" \u03c0 ;#greek small letter pi - dict set map_special_graphics "|" \u2260 ;#not equal to - dict set map_special_graphics "\}" \u00a3 ;#pound sign - dict set map_special_graphics ~ \u00b7 ;#middle dot + tcl::dict::set map_special_graphics _ \u00a0 ;#no-break space + tcl::dict::set map_special_graphics "`" \u25c6 ;#black diamond + tcl::dict::set map_special_graphics a \u2592 ;#shaded block (checkerboard stipple), medium shade - Block Elements + tcl::dict::set map_special_graphics b \u2409 ;#symbol for HT + tcl::dict::set map_special_graphics c \u240c ;#symbol for FF + tcl::dict::set map_special_graphics d \u240d ;#symbol for CR + tcl::dict::set map_special_graphics e \u240a ;#symbol for LF + tcl::dict::set map_special_graphics f \u00b0 ;#degree sign + tcl::dict::set map_special_graphics g \u00b1 ;#plus-minus sign + tcl::dict::set map_special_graphics h \u2424 ;#symbol for NL + tcl::dict::set map_special_graphics i \u240b ;#symbol for VT + tcl::dict::set map_special_graphics j \u2518 ;#brc, light up and left - box drawing + tcl::dict::set map_special_graphics k \u2510 ;#trc, light down and left - box drawing + tcl::dict::set map_special_graphics l \u250c ;#tlc, light down and right - box drawing + tcl::dict::set map_special_graphics m \u2514 ;#blc, light up and right - box drawing + tcl::dict::set map_special_graphics n \u253c ;#light vertical and horizontal - box drawing + tcl::dict::set map_special_graphics o \u23ba ;#horizontal scan line-1 + tcl::dict::set map_special_graphics p \u23bb ;#horizontal scan line-3 + tcl::dict::set map_special_graphics q \u2500 ;#light horizontal - box drawing + tcl::dict::set map_special_graphics r \u23bc ;#horizontal scan line-7 + tcl::dict::set map_special_graphics s \u23bd ;#horizontal scan line-9 + tcl::dict::set map_special_graphics t \u251c ;#light vertical and right - box drawing + tcl::dict::set map_special_graphics u \u2524 ;#light vertical and left - box drawing + tcl::dict::set map_special_graphics v \u2534 ;#light up and horizontal - box drawing + tcl::dict::set map_special_graphics w \u252c ;#light down and horizontal - box drawing + tcl::dict::set map_special_graphics x \u2502 ;#light vertical - box drawing + tcl::dict::set map_special_graphics y \u2264 ;#less than or equal + tcl::dict::set map_special_graphics z \u2265 ;#greater than or equal + tcl::dict::set map_special_graphics "\{" \u03c0 ;#greek small letter pi + tcl::dict::set map_special_graphics "|" \u2260 ;#not equal to + tcl::dict::set map_special_graphics "\}" \u00a3 ;#pound sign + tcl::dict::set map_special_graphics ~ \u00b7 ;#middle dot #see also ansicolour page on wiki https://wiki.tcl-lang.org/page/ANSI+color+control variable test "blah\033\[1;33mETC\033\[0;mOK" #Note that a? is actually a pattern. We can't explicitly match it without also matcing a+ ab etc. Presumably this won't matter here. - namespace export\ + tcl::namespace::export\ {a?} {a+} a \ ansistring\ convert*\ @@ -427,11 +427,11 @@ namespace eval punk::ansi { variable escape_terminals #single "final byte" in the range 0x40–0x7E (ASCII @A–Z[\]^_`a–z{|}~). - dict set escape_terminals CSI [list @ \\ ^ _ ` | ~ a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z "\{" "\}"] - #dict set escape_terminals CSI [list J K m n A B C D E F G s u] ;#basic - dict set escape_terminals OSC [list \007 \033\\ \u009c] ;#note mix of 1 and 2-byte terminals - dict set escape_terminals DCS [list \007 \033\\ \u009c] - dict set escape_terminals MISC [list \007 \033\\ \u009c] + tcl::dict::set escape_terminals CSI [list @ \\ ^ _ ` | ~ a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z "\{" "\}"] + #tcl::dict::set escape_terminals CSI [list J K m n A B C D E F G s u] ;#basic + tcl::dict::set escape_terminals OSC [list \007 \033\\ \u009c] ;#note mix of 1 and 2-byte terminals + tcl::dict::set escape_terminals DCS [list \007 \033\\ \u009c] + tcl::dict::set escape_terminals MISC [list \007 \033\\ \u009c] #NOTE - we are assuming an OSC or DCS started with one type of sequence (7 or 8bit) can be terminated by either 7 or 8 bit ST (or BEL e.g wezterm ) #This using a different type of ST to that of the opening sequence is presumably unlikely in the wild - but who knows? @@ -439,7 +439,7 @@ namespace eval punk::ansi { # https://espterm.github.io/docs/VT100%20escape%20codes.html #self-contained 2 byte ansi escape sequences - review more? - set ansi_2byte_codes_dict [dict create\ + set ansi_2byte_codes_dict [tcl::dict::create\ "reset_terminal" "\u001bc"\ "save_cursor_posn" "\u001b7"\ "restore_cursor_posn" "\u001b8"\ @@ -457,9 +457,9 @@ namespace eval punk::ansi { proc test_cat1 {ansi1 ansi2} { #make sure objects have splits set s1 [ansistring NEW $ansi1] - namespace eval [info object namespace $s1] {my MakeSplit} + tcl::namespace::eval [info object namespace $s1] {my MakeSplit} set s2 [ansistring NEW $ansi2] - namespace eval [info object namespace $s2] {my MakeSplit} + tcl::namespace::eval [info object namespace $s2] {my MakeSplit} #operation under test # -- @@ -474,9 +474,9 @@ namespace eval punk::ansi { proc test_cat2 {ansi1 ansi2} { #make sure objects have splits set s1 [ansistring NEW $ansi1] - namespace eval [info object namespace $s1] {my MakeSplit} + tcl::namespace::eval [info object namespace $s1] {my MakeSplit} set s2 [ansistring NEW $ansi2] - namespace eval [info object namespace $s2] {my MakeSplit} + tcl::namespace::eval [info object namespace $s2] {my MakeSplit} #operation under test # -- @@ -572,16 +572,16 @@ namespace eval punk::ansi { foreach f $fnames { if {![file exists $ansibase/$f]} { set p [overtype::left $missingbase "[a+ red bold]\nMissing file\n$ansibase/$f[a]"] - lappend pics [dict create filename $f pic $p status missing] + lappend pics [tcl::dict::create filename $f pic $p status missing] } else { set img [join [lines_as_list -line trimline -block trimtail [ansicat $ansibase/$f]] \n] - lappend pics [dict create filename $f pic $img status ok] + lappend pics [tcl::dict::create filename $f pic $img status ok] } } set termsize [punk::console:::get_size] set margin 4 - set freewidth [expr {[dict get $termsize columns]-$margin}] + set freewidth [expr {[tcl::dict::get $termsize columns]-$margin}] set per_row [expr {$freewidth / 80}] set rowlist [list] @@ -589,11 +589,11 @@ namespace eval punk::ansi { set i 1 foreach picinfo $pics { set subtitle "" - if {[dict get $picinfo status] ne "ok"} { - set subtitle [dict get $picinfo status] + if {[tcl::dict::get $picinfo status] ne "ok"} { + set subtitle [tcl::dict::get $picinfo status] } - set title [dict get $picinfo filename] - lappend row [textblock::frame -subtitle $subtitle -title $title [dict get $picinfo pic]] + set title [tcl::dict::get $picinfo filename] + lappend row [textblock::frame -subtitle $subtitle -title $title [tcl::dict::get $picinfo pic]] if {$i % $per_row == 0} { lappend rowlist $row set row [list] @@ -605,7 +605,7 @@ namespace eval punk::ansi { set result "" foreach r $rowlist { - append result [textblock::join {*}$r] \n + append result [textblock::join_basic -- {*}$r] \n } @@ -658,30 +658,14 @@ namespace eval punk::ansi { #This doesn't change the output length - so support is tricky to detec. (terminal checksum report?) #candidate for zig/c implementation? - proc stripansi {text} { - #*** !doctools - #[call [fun stripansi] [arg text] ] - #[para]Return a string with ansi codes stripped out - #[para]Alternate graphics chars are replaced with modern unicode equivalents (e.g boxdrawing glyphs) + proc stripansi2 {text} { - #using detect costs us a couple of uS - but saves time on plain text - #we should probably leave this for caller - otherwise it ends up being called more than necessary - #if {![::punk::ansi::ta::detect $text]} { - # return $text - #} set text [convert_g0 $text];#Convert ansi borders to unicode line drawing instead of ascii letters join [::punk::ansi::ta::split_at_codes $text] "" } - proc stripansiraw {text} { - #*** !doctools - #[call [fun stripansi] [arg text] ] - #[para]Return a string with ansi codes stripped out - #[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 - join [::punk::ansi::ta::split_at_codes $text] "" - } + proc stripansi1 {text} { #todo - character set selection - SS2 SS3 - how are they terminated? REVIEW @@ -692,7 +676,7 @@ namespace eval punk::ansi { set text [convert_g0 $text] - set text [string map $standalone_code_map $text] + set text [tcl::string::map $standalone_code_map $text] #e.g standalone 2 byte and 3 byte VT100(?) sequences - some of these work in wezterm #\x1b#3 double-height letters top half #\x1b#4 double-height letters bottom half @@ -720,7 +704,7 @@ namespace eval punk::ansi { #2nd byte - done. set in_escapesequence 0 } elseif {$in_escapesequence != 0} { - set endseq [dict get $escape_terminals $in_escapesequence] + set endseq [tcl::dict::get $escape_terminals $in_escapesequence] if {$u in $endseq} { set in_escapesequence 0 } elseif {$uv in $endseq} { @@ -754,7 +738,7 @@ namespace eval punk::ansi { variable map_special_graphics #using not \033 inside to stop greediness - review how does it compare to ".*?" - #variable re_altg0_group {(?:\x1b\(0)(?:(?!\x1b\(B).)*\x1b\(B} + #variable re_g0_group {(?:\x1b\(0)(?:(?!\x1b\(B).)*\x1b\(B} #set re {\033\(0[^\033]*\033\(B} #set re {(?:\x1b\(0)(?:(?!\x1b\(B).)*\x1b\(B} #set re2 {\033\(0(.*)\033\(B} ;#capturing @@ -766,24 +750,25 @@ namespace eval punk::ansi { #mqj #m = boxd_lur + #don't call detect_g0 in here. Leave for caller. e.g stripansi uses detect_g0 to decide whether to call this. set re_g0_open_or_close {\x1b\(0|\x1b\(B} set parts [::punk::ansi::ta::_perlish_split $re_g0_open_or_close $text] - set out "" + set out {} set g0_on 0 foreach {other g} $parts { if {$g0_on} { #split for non graphics-set codes set othersplits [punk::ansi::ta::split_codes $other] ;#we don't need single codes here foreach {inner_plaintext inner_codes} $othersplits { - append out [string map $map_special_graphics $inner_plaintext] $inner_codes + lappend out [tcl::string::map $map_special_graphics $inner_plaintext] $inner_codes #Simplifying assumption: no mapping required on any inner_codes - ST codes, titlesets etc don't require/use g0 content } } else { - append out $other ;#may be a mix of plaintext and other ansi codes - put it all through. + lappend out $other ;#may be a mix of plaintext and other ansi codes - put it all through. } #trust our splitting regex has done the work to leave us with only \x1b\(0 or \x1b(B - test last char rather than use punk::ansi::codetype::is_gx_open/is_gx_close - switch -- [string index $g end] { + switch -- [tcl::string::index $g end] { 0 { set g0_on 1 } @@ -792,13 +777,13 @@ namespace eval punk::ansi { } } } - return $out + return [join $out ""] } proc convert_g0_wrong {text} { #Attempting to split on a group is wrong - because there could be other ansi codes while inside a g0 section #That will either stop us matching - so no conversion - or risk converting parts of the ansi codes #using not \033 inside to stop greediness - review how does it compare to ".*?" - #variable re_altg0_group {(?:\x1b\(0)(?:(?!\x1b\(B).)*\x1b\(B} + #variable re_g0_group {(?:\x1b\(0)(?:(?!\x1b\(B).)*\x1b\(B} set re {\033\(0[^\033]*\033\(B} set re2 {\033\(0(.*)\033\(B} ;#capturing @@ -818,7 +803,7 @@ namespace eval punk::ansi { if {$g ne ""} { #puts --$g-- regexp $re2 $g _match contents - append out [string map $map $contents] + append out [tcl::string::map $map $contents] } } return $out @@ -835,7 +820,7 @@ namespace eval punk::ansi { #e.g "\033)X" - where X is any char other than 0 to reset ?? #return [convert_g0 $text] - return [string map [list "\x1b(0" "" \x1b(B" "" "\x1b)0" "" "\x1b)X" ""] $text] + return [tcl::string::map [list "\x1b(0" "" \x1b(B" "" "\x1b)0" "" "\x1b)X" ""] $text] } @@ -857,7 +842,7 @@ brightblack 90 brightred 91 brightgreen 92 brightyellow 93 brightblu 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] + set SGR_map [tcl::dict::merge $SGR_colour_map $SGR_setting_map] #we use prefixes e.g web-white and/or x11-white #Only a leading capital letter will indicate the colour target is background vs lowercase for foreground @@ -874,197 +859,197 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #css 1-2.0 HTML 3.2-4 Basic colours eg web-silver for fg Web-silver for bg # 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 + tcl::dict::set WEB_colour_map_basic white 255-255-255 ;# #FFFFFF + tcl::dict::set WEB_colour_map_basic silver 192-192-192 ;# #C0C0C0 + tcl::dict::set WEB_colour_map_basic gray 128-128-128 ;# #808080 + tcl::dict::set WEB_colour_map_basic black 0-0-0 ;# #000000 + tcl::dict::set WEB_colour_map_basic red 255-0-0 ;# #FF0000 + tcl::dict::set WEB_colour_map_basic maroon 128-0-0 ;# #800000 + tcl::dict::set WEB_colour_map_basic yellow 255-255-0 ;# #FFFF00 + tcl::dict::set WEB_colour_map_basic olive 128-128-0 ;# #808000 + tcl::dict::set WEB_colour_map_basic lime 0-255-0 ;# #00FF00 + tcl::dict::set WEB_colour_map_basic green 0-128-0 ;# #008000 + tcl::dict::set WEB_colour_map_basic aqua 0-255-255 ;# #00FFFF + tcl::dict::set WEB_colour_map_basic teal 0-128-128 ;# #008080 + tcl::dict::set WEB_colour_map_basic blue 0-0-255 ;# #0000FF + tcl::dict::set WEB_colour_map_basic navy 0-0-128 ;# #000080 + tcl::dict::set WEB_colour_map_basic fuchsia 255-0-255 ;# #FF00FF + tcl::dict::set WEB_colour_map_basic purple 128-0-128 ;# #800080 # -- --- --- #Pink colours 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 + tcl::dict::set WEB_colour_map_pink mediumvioletred 199-21-133 ;# #C71585 + tcl::dict::set WEB_colour_map_pink deeppink 255-20-147 ;# #FF1493 + tcl::dict::set WEB_colour_map_pink palevioletred 219-112-147 ;# #DB7093 + tcl::dict::set WEB_colour_map_pink hotpink 255-105-180 ;# #FF69B4 + tcl::dict::set WEB_colour_map_pink lightpink 255-182-193 ;# #FFB6C1 + tcl::dict::set WEB_colour_map_pink pink 255-192-203 ;# #FFCOCB # -- --- --- #Red colours 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 + tcl::dict::set WEB_colour_map_red darkred 139-0-0 ;# #8B0000 + tcl::dict::set WEB_colour_map_red red 255-0-0 ;# #FF0000 + tcl::dict::set WEB_colour_map_red firebrick 178-34-34 ;# #B22222 + tcl::dict::set WEB_colour_map_red crimson 220-20-60 ;# #DC143C + tcl::dict::set WEB_colour_map_red indianred 205-92-92 ;# #CD5C5C + tcl::dict::set WEB_colour_map_red lightcoral 240-128-128 ;# #F08080 + tcl::dict::set WEB_colour_map_red salmon 250-128-114 ;# #FA8072 + tcl::dict::set WEB_colour_map_red darksalmon 233-150-122 ;# #E9967A + tcl::dict::set WEB_colour_map_red lightsalmon 255-160-122 ;# #FFA07A # -- --- --- #Orange colours 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 + tcl::dict::set WEB_colour_map_orange orangered 255-69-0 ;# #FF4500 + tcl::dict::set WEB_colour_map_orange tomato 255-99-71 ;# #FF6347 + tcl::dict::set WEB_colour_map_orange darkorange 255-140-0 ;# #FF8C00 + tcl::dict::set WEB_colour_map_orange coral 255-127-80 ;# #FF7F50 + tcl::dict::set WEB_colour_map_orange orange 255-165-0 ;# #FFA500 # -- --- --- #Yellow colours 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 + tcl::dict::set WEB_colour_map_yellow darkkhaki 189-183-107 ;# #BDB76B + tcl::dict::set WEB_colour_map_yellow gold 255-215-0 ;# #FFD700 + tcl::dict::set WEB_colour_map_yellow khaki 240-230-140 ;# #F0E68C + tcl::dict::set WEB_colour_map_yellow peachpuff 255-218-185 ;# #FFDAB9 + tcl::dict::set WEB_colour_map_yellow yellow 255-255-0 ;# #FFFF00 + tcl::dict::set WEB_colour_map_yellow palegoldenrod 238-232-170 ;# #EEE8AA + tcl::dict::set WEB_colour_map_yellow moccasin 255-228-181 ;# #FFE4B5 + tcl::dict::set WEB_colour_map_yellow papayawhip 255-239-213 ;# #FFEFD5 + tcl::dict::set WEB_colour_map_yellow lightgoldenrodyeallow 250-250-210 ;# #FAFAD2 + tcl::dict::set WEB_colour_map_yellow lemonchiffon 255-250-205 ;# #FFFACD + tcl::dict::set WEB_colour_map_yellow lightyellow 255-255-224 ;# #FFFFE0 # -- --- --- #Brown colours #maroon as above 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 + tcl::dict::set WEB_colour_map_brown brown 165-42-42 ;# #A52A2A + tcl::dict::set WEB_colour_map_brown saddlebrown 139-69-19 ;# #8B4513 + tcl::dict::set WEB_colour_map_brown sienna 160-82-45 ;# #A0522D + tcl::dict::set WEB_colour_map_brown chocolate 210-105-30 ;# #D2691E + tcl::dict::set WEB_colour_map_brown darkgoldenrod 184-134-11 ;# #B8860B + tcl::dict::set WEB_colour_map_brown peru 205-133-63 ;# #CD853F + tcl::dict::set WEB_colour_map_brown rosybrown 188-143-143 ;# #BC8F8F + tcl::dict::set WEB_colour_map_brown goldenrod 218-165-32 ;# #DAA520 + tcl::dict::set WEB_colour_map_brown sandybrown 244-164-96 ;# #F4A460 + tcl::dict::set WEB_colour_map_brown tan 210-180-140 ;# #D2B48C + tcl::dict::set WEB_colour_map_brown burlywood 222-184-135 ;# #DEB887 + tcl::dict::set WEB_colour_map_brown wheat 245-222-179 ;# #F5DEB3 + tcl::dict::set WEB_colour_map_brown navajowhite 255-222-173 ;# #FFDEAD + tcl::dict::set WEB_colour_map_brown bisque 255-228-196 ;# #FFEfC4 + tcl::dict::set WEB_colour_map_brown blanchedalmond 255-228-196 ;# #FFEfC4 + tcl::dict::set WEB_colour_map_brown cornsilk 255-248-220 ;# #FFF8DC # -- --- --- #Purple, violet, and magenta colours 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-250 ;# #E6E6FA + tcl::dict::set WEB_colour_map_purple indigo 75-0-130 ;# #4B0082 + tcl::dict::set WEB_colour_map_purple purple 128-0-128 ;# #800080 + tcl::dict::set WEB_colour_map_purple darkmagenta 139-0-139 ;# #8B008B + tcl::dict::set WEB_colour_map_purple darkviolet 148-0-211 ;# #9400D3 + tcl::dict::set WEB_colour_map_purple darkslateblue 72-61-139 ;# #9400D3 + tcl::dict::set WEB_colour_map_purple blueviolet 138-43-226 ;# #8A2BE2 + tcl::dict::set WEB_colour_map_purple darkorchid 153-50-204 ;# #9932CC + tcl::dict::set WEB_colour_map_purple fuchsia 255-0-255 ;# #FF00FF + tcl::dict::set WEB_colour_map_purple magenta 255-0-255 ;# #FF00FF - same as fuchsia + tcl::dict::set WEB_colour_map_purple slateblue 106-90-205 ;# #6A5ACD + tcl::dict::set WEB_colour_map_purple mediumslateblue 123-104-238 ;# #7B68EE + tcl::dict::set WEB_colour_map_purple mediumorchid 186-85-211 ;# #BA5503 + tcl::dict::set WEB_colour_map_purple mediumpurple 147-112-219 ;# #9370DB + tcl::dict::set WEB_colour_map_purple orchid 218-112-214 ;# #DA70D6 + tcl::dict::set WEB_colour_map_purple violet 238-130-238 ;# #EE82EE + tcl::dict::set WEB_colour_map_purple plum 221-160-221 ;# #DDA0DD + tcl::dict::set WEB_colour_map_purple thistle 216-191-216 ;# #D88FD8 + tcl::dict::set WEB_colour_map_purple lavender 230-230-250 ;# #E6E6FA # -- --- --- #Blue colours 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 + tcl::dict::set WEB_colour_map_blue midnightblue 25-25-112 ;# #191970 + tcl::dict::set WEB_colour_map_blue navy 0-0-128 ;# #000080 + tcl::dict::set WEB_colour_map_blue darkblue 0-0-139 ;# #00008B + tcl::dict::set WEB_colour_map_blue mediumblue 0-0-205 ;# #0000CD + tcl::dict::set WEB_colour_map_blue blue 0-0-255 ;# #0000FF + tcl::dict::set WEB_colour_map_blue royalblue 65-105-225 ;# #4169E1 + tcl::dict::set WEB_colour_map_blue steelblue 70-130-180 ;# #4682B4 + tcl::dict::set WEB_colour_map_blue dodgerblue 30-144-255 ;# #1E90FF + tcl::dict::set WEB_colour_map_blue deepskyblue 0-191-255 ;# #00BFFF + tcl::dict::set WEB_colour_map_blue cornflowerblue 100-149-237 ;# #6495ED + tcl::dict::set WEB_colour_map_blue skyblue 135-206-235 ;# #87CEEB + tcl::dict::set WEB_colour_map_blue lightskyblue 135-206-250 ;# #87CEFA + tcl::dict::set WEB_colour_map_blue lightsteelblue 176-196-222 ;# #B0C4DE + tcl::dict::set WEB_colour_map_blue lightblue 173-216-230 ;# #ADD8E6 + tcl::dict::set WEB_colour_map_blue powderblue 176-224-230 ;# #B0E0E6 # -- --- --- #Cyan colours #teal as above 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 + tcl::dict::set WEB_colour_map_cyan darkcyan 0-139-139 ;# #008B8B + tcl::dict::set WEB_colour_map_cyan lightseagreen 32-178-170 ;# #20B2AA + tcl::dict::set WEB_colour_map_cyan cadetblue 95-158-160 ;# #5F9EA0 + tcl::dict::set WEB_colour_map_cyan darkturquoise 0-206-209 ;# #00CED1 + tcl::dict::set WEB_colour_map_cyan mediumturquoise 72-209-204 ;# #48D1CC + tcl::dict::set WEB_colour_map_cyan turquoise 64-224-208 ;# #40E0D0 + tcl::dict::set WEB_colour_map_cyan aqua 0-255-255 ;# #00FFFF + tcl::dict::set WEB_colour_map_cyan cyan 0-255-255 ;# #00FFFF - same as aqua + tcl::dict::set WEB_colour_map_cyan aquamarine 127-255-212 ;# #7FFFD4 + tcl::dict::set WEB_colour_map_cyan paleturquoise 175-238-238 ;# #AFEEEE + tcl::dict::set WEB_colour_map_cyan lightcyan 224-255-255 ;# #E0FFFF # -- --- --- #Green colours 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 + tcl::dict::set WEB_colour_map_green darkgreen 0-100-0 ;# #006400 + tcl::dict::set WEB_colour_map_green green 0-128-0 ;# #008000 + tcl::dict::set WEB_colour_map_green darkolivegreen 85-107-47 ;# #55682F + tcl::dict::set WEB_colour_map_green forestgreen 34-139-34 ;# #228B22 + tcl::dict::set WEB_colour_map_green seagreen 46-139-87 ;# #2E8B57 + tcl::dict::set WEB_colour_map_green olive 128-128-0 ;# #808000 + tcl::dict::set WEB_colour_map_green olivedrab 107-142-35 ;# #6B8E23 + tcl::dict::set WEB_colour_map_green mediumseagreen 60-179-113 ;# #3CB371 + tcl::dict::set WEB_colour_map_green limegreen 50-205-50 ;# #32CD32 + tcl::dict::set WEB_colour_map_green lime 0-255-0 ;# #00FF00 + tcl::dict::set WEB_colour_map_green springgreen 0-255-127 ;# #00FF7F + tcl::dict::set WEB_colour_map_green mediumspringgreen 0-250-154 ;# #00FA9A + tcl::dict::set WEB_colour_map_green darkseagreen 143-188-143 ;# #8FBC8F + tcl::dict::set WEB_colour_map_green mediumaquamarine 102-205-170 ;# #66CDAA + tcl::dict::set WEB_colour_map_green yellowgreen 154-205-50 ;# #9ACD32 + tcl::dict::set WEB_colour_map_green lawngreen 124-252-0 ;# #7CFC00 + tcl::dict::set WEB_colour_map_green chartreuse 127-255-0 ;# #7FFF00 + tcl::dict::set WEB_colour_map_green lightgreen 144-238-144 ;# #90EE90 + tcl::dict::set WEB_colour_map_green greenyellow 173-255-47 ;# #ADFF2F + tcl::dict::set WEB_colour_map_green palegreen 152-251-152 ;# #98FB98 # -- --- --- #White colours 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 + tcl::dict::set WEB_colour_map_white mistyrose 255-228-225 ;# #FFE4E1 + tcl::dict::set WEB_colour_map_white antiquewhite 250-235-215 ;# #FAEBD7 + tcl::dict::set WEB_colour_map_white linen 250-240-230 ;# #FAF0E6 + tcl::dict::set WEB_colour_map_white beige 245-245-220 ;# #F5F5DC + tcl::dict::set WEB_colour_map_white whitesmoke 245-245-245 ;# #F5F5F5 + tcl::dict::set WEB_colour_map_white lavenderblush 255-240-245 ;# #FFF0F5 + tcl::dict::set WEB_colour_map_white oldlace 253-245-230 ;# #FDF5E6 + tcl::dict::set WEB_colour_map_white aliceblue 240-248-255 ;# #F0F8FF + tcl::dict::set WEB_colour_map_white seashell 255-245-238 ;# #FFF5EE + tcl::dict::set WEB_colour_map_white ghostwhite 248-248-255 ;# #F8F8FF + tcl::dict::set WEB_colour_map_white honeydew 240-255-240 ;# #F0FFF0 + tcl::dict::set WEB_colour_map_white floralwhite 255-250-240 ;# #FFFAF0 + tcl::dict::set WEB_colour_map_white azure 240-255-255 ;# #F0FFFF + tcl::dict::set WEB_colour_map_white mintcream 245-255-250 ;# #F5FFFA + tcl::dict::set WEB_colour_map_white snow 255-250-250 ;# #FFFAFA + tcl::dict::set WEB_colour_map_white ivory 255-255-240 ;# #FFFFF0 + tcl::dict::set WEB_colour_map_white white 255-255-255 ;# #FFFFFF # -- --- --- #Gray and black colours 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\ + tcl::dict::set WEB_colour_map_gray black 0-0-0 ;# #000000 + tcl::dict::set WEB_colour_map_gray darkslategray 47-79-79 ;# #2F4F4F + tcl::dict::set WEB_colour_map_gray dimgray 105-105-105 ;# #696969 + tcl::dict::set WEB_colour_map_gray slategray 112-128-144 ;# #708090 + tcl::dict::set WEB_colour_map_gray gray 128-128-128 ;# #808080 + tcl::dict::set WEB_colour_map_gray lightslategray 119-136-153 ;# #778899 + tcl::dict::set WEB_colour_map_gray darkgray 169-169-169 ;# #A9A9A9 + tcl::dict::set WEB_colour_map_gray silver 192-192-192 ;# #C0C0C0 + tcl::dict::set WEB_colour_map_gray lightgray 211-211-211 ;# #D3D3D3 + tcl::dict::set WEB_colour_map_gray gainsboro 220-220-220 ;# #DCDCDC + + set WEB_colour_map [tcl::dict::merge\ $WEB_colour_map_basic\ $WEB_colour_map_pink\ $WEB_colour_map_red\ @@ -1081,13 +1066,13 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #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_diff ;#maintain the difference as a separate dict so we can display in a? x11 - dict set X11_colour_map_diff gray 190-190-190 ;# #BEBEBE - dict set X11_colour_map_diff green 0-255-0 ;# #00FF00 - dict set X11_colour_map_diff maroon 176-48-96 ;# #B03060 - dict set X11_colour_map_diff purple 160-32-240 ;# #A020F0 + tcl::dict::set X11_colour_map_diff gray 190-190-190 ;# #BEBEBE + tcl::dict::set X11_colour_map_diff green 0-255-0 ;# #00FF00 + tcl::dict::set X11_colour_map_diff maroon 176-48-96 ;# #B03060 + tcl::dict::set X11_colour_map_diff purple 160-32-240 ;# #A020F0 variable X11_colour_map - set X11_colour_map [dict merge $WEB_colour_map $X11_colour_map_diff] + set X11_colour_map [tcl::dict::merge $WEB_colour_map $X11_colour_map_diff] #Xterm colour names (256 colours) @@ -1369,21 +1354,21 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu grey93\ ] variable TERM_colour_map - set TERM_colour_map [dict create] + set TERM_colour_map [tcl::dict::create] variable TERM_colour_map_reverse - set TERM_colour_map_reverse [dict create] + set TERM_colour_map_reverse [tcl::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 + if {![tcl::dict::exists $TERM_colour_map $cname]} { + tcl::dict::set TERM_colour_map $cname $cidx + tcl::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 + if {![tcl::dict::exists $TERM_colour_map $cname-$suffix]} { + tcl::dict::set TERM_colour_map $cname-$suffix $cidx + tcl::dict::set TERM_colour_map_reverse $cidx $cname-$suffix set did_rename 1 break } @@ -1405,7 +1390,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #dict for {k v} $WEB_colour_map { # set dectriple [split $v -] # set webhex [::join [format %02X%02X%02X {*}$dectriple] ;# e.g 808080, FFFFFF, 000000 - # dict set HEX_colour_map $webhex [join $dectriple {;}] + # tcl::dict::set HEX_colour_map $webhex [join $dectriple {;}] #} proc colour_hex2ansidec {hex6} { return [join [::scan $hex6 %2X%2X%2X] {;}] @@ -1415,11 +1400,11 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu # eg dec-dec-dec <-> #HHHHHH #allow hex to be specified with or without leading # proc colour_hex2dec {hex6} { - set hex6 [string map [list # ""] $hex6] + set hex6 [tcl::string::map {# ""} $hex6] return [join [::scan $hex6 %2X%2X%2X] {-}] } proc colour_dec2hex {decimalcolourstring} { - set dec [string map [list {;} - , -] $decimalcolourstring] + set dec [tcl::string::map [list {;} - , -] $decimalcolourstring] set declist [split $dec -] set hex #[format %02X%02X%02X {*}$declist] } @@ -1434,19 +1419,19 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu foreach {k v} $args { switch -- $k { -bg - -forcecolour { - dict set opts $k $v + tcl::dict::set opts $k $v } default { - error "colourmap1 unrecognised option $k. Known-options: [dict keys $opts] + error "colourmap1 unrecognised option $k. Known-options: [tcl::dict::keys $opts] } } } - if {[dict get $opts -forcecolour]} { + if {[tcl::dict::get $opts -forcecolour]} { set fc "forcecolour" } else { set fc "" } - set bgname [dict get $opts -bg] + set bgname [tcl::dict::get $opts -bg] package require textblock set bg [textblock::block 33 3 "[a+ {*}$fc $bgname] [a]"] @@ -1461,12 +1446,12 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu } proc colourmap2 {args} { set defaults {-forcecolour 0 -bg Web-white} - set opts [dict merge $defaults $args] + set opts [tcl::dict::merge $defaults $args] set fc "" - if {[dict get $opts -forcecolour]} { + if {[tcl::dict::get $opts -forcecolour]} { set fc "forcecolour" } - set bgname [dict get $opts -bg] + set bgname [tcl::dict::get $opts -bg] package require textblock set bg [textblock::block 39 3 "[a+ {*}$fc $bgname] [a]"] @@ -1485,9 +1470,9 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu } proc colourtable_216 {args} { set defaults {-forcecolour 0} - set opts [dict merge $defaults $args] + set opts [tcl::dict::merge $defaults $args] set fc "" - if {[dict get $opts -forcecolour]} { + if {[tcl::dict::get $opts -forcecolour]} { set fc "forcecolour" } package require textblock @@ -1504,7 +1489,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu lappend clist "[a+ {*}$fc {*}$fg Term$i][format %3s $i]" } - set t [textblock::list_as_table 36 $clist -return object] + set t [textblock::list_as_table -columns 36 -return tableobject $clist] $t configure -show_hseps 0 #return [$t print] return $t @@ -1513,9 +1498,9 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #1st 16 colours of 256 - match SGR colours proc colourblock_16 {args} { set defaults {-forcecolour 0} - set opts [dict merge $defaults $args] + set opts [tcl::dict::merge $defaults $args] set fc "" - if {[dict get $opts -forcecolour]} { + if {[tcl::dict::get $opts -forcecolour]} { set fc "forcecolour" } set out "" @@ -1531,9 +1516,9 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu } proc colourtable_16_names {args} { set defaults {-forcecolour 0} - set opts [dict merge $defaults $args] + set opts [tcl::dict::merge $defaults $args] set fc "" - if {[dict get $opts -forcecolour]} { + if {[tcl::dict::get $opts -forcecolour]} { set fc "forcecolour" } variable TERM_colour_map_reverse @@ -1543,7 +1528,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu set t [textblock::class::table new] $t configure -show_seps 0 -show_edge 0 for {set i 0} {$i <=15} {incr i} { - set cname [dict get $TERM_colour_map_reverse $i] ;#use term-$i etc instead of term-$name? + set cname [tcl::dict::get $TERM_colour_map_reverse $i] ;#use term-$i etc instead of term-$name? if {[llength $row]== 8} { lappend rows $row set row [list] @@ -1563,15 +1548,15 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu append out [$t print] $t destroy append out [a] - return [string trimleft $out \n] + return [tcl::string::trimleft $out \n] } #216 colours of 256 proc colourblock_216 {args} { set defaults {-forcecolour 0} - set opts [dict merge $defaults $args] + set opts [tcl::dict::merge $defaults $args] set fc "" - if {[dict get $opts -forcecolour]} { + if {[tcl::dict::get $opts -forcecolour]} { set fc "forcecolour" } set out "" @@ -1590,18 +1575,18 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu append out "$br[a+ {*}$fc {*}$fg Term$i][format %3s $i] " } append out [a] - return [string trimleft $out \n] + return [tcl::string::trimleft $out \n] } #x6 is reasonable from a width (124 screen cols) and colour viewing perspective proc colourtable_216_names {args} { set defaults {-forcecolour 0 -columns 6} - set opts [dict merge $defaults $args] + set opts [tcl::dict::merge $defaults $args] set fc "" - if {[dict get $opts -forcecolour]} { + if {[tcl::dict::get $opts -forcecolour]} { set fc "forcecolour" } - set cols [dict get $opts -columns] + set cols [tcl::dict::get $opts -columns] set out "" #use the reverse lookup dict - the original xterm_names list has duplicates - we want the disambiguated (potentially suffixed) names @@ -1612,7 +1597,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu set t [textblock::class::table new] $t configure -show_seps 0 -show_edge 0 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 [tcl::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]== $cols} { lappend rows $row set row [list] @@ -1633,13 +1618,13 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu append out [$t print] $t destroy append out [a] - return [string trimleft $out \n] + return [tcl::string::trimleft $out \n] } proc colourtable_term_pastel {args} { set defaults {-forcecolour 0} - set opts [dict merge $defaults $args] + set opts [tcl::dict::merge $defaults $args] set fc "" - if {[dict get $opts -forcecolour]} { + if {[tcl::dict::get $opts -forcecolour]} { set fc "forcecolour" } set out "" @@ -1688,9 +1673,9 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu } proc colourtable_term_rainbow {args} { set defaults {-forcecolour 0} - set opts [dict merge $defaults $args] + set opts [tcl::dict::merge $defaults $args] set fc "" - if {[dict get $opts -forcecolour]} { + if {[tcl::dict::get $opts -forcecolour]} { set fc "forcecolour" } set out "" @@ -1756,9 +1741,9 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #24 greys of 256 proc colourblock_24 {args} { set defaults {-forcecolour 0} - set opts [dict merge $defaults $args] + set opts [tcl::dict::merge $defaults $args] set fc "" - if {[dict get $opts -forcecolour]} { + if {[tcl::dict::get $opts -forcecolour]} { set fc "forcecolour" } @@ -1775,9 +1760,9 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu } proc colourtable_24_names {args} { set defaults {-forcecolour 0} - set opts [dict merge $defaults $args] + set opts [tcl::dict::merge $defaults $args] set fc "" - if {[dict get $opts -forcecolour]} { + if {[tcl::dict::get $opts -forcecolour]} { set fc "forcecolour" } @@ -1788,7 +1773,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu set t [textblock::class::table new] $t configure -show_hseps 0 -show_edge 0 for {set i 232} {$i <=255} {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 [tcl::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} { lappend rows $row set row [list] @@ -1805,10 +1790,10 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu append out [$t print] $t destroy append out [a] - return [string trimleft $out \n] + return [tcl::string::trimleft $out \n] } - #set WEB_colour_map [dict merge\ + #set WEB_colour_map [tcl::dict::merge\ # $WEB_colour_map_basic\ # $WEB_colour_map_pink\ # $WEB_colour_map_red\ @@ -1827,18 +1812,18 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu foreach {k v} $args { switch -- $k { -groups - -forcecolour { - dict set opts $k $v + tcl::dict::set opts $k $v } default { - error "colourtable_web unrecognised option '$k'. Known-options: [dict keys $defaults]" + error "colourtable_web unrecognised option '$k'. Known-options: [tcl::dict::keys $defaults]" } } } set fc "" - if {[dict get $opts -forcecolour]} { + if {[tcl::dict::get $opts -forcecolour]} { set fc "forcecolour" } - set groups [dict get $opts -groups] + set groups [tcl::dict::get $opts -groups] #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] @@ -1875,7 +1860,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu 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] { + tcl::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" @@ -1886,14 +1871,14 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu $t configure_row [expr {[$t row_count]-1}] -ansibase [a+ {*}$fc $fg Web-$cname] } $t configure -frametype {} - $t configure_column 0 -headers [list "[string totitle $g] colours"] + $t configure_column 0 -headers [list "[tcl::string::totitle $g] colours"] $t configure_column 0 -header_colspans [list all] $t configure -ansibase_header [a+ {*}$fc web-black Web-white] lappend grouptables [$t print] $t destroy } #set displaytable [textblock::class::table new] - set displaytable [textblock::list_as_table 3 $grouptables -return object] + set displaytable [textblock::list_as_table -columns 3 -return tableobject $grouptables] $displaytable configure -show_header 0 -show_vseps 0 #return $displaytable set result [$displaytable print] @@ -1903,22 +1888,22 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu proc colourtable_x11diff {args} { variable X11_colour_map_diff variable WEB_colour_map - set opts [dict create\ + set opts [tcl::dict::create\ -forcecolour 0\ -return "string"\ ] foreach {k v} $args { switch -- $k { -return - -forcecolour { - dict set opts $k $v + tcl::dict::set opts $k $v } default { - error "colourtable_x11diff unrecognised option '$k'. Known options [dict keys $opts]" + error "colourtable_x11diff unrecognised option '$k'. Known options [tcl::dict::keys $opts]" } } } set fc "" - if {[dict get $opts -forcecolour]} { + if {[tcl::dict::get $opts -forcecolour]} { set fc "forcecolour" } @@ -1927,7 +1912,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu # -- --- --- set t [textblock::class::table new] $t configure -show_edge 0 -show_seps 0 -show_header 1 - dict for {cname cdec} [set X11_colour_map_diff] { + tcl::dict::for {cname cdec} [set X11_colour_map_diff] { $t add_row [list "$cname " "[colour_dec2hex $cdec] " $cdec] set fg "web-white" $t configure_row [expr {[$t row_count]-1}] -ansibase [a+ {*}$fc $fg X11-$cname] @@ -1940,15 +1925,15 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu $t destroy # -- --- --- - set WEB_map_subset [dict create] - dict for {k v} $X11_colour_map_diff { - dict set WEB_map_subset $k [dict get $WEB_colour_map $k] + set WEB_map_subset [tcl::dict::create] + tcl::dict::for {k v} $X11_colour_map_diff { + tcl::dict::set WEB_map_subset $k [tcl::dict::get $WEB_colour_map $k] } # -- --- --- set t [textblock::class::table new] $t configure -show_edge 0 -show_seps 0 -show_header 1 - dict for {cname cdec} [set WEB_map_subset] { + tcl::dict::for {cname cdec} [set WEB_map_subset] { $t add_row [list "$cname " "[colour_dec2hex $cdec] " $cdec] set fg "web-white" $t configure_row [expr {[$t row_count]-1}] -ansibase [a+ {*}$fc $fg Web-$cname] @@ -1961,10 +1946,10 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu $t destroy # -- --- --- - set displaytable [textblock::list_as_table 2 $comparetables -return object] + set displaytable [textblock::list_as_table -columns 2 -return tableobject $comparetables] $displaytable configure -show_header 0 -show_vseps 0 - if {[dict get $opts -return] eq "string"} { + if {[tcl::dict::get $opts -return] eq "string"} { set result [$displaytable print] $displaytable destroy return $result @@ -2006,7 +1991,9 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu append out "[a+ {*}$fc web-white]Standard SGR colours and attributes $RST" \n set settings_applied $SGR_setting_map set strmap [list] - dict for {k v} $SGR_setting_map { + #safe jumptable test + #dict for {k v} $SGR_setting_map {} + tcl::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 " @@ -2021,20 +2008,20 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu } } } - set settings_applied [string trim $SGR_setting_map \n] + set settings_applied [tcl::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 [tcl::string::map $strmap $settings_applied]] \n + append out [textblock::join $indent [tcl::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 "Web-white" set map1 [colourmap1 -bg $bgname -forcecolour $opt_forcecolour] set map1 [overtype::centre -transparent 1 $map1 "[a {*}$fc black $bgname]Standard colours[a]"] set map2 [colourmap2 -bg $bgname -forcecolour $opt_forcecolour] set map2 [overtype::centre -transparent 1 $map2 "[a {*}$fc 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+ {*}$fc 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 -forcecolour $opt_forcecolour]] \n append out "[a+ {*}$fc web-white]24 Greyscale colours[a]" \n @@ -2045,7 +2032,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu append out [textblock::join $indent "Example: \[a+ term-lightsteelblue Term-gold1\]text\[a] -> [a+ {*}$fc term-lightsteelblue Term-gold1]text[a]"] \n append out \n append out "[a+ {*}$fc web-white]16 Million colours[a]" \n - #dict set WEB_colour_map mediumvioletred 199-21-133 ;# #C71585 + #tcl::dict::set WEB_colour_map mediumvioletred 199-21-133 ;# #C71585 append out [textblock::join $indent "Example: \[a+ rgb-199-21-133\]text\[a] -> [a+ {*}$fc rgb-199-21-133]text[a]"] \n append out [textblock::join $indent "Example: \[a+ Rgb#C71585\]text\[a] -> [a+ {*}$fc Rgb#C71585]text[a]"] \n append out [textblock::join $indent "Examine a sequence: a? bold rgb-46-139-87 Rgb#C71585 "] \n @@ -2060,7 +2047,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu append out "[a+ {*}$fc web-white]X11 colours[a] - mostly match Web colours" \n append out [textblock::join $indent "To see differences: a? x11"] \n - if {[info exists ::punk::console::colour_disabled] && $::punk::console::colour_disabled} { + if {[tcl::info::exists ::punk::console::colour_disabled] && $::punk::console::colour_disabled} { append out \n if {$fc ne ""} { append out "[a+ {*}$fc web-white]Colour is currently disabled - returning with colour anyway because 'forcecolour' argument was supplied[a]" \n @@ -2130,13 +2117,13 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu set resultlist [list] foreach i $args { - set f4 [string range $i 0 3] + set f4 [tcl::string::range $i 0 3] set s [a+ {*}$fc $i]sample 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 tail [tcl::string::tolower [tcl::string::trim [tcl::string::range $i 4 end] -]] + if {[tcl::dict::exists $WEB_colour_map $tail]} { + set dec [tcl::dict::get $WEB_colour_map $tail] set hex [colour_dec2hex $dec] set descr "$hex $dec" } else { @@ -2145,17 +2132,17 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu $t add_row [list $i $descr $s [ansistring VIEW $s]] } term - Term - undt { - set tail [string trim [string range $i 4 end] -] - if {[string is integer -strict $tail]} { + set tail [tcl::string::trim [tcl::string::range $i 4 end] -] + if {[tcl::string::is integer -strict $tail]} { if {$tail < 256} { - set descr "[dict get $TERM_colour_map_reverse $tail]" + set descr "[tcl::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] + set tail [tcl::string::tolower $tail] + if {[tcl::dict::exists $TERM_colour_map $tail]} { + set descr [tcl::dict::get $TERM_colour_map $tail] } else { set descr "UNKNOWN colour for term" } @@ -2163,9 +2150,9 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu $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 tail [tcl::string::tolower [tcl::string::trim [tcl::string::range $i 4 end] -]] + if {[tcl::dict::exists $X11_colour_map $tail]} { + set dec [tcl::dict::get $X11_colour_map $tail] set hex [colour_dec2hex $dec] set descr "$hex $dec" } else { @@ -2179,13 +2166,13 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu RGB0 - RGB1 - RGB2 - RGB3 - RGB4 - RGB5 - RGB6 - RGB7 - RGB8 - RGB9 - rgb# - Rgb# - RGB# - und# - und- { - if {[string index $i 3] eq "#"} { - set tail [string range $i 4 end] + if {[tcl::string::index $i 3] eq "#"} { + set tail [tcl::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 tail [tcl::string::trim [tcl::string::range $i 3 end] -] set dec $tail set hex [colour_dec2hex $dec] set info $hex @@ -2226,12 +2213,12 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu } } default { - if {[string is integer -strict $i]} { + if {[tcl::string::is integer -strict $i]} { set rmap [lreverse $SGR_map] - $t add_row [list $i "SGR [dict get $rmap $i]" $s [ansistring VIEW $s]] + $t add_row [list $i "SGR [tcl::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]] + if {[tcl::dict::exists $SGR_map $i]} { + $t add_row [list $i "SGR [tcl::dict::get $SGR_map $i]" $s [ansistring VIEW $s]] } else { $t add_row [list $i UNKNOWN $s [ansistring VIEW $s]] } @@ -2247,7 +2234,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #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]} { + if {[tcl::string::length $merged] < [tcl::string::length $ansi]} { #only refer to redundancies if shorter - merge may reorder - REVIEW set warning "[a+ web-red Web-yellow]REDUNDANCIES FOUND" } else { @@ -2271,7 +2258,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu # On wezterm - we can get cells changing colour as we scroll after a pallette change - so something appears to be caching colours variable sgr_cache - set sgr_cache [dict create] + set sgr_cache [tcl::dict::create] #sgr_cache clear called by punk::console::ansi when set to off proc sgr_cache {{action ""}} { @@ -2280,11 +2267,11 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu error "sgr_cache action '$action' not understood. Valid actions: clear" } if {$action eq "clear"} { - set sgr_cache [dict create] + set sgr_cache [tcl::dict::create] return "sgr_cache cleared" } if {[catch { - set termwidth [dict get [punk::console::get_size] columns] + set termwidth [tcl::dict::get [punk::console::get_size] columns] } errM]} { set termwidth 80 } @@ -2295,8 +2282,8 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu set lines [list] set line "" #todo - terminal width? table? - dict for {key ansi} $sgr_cache { - set thislen [expr {[string length $key]+1}] + tcl::dict::for {key ansi} $sgr_cache { + set thislen [expr {[tcl::string::length $key]+1}] if {$linelen + $thislen >= $termwidth-1} { lappend lines $line set line "$ansi$key$RST " @@ -2306,7 +2293,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu incr linelen $thislen } } - if {[string length $line]} { + if {[tcl::string::length $line]} { lappend lines $line } return [join $lines \n] @@ -2325,8 +2312,8 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #function name part of cache-key because a and a+ return slightly different results (a has leading reset) variable sgr_cache set cache_key a+$args ;#ensure cache_key static - we may remove for example 'forcecolour' from args - but it needs to remain part of cache_key - if {[dict exists $sgr_cache $cache_key]} { - return [dict get $sgr_cache $cache_key] + if {[tcl::dict::exists $sgr_cache $cache_key]} { + return [tcl::dict::get $sgr_cache $cache_key] } #don't disable ansi here. @@ -2337,7 +2324,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu set colour_disabled 0 #whatever function disables or re-enables colour should have made a call to punk::ansi::sgr_cache clear - if {[info exists ::punk::console::colour_disabled] && $::punk::console::colour_disabled} { + if {[tcl::info::exists ::punk::console::colour_disabled] && $::punk::console::colour_disabled} { set colour_disabled 1 } #allow a mechanism to override the colour_disabled terminal preference - for code that is generating colour for something else - don't let no_color ruin everything. @@ -2351,16 +2338,16 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu set t [list] set e [list] ;#extended codes needing to go in own escape sequence foreach i $args { - set f4 [string range $i 0 3] + set f4 [tcl::string::range $i 0 3] switch -- $f4 { web- { #variable WEB_colour_map #upvar ::punk::ansi::WEB_colour_map WEB_colour_map #foreground web colour - set cname [string tolower [string range $i 4 end]] - if {[dict exists $WEB_colour_map $cname]} { - set rgbdash [dict get $WEB_colour_map $cname] - set rgb [string map { - ;} $rgbdash] + set cname [tcl::string::tolower [tcl::string::range $i 4 end]] + if {[tcl::dict::exists $WEB_colour_map $cname]} { + set rgbdash [tcl::dict::get $WEB_colour_map $cname] + set rgb [tcl::string::map { - ;} $rgbdash] lappend t "38;2;$rgb" } else { puts stderr "ansi web colour unmatched: '$i' in call 'a+ $args'" @@ -2370,9 +2357,9 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #variable WEB_colour_map #upvar ::punk::ansi::WEB_colour_map WEB_colour_map #background web colour - set cname [string tolower [string range $i 4 end]] - if {[dict exists $WEB_colour_map $cname]} { - lappend t "48;2;[string map {- ;} [dict get $WEB_colour_map $cname]]" + set cname [tcl::string::tolower [tcl::string::range $i 4 end]] + if {[tcl::dict::exists $WEB_colour_map $cname]} { + lappend t "48;2;[tcl::string::map {- ;} [tcl::dict::get $WEB_colour_map $cname]]" } else { puts stderr "ansi Web colour unmatched: '$i' in call 'a+ $args'" } @@ -2404,7 +2391,9 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu } underextendedoff { #lremove any existing 4:1 etc - set e [struct::set difference $e [list 4:1 4:2 4:3 4:4 4:5]] + #NOTE struct::set result order can differ depending on whether tcl/critcl imp used + #set e [struct::set difference $e [list 4:1 4:2 4:3 4:4 4:5]] + set e [punk::lib::ldiff $e [list 4:1 4:2 4:3 4:4 4:5]] lappend e 4:0 } undersingle { @@ -2510,12 +2499,12 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #variable TERM_colour_map #256 colour foreground by Xterm name or by integer #name is xterm name or colour index from 0 - 255 - set cc [string trim [string tolower [string range $i 4 end]] -] - if {[string is integer -strict $cc] & $cc < 256} { + set cc [tcl::string::trim [tcl::string::tolower [tcl::string::range $i 4 end]] -] + if {[tcl::string::is integer -strict $cc] & $cc < 256} { lappend t "38;5;$cc" } else { - if {[dict exists $TERM_colour_map $cc]} { - lappend t "38;5;[dict get $TERM_colour_map $cc]" + if {[tcl::dict::exists $TERM_colour_map $cc]} { + lappend t "38;5;[tcl::dict::get $TERM_colour_map $cc]" } else { puts stderr "ansi term colour unmatched: '$i' in call 'a+ $args'" } @@ -2524,12 +2513,12 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu Term - TERM { #variable TERM_colour_map #256 colour background by Xterm name or by integer - set cc [string trim [string tolower [string range $i 4 end]] -] - if {[string is integer -strict $cc] && $cc < 256} { + set cc [tcl::string::trim [tcl::string::tolower [tcl::string::range $i 4 end]] -] + if {[tcl::string::is integer -strict $cc] && $cc < 256} { lappend t "48;5;$cc" } else { - if {[dict exists $TERM_colour_map $cc]} { - lappend t "48;5;[dict get $TERM_colour_map $cc]" + if {[tcl::dict::exists $TERM_colour_map $cc]} { + lappend t "48;5;[tcl::dict::get $TERM_colour_map $cc]" } else { puts stderr "ansi Term colour unmatched: '$i' in call 'a+ $args'" } @@ -2538,38 +2527,38 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu rgb- - rgb0 - rgb1 - rgb2 - rgb3 - rgb4 - rgb5 - rgb6 - rgb7 - rgb8 - rgb9 { #decimal rgb foreground #allow variants rgb-xxx-xxx-xxx and rgbxxx-xxx-xxx - set rgbspec [string trim [string range $i 3 end] -] - set rgb [string map [list - {;} , {;}] $rgbspec] + set rgbspec [tcl::string::trim [tcl::string::range $i 3 end] -] + set rgb [tcl::string::map [list - {;} , {;}] $rgbspec] lappend t "38;2;$rgb" } Rgb- - RGB- - Rgb0 - Rgb1 - Rgb2 - Rgb3 - Rgb4 - Rgb5 - Rgb6 - Rgb7 - Rgb8 - Rgb9 - RGB0 - RGB1 - RGB2 - RGB3 - RGB4 - RGB5 - RGB6 - RGB7 - RGB8 - RGB9 { #decimal rgb background - set rgbspec [string trim [string range $i 3 end] -] - set rgb [string map [list - {;} , {;}] $rgbspec] + set rgbspec [tcl::string::trim [tcl::string::range $i 3 end] -] + set rgb [tcl::string::map [list - {;} , {;}] $rgbspec] lappend t "48;2;$rgb" } "rgb#" { #hex rgb foreground - set hex6 [string trim [string range $i 4 end] -] + set hex6 [tcl::string::trim [tcl::string::range $i 4 end] -] set rgb [join [::scan $hex6 %2X%2X%2X] {;}] lappend t "38;2;$rgb" } "Rgb#" - "RGB#" { #hex rgb background - set hex6 [string trim [string range $i 4 end] -] + set hex6 [tcl::string::trim [tcl::string::range $i 4 end] -] set rgb [join [::scan $hex6 %2X%2X%2X] {;}] lappend t "48;2;$rgb" } und- - und0 - und1 - und2 - und3 - und4 - und5 - und6 - und7 - und8 - und9 { #decimal rgb underline #allow variants und-xxx-xxx-xxx and undxxx-xxx-xxx - set rgbspec [string trim [string range $i 3 end] -] - set rgb [string map [list - {:} , {:}] $rgbspec] + set rgbspec [tcl::string::trim [tcl::string::range $i 3 end] -] + set rgb [tcl::string::map [list - {:} , {:}] $rgbspec] lappend e "58:2::$rgb" } "und#" { #hex rgb underline - (e.g kitty, wezterm) - uses colons as separators - set hex6 [string trim [string range $i 4 end] -] + set hex6 [tcl::string::trim [tcl::string::range $i 4 end] -] set rgb [join [::scan $hex6 %2X%2X%2X] {:}] lappend e "58:2::$rgb" } @@ -2577,12 +2566,12 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #variable TERM_colour_map #256 colour underline by Xterm name or by integer #name is xterm name or colour index from 0 - 255 - set cc [string trim [string tolower [string range $i 4 end]] -] - if {[string is integer -strict $cc] & $cc < 256} { + set cc [tcl::string::trim [tcl::string::tolower [tcl::string::range $i 4 end]] -] + if {[tcl::string::is integer -strict $cc] & $cc < 256} { lappend e "58:5:$cc" } else { - if {[dict exists $TERM_colour_map $cc]} { - lappend e "58:5:[dict get $TERM_colour_map $cc]" + if {[tcl::dict::exists $TERM_colour_map $cc]} { + lappend e "58:5:[tcl::dict::get $TERM_colour_map $cc]" } else { puts stderr "ansi term underline colour unmatched: '$i' in call 'a $args'" } @@ -2591,10 +2580,10 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu x11- { variable X11_colour_map #foreground X11 names - set cname [string tolower [string range $i 4 end]] - if {[dict exists $X11_colour_map $cname]} { - set rgbdash [dict get $X11_colour_map $cname] - set rgb [string map [list - {;}] $rgbdash] + set cname [tcl::string::tolower [tcl::string::range $i 4 end]] + if {[tcl::dict::exists $X11_colour_map $cname]} { + set rgbdash [tcl::dict::get $X11_colour_map $cname] + set rgb [tcl::string::map [list - {;}] $rgbdash] lappend t "38;2;$rgb" } else { puts stderr "ansi x11 colour unmatched: '$i' in call 'a+ $args'" @@ -2603,19 +2592,19 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu X11- { variable X11_colour_map #background X11 names - set cname [string tolower [string range $i 4 end]] - if {[dict exists $X11_colour_map $cname]} { - set rgbdash [dict get $X11_colour_map $cname] - set rgb [string map [list - {;}] $rgbdash] + set cname [tcl::string::tolower [tcl::string::range $i 4 end]] + if {[tcl::dict::exists $X11_colour_map $cname]} { + set rgbdash [tcl::dict::get $X11_colour_map $cname] + set rgb [tcl::string::map [list - {;}] $rgbdash] lappend t "48;2;$rgb" } else { puts stderr "ansi X11 colour unmatched: '$i'" } } default { - if {[string is integer -strict $i] || [string first ";" $i] > 0} { + if {[tcl::string::is integer -strict $i] || [tcl::string::first ";" $i] > 0} { lappend t $i - } elseif {[string first : $i] > 0} { + } elseif {[tcl::string::first : $i] > 0} { lappend e $i } else { puts stderr "ansi name unmatched: '$i' in call 'a+ $args' Perhaps missing prefix? e.g web- x11- term- rgb# rgb-" @@ -2664,7 +2653,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu set result "\x1b\[[join $t {;}]m\x1b\[[join $e {;}]m" } } - dict set sgr_cache $cache_key $result + tcl::dict::set sgr_cache $cache_key $result return $result } @@ -2682,8 +2671,8 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #It's important to put the functionname in the cache-key because a and a+ return slightly different results variable sgr_cache set cache_key a_$args - if {[dict exists $sgr_cache $cache_key]} { - return [dict get $sgr_cache $cache_key] + if {[tcl::dict::exists $sgr_cache $cache_key]} { + return [tcl::dict::get $sgr_cache $cache_key] } #don't disable ansi here. @@ -2693,7 +2682,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu set colour_disabled 0 #whatever function disables or re-enables colour should have made a call to punk::ansi::sgr_cache clear - if {[info exists ::punk::console::colour_disabled] && $::punk::console::colour_disabled} { + if {[tcl::info::exists ::punk::console::colour_disabled] && $::punk::console::colour_disabled} { set colour_disabled 1 } #allow a mechanism to override the colour_disabled terminal preference - for code that is generating colour for something else - don't let no_color ruin everything. @@ -2707,16 +2696,16 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu set t [list] set e [list] ;#extended codes will suppress standard SGR colours and attributes if merged in same escape sequence foreach i $args { - set f4 [string range $i 0 3] + set f4 [tcl::string::range $i 0 3] switch -- $f4 { web- { #variable WEB_colour_map #upvar ::punk::ansi::WEB_colour_map WEB_colour_map #foreground web colour - set cname [string tolower [string range $i 4 end]] - if {[dict exists $WEB_colour_map $cname]} { - set rgbdash [dict get $WEB_colour_map $cname] - set rgb [string map { - ;} $rgbdash] + set cname [tcl::string::tolower [tcl::string::range $i 4 end]] + if {[tcl::dict::exists $WEB_colour_map $cname]} { + set rgbdash [tcl::dict::get $WEB_colour_map $cname] + set rgb [tcl::string::map { - ;} $rgbdash] lappend t "38;2;$rgb" } else { puts stderr "ansi web colour unmatched: '$i' in call 'a $args'" @@ -2726,9 +2715,9 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #variable WEB_colour_map #upvar ::punk::ansi::WEB_colour_map WEB_colour_map #background web colour - set cname [string tolower [string range $i 4 end]] - if {[dict exists $WEB_colour_map $cname]} { - lappend t "48;2;[string map {- ;} [dict get $WEB_colour_map $cname]]" + set cname [tcl::string::tolower [tcl::string::range $i 4 end]] + if {[tcl::dict::exists $WEB_colour_map $cname]} { + lappend t "48;2;[tcl::string::map {- ;} [tcl::dict::get $WEB_colour_map $cname]]" } else { puts stderr "ansi Web colour unmatched: '$i' in call 'a $args'" } @@ -2757,7 +2746,9 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu } underextendedoff { #lremove any existing 4:1 etc - set e [struct::set difference $e [list 4:1 4:2 4:3 4:4 4:5]] + #use of struct::set with critcl is fast, but will reorder e (with differences depending on tcl vs critcl) + #set e [struct::set difference $e [list 4:1 4:2 4:3 4:4 4:5]] + set e [punk::lib::ldiff $e [list 4:1 4:2 4:3 4:4 4:5]] lappend e 4:0 } undersingle { @@ -2863,12 +2854,12 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #variable TERM_colour_map #256 colour foreground by Xterm name or by integer #name is xterm name or colour index from 0 - 255 - set cc [string trim [string tolower [string range $i 4 end]] -] - if {[string is integer -strict $cc] & $cc < 256} { + set cc [tcl::string::trim [tcl::string::tolower [tcl::string::range $i 4 end]] -] + if {[tcl::string::is integer -strict $cc] & $cc < 256} { lappend t "38;5;$cc" } else { - if {[dict exists $TERM_colour_map $cc]} { - lappend t "38;5;[dict get $TERM_colour_map $cc]" + if {[tcl::dict::exists $TERM_colour_map $cc]} { + lappend t "38;5;[tcl::dict::get $TERM_colour_map $cc]" } else { puts stderr "ansi term colour unmatched: '$i' in call 'a $args'" } @@ -2877,12 +2868,12 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu Term - TERM { #variable TERM_colour_map #256 colour background by Xterm name or by integer - set cc [string trim [string tolower [string range $i 4 end]] -] - if {[string is integer -strict $cc] && $cc < 256} { + set cc [tcl::string::trim [tcl::string::tolower [tcl::string::range $i 4 end]] -] + if {[tcl::string::is integer -strict $cc] && $cc < 256} { lappend t "48;5;$cc" } else { - if {[dict exists $TERM_colour_map $cc]} { - lappend t "48;5;[dict get $TERM_colour_map $cc]" + if {[tcl::dict::exists $TERM_colour_map $cc]} { + lappend t "48;5;[tcl::dict::get $TERM_colour_map $cc]" } else { puts stderr "ansi Term colour unmatched: '$i' in call 'a $args'" } @@ -2891,38 +2882,38 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu rgb- - rgb0 - rgb1 - rgb2 - rgb3 - rgb4 - rgb5 - rgb6 - rgb7 - rgb8 - rgb9 { #decimal rgb foreground #allow variants rgb-xxx-xxx-xxx and rgbxxx-xxx-xxx - set rgbspec [string trim [string range $i 3 end] -] - set rgb [string map [list - {;} , {;}] $rgbspec] + set rgbspec [tcl::string::trim [tcl::string::range $i 3 end] -] + set rgb [tcl::string::map [list - {;} , {;}] $rgbspec] lappend t "38;2;$rgb" } Rgb- - RGB- - Rgb0 - Rgb1 - Rgb2 - Rgb3 - Rgb4 - Rgb5 - Rgb6 - Rgb7 - Rgb8 - Rgb9 - RGB0 - RGB1 - RGB2 - RGB3 - RGB4 - RGB5 - RGB6 - RGB7 - RGB8 - RGB9 { #decimal rgb background - set rgbspec [string trim [string range $i 3 end] -] - set rgb [string map [list - {;} , {;}] $rgbspec] + set rgbspec [tcl::string::trim [tcl::string::range $i 3 end] -] + set rgb [tcl::string::map [list - {;} , {;}] $rgbspec] lappend t "48;2;$rgb" } "rgb#" { #hex rgb foreground - set hex6 [string trim [string range $i 4 end] -] + set hex6 [tcl::string::trim [tcl::string::range $i 4 end] -] set rgb [join [::scan $hex6 %2X%2X%2X] {;}] lappend t "38;2;$rgb" } "Rgb#" - "RGB#" { #hex rgb background - set hex6 [string trim [string range $i 4 end] -] + set hex6 [tcl::string::trim [tcl::string::range $i 4 end] -] set rgb [join [::scan $hex6 %2X%2X%2X] {;}] lappend t "48;2;$rgb" } und- - und0 - und1 - und2 - und3 - und4 - und5 - und6 - und7 - und8 - und9 { #decimal rgb underline #allow variants und-xxx-xxx-xxx and undxxx-xxx-xxx - set rgbspec [string trim [string range $i 3 end] -] - set rgb [string map [list - {:} , {:}] $rgbspec] + set rgbspec [tcl::string::trim [tcl::string::range $i 3 end] -] + set rgb [tcl::string::map [list - {:} , {:}] $rgbspec] lappend e "58:2::$rgb" } "und#" { #hex rgb underline - (e.g kitty, wezterm) - uses colons as separators - set hex6 [string trim [string range $i 4 end] -] + set hex6 [tcl::string::trim [tcl::string::range $i 4 end] -] set rgb [join [::scan $hex6 %2X%2X%2X] {:}] lappend e "58:2::$rgb" } @@ -2930,12 +2921,12 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #variable TERM_colour_map #256 colour underline by Xterm name or by integer #name is xterm name or colour index from 0 - 255 - set cc [string trim [string tolower [string range $i 4 end]] -] - if {[string is integer -strict $cc] & $cc < 256} { + set cc [tcl::string::trim [tcl::string::tolower [tcl::string::range $i 4 end]] -] + if {[tcl::string::is integer -strict $cc] & $cc < 256} { lappend e "58:5:$cc" } else { - if {[dict exists $TERM_colour_map $cc]} { - lappend e "58:5:[dict get $TERM_colour_map $cc]" + if {[tcl::dict::exists $TERM_colour_map $cc]} { + lappend e "58:5:[tcl::dict::get $TERM_colour_map $cc]" } else { puts stderr "ansi term underline colour unmatched: '$i' in call 'a $args'" } @@ -2944,10 +2935,10 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu x11- { variable X11_colour_map #foreground X11 names - set cname [string tolower [string range $i 4 end]] - if {[dict exists $X11_colour_map $cname]} { - set rgbdash [dict get $X11_colour_map $cname] - set rgb [string map [list - {;}] $rgbdash] + set cname [tcl::string::tolower [tcl::string::range $i 4 end]] + if {[tcl::dict::exists $X11_colour_map $cname]} { + set rgbdash [tcl::dict::get $X11_colour_map $cname] + set rgb [tcl::string::map [list - {;}] $rgbdash] lappend t "38;2;$rgb" } else { puts stderr "ansi x11 colour unmatched: '$i'" @@ -2956,19 +2947,19 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu X11- { variable X11_colour_map #background X11 names - set cname [string tolower [string range $i 4 end]] - if {[dict exists $X11_colour_map $cname]} { - set rgbdash [dict get $X11_colour_map $cname] - set rgb [string map [list - {;}] $rgbdash] + set cname [tcl::string::tolower [tcl::string::range $i 4 end]] + if {[tcl::dict::exists $X11_colour_map $cname]} { + set rgbdash [tcl::dict::get $X11_colour_map $cname] + set rgb [tcl::string::map [list - {;}] $rgbdash] lappend t "48;2;$rgb" } else { puts stderr "ansi X11 colour unmatched: '$i'" } } default { - if {[string is integer -strict $i] || [string first ";" $i] > 0} { + if {[tcl::string::is integer -strict $i] || [tcl::string::first ";" $i] > 0} { lappend t $i - } elseif {[string first : $i] > 0} { + } elseif {[tcl::string::first : $i] > 0} { lappend e $i } else { puts stderr "ansi name unmatched: '$i' in call 'a $args' Perhaps missing prefix? e.g web- x11- term- rgb# rgb-" @@ -3008,7 +2999,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu } else { set result "\x1b\[[join $t {;}]m\x1b\[[join $e {;}]m" } - dict set sgr_cache $cache_key $result + tcl::dict::set sgr_cache $cache_key $result return $result } @@ -3028,7 +3019,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu set res [list] foreach i [split $code ";"] { set ix [lsearch -exact $SGR_map $i] - if {[string is digit -strict $code]} { + if {[tcl::string::is digit -strict $code]} { if {$ix>-1} {lappend res [lindex $SGR_map [incr ix -1]]} } else { #reverse lookup code from name @@ -3380,7 +3371,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #If the raw line is appended to another string without such processing - the backspaces & carriage returns can affect data prior to the start of the string. proc printing_length {line} { #string last faster than string first for long strings anyway - if {[string last \n $line] >= 0} { + if {[tcl::string::last \n $line] >= 0} { error "line_print_length must not contain newline characters" } #what if line has \v (vertical tab) ie more than one logical screen line? @@ -3406,8 +3397,8 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #for this reason - it would seem best to normalize the tabs to spaces prior to performing the backspace calculation - otherwise we won't account for the 'short' tabs it effectivley produces #normalize tabs to an appropriate* width #*todo - handle terminal/context where tabwidth != the default 8 spaces - if {[string last \t $line] >= 0} { - if {[info exists punk::console::tabwidth]} { + if {[tcl::string::last \t $line] >= 0} { + if {[tcl::info::exists punk::console::tabwidth]} { set tw $::punk::console::tabwidth } else { set tw 8 @@ -3420,8 +3411,8 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #This means for example that abc\b has a length of 3. Trailing or leading backslashes have no effect #set bs [format %c 0x08] - #set line [string map [list "\r\b" "\r"] $line] ;#backsp following a \r will have no effect - set line [string trim $line \b] ;#take off at start and tail only + #set line [tcl::string::map [list "\r\b" "\r"] $line] ;#backsp following a \r will have no effect + set line [tcl::string::trim $line \b] ;#take off at start and tail only #counterintuitively "x\b" still shows the x ie length is still one. The backspace just moves the position. There must be a char following \b for it to affect the length. #(we are not interested in the destructive backspace case present in editors,terminals etc - that is a different context) @@ -3489,7 +3480,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu set currPos 0 while { 1 } { - set currPos [string first \t $line $currPos] + set currPos [tcl::string::first \t $line $currPos] if { $currPos == -1 } { # no more tabs break @@ -3498,7 +3489,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu # how far is the next tab position ? set dist [expr {$num - ($currPos % $num)}] # replace '\t' at $currPos with $dist spaces - set line [string replace $line $currPos $currPos $Spaces($dist)] + set line [tcl::string::replace $line $currPos $currPos $Spaces($dist)] # set up for next round (not absolutely necessary but maybe a trifle # more efficient) @@ -3513,7 +3504,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #[list_end] [comment {--- end definitions namespace punk::ansi ---}] } -namespace eval punk::ansi { +tcl::namespace::eval punk::ansi { # -- --- --- --- --- --- @@ -3546,7 +3537,7 @@ namespace eval punk::ansi { set payload [join $hexkeys ";"] return "\u0090+q$payload\u009c" } - namespace eval codetype { + tcl::namespace::eval codetype { #*** !doctools #[subsection {Namespace punk::ansi::codetype}] #[para] API functions for punk::ansi::codetype @@ -3569,9 +3560,9 @@ namespace eval punk::ansi { if {[regexp {\033\[[0-9]*(:?C|D|G)$} $code]} { return 1 } - if {[string is integer -strict $knownline]} { + if {[tcl::string::is integer -strict $knownline]} { #CSI n : m H where row n happens to be current line - review/test - set re [string map [list %n% $knownline] {\x1b\[%n%:[0-9]*H$}] + set re [tcl::string::map [list %n% $knownline] {\x1b\[%n%:[0-9]*H$}] if {[regexp $re $code]} { return 1 } @@ -3607,7 +3598,7 @@ namespace eval punk::ansi { if {[regexp {^\033\[([^m]*)m} $code _match params]} { #must match trailing m to be the type of reset we're looking for set plist [split $params ";"] - if {[string trim [lindex $plist 0] 0] eq ""} { + if {[tcl::string::trim [lindex $plist 0] 0] eq ""} { #e.g \033\[m \033\[0\;...m \033\[0000...m return 1 } else { @@ -3636,50 +3627,50 @@ namespace eval punk::ansi { #This is not order-preserving if non-sgr codes are present as they are tacked on to the end even if they initially were before all SGR codes variable codestate_empty - set codestate_empty [dict create] - dict set codestate_empty rst "" ;#0 (or empty) - dict set codestate_empty intensity "" ;#1 bold, 2 dim, 22 normal - dict set codestate_empty italic "" ;#3 on 23 off - dict set codestate_empty underline "" ;#4 on 24 off + set codestate_empty [tcl::dict::create] + tcl::dict::set codestate_empty rst "" ;#0 (or empty) + tcl::dict::set codestate_empty intensity "" ;#1 bold, 2 dim, 22 normal + tcl::dict::set codestate_empty italic "" ;#3 on 23 off + tcl::dict::set codestate_empty underline "" ;#4 on 24 off #nonstandard/extended 4:0,4:1,4:2,4:3,4:4,4:5 #4:1 single underline and 4:2 double underline deliberately kept separate to standard SGR versions #The extended codes are merged separately allowing fallback SGR to be specified for terminals which don't support extended underlines - dict set codestate_empty underextended "" ;#4:0 for no extended underline 4:1 etc for underline styles - #dict set codestate_empty undersingle "" - #dict set codestate_empty underdouble "" - #dict set codestate_empty undercurly "" - #dict set codestate_empty underdottedn "" - #dict set codestate_empty underdashed "" - - dict set codestate_empty blink "" ;#5 or 6 for slow/fast, 25 for off - dict set codestate_empty reverse "" ;#7 on 27 off - dict set codestate_empty hide "" ;#8 on 28 off - dict set codestate_empty strike "" ;#9 on 29 off - dict set codestate_empty font "" ;#10, 11-19 10 being primary - dict set codestate_empty gothic "" ;#20 - dict set codestate_empty doubleunderline "" ;#21 (standard SGR double as opposed to underdouble) - dict set codestate_empty proportional "" ;#26 - see note below - dict set codestate_empty frame_or_circle "" ;#51,52 on - 54 off (54 off) (not generally used - mintty has repurposed for emoji variation selector) + tcl::dict::set codestate_empty underextended "" ;#4:0 for no extended underline 4:1 etc for underline styles + #tcl::dict::set codestate_empty undersingle "" + #tcl::dict::set codestate_empty underdouble "" + #tcl::dict::set codestate_empty undercurly "" + #tcl::dict::set codestate_empty underdottedn "" + #tcl::dict::set codestate_empty underdashed "" + + tcl::dict::set codestate_empty blink "" ;#5 or 6 for slow/fast, 25 for off + tcl::dict::set codestate_empty reverse "" ;#7 on 27 off + tcl::dict::set codestate_empty hide "" ;#8 on 28 off + tcl::dict::set codestate_empty strike "" ;#9 on 29 off + tcl::dict::set codestate_empty font "" ;#10, 11-19 10 being primary + tcl::dict::set codestate_empty gothic "" ;#20 + tcl::dict::set codestate_empty doubleunderline "" ;#21 (standard SGR double as opposed to underdouble) + tcl::dict::set codestate_empty proportional "" ;#26 - see note below + tcl::dict::set codestate_empty frame_or_circle "" ;#51,52 on - 54 off (54 off) (not generally used - mintty has repurposed for emoji variation selector) #ideogram rarely supported - this implementation untested - each attribute kept separate as they presumably can be applied simultaneously - dict set codestate_empty ideogram_underline "" - dict set codestate_empty ideogram_doubleunderline "" - dict set codestate_empty ideogram_overline "" - dict set codestate_empty ideogram_doubleoverline "" - dict set codestate_empty ideogram_clear "" + tcl::dict::set codestate_empty ideogram_underline "" + tcl::dict::set codestate_empty ideogram_doubleunderline "" + tcl::dict::set codestate_empty ideogram_overline "" + tcl::dict::set codestate_empty ideogram_doubleoverline "" + tcl::dict::set codestate_empty ideogram_clear "" - dict set codestate_empty overline "" ;#53 on 55 off - probably not supported - pass through. Seem to be ok to merge with other SGR even if not supported. - dict set codestate_empty underlinecolour "" ;#58 - same arguments as 256colour and rgb (nonstandard - in Kitty ,VTE,mintty and iTerm2) + tcl::dict::set codestate_empty overline "" ;#53 on 55 off - probably not supported - pass through. Seem to be ok to merge with other SGR even if not supported. + tcl::dict::set codestate_empty underlinecolour "" ;#58 - same arguments as 256colour and rgb (nonstandard - in Kitty ,VTE,mintty and iTerm2) # -- mintty? - dict set codestate_empty superscript "" ;#73 - dict set codestate_empty subscript "" ;#74 - dict set codestate_empty nosupersub "" ;#75 + tcl::dict::set codestate_empty superscript "" ;#73 + tcl::dict::set codestate_empty subscript "" ;#74 + tcl::dict::set codestate_empty nosupersub "" ;#75 # -- - dict set codestate_empty fg "" ;#30-37 + 90-97 - dict set codestate_empty bg "" ;#40-47 + 100-107 + tcl::dict::set codestate_empty fg "" ;#30-37 + 90-97 + tcl::dict::set codestate_empty bg "" ;#40-47 + 100-107 #misnomer should have been sgr_merge_args ? :/ @@ -3700,30 +3691,34 @@ namespace eval punk::ansi { } sgr_merge_singles $allparts {*}$args } + + variable defaultopts_sgr_merge_singles + set defaultopts_sgr_merge_singles [tcl::dict::create\ + -filter_fg 0\ + -filter_bg 0\ + -filter_reset 0\ + ] + #codes *must* already have been split so that one esc per element in codelist #e.g codelist [a+ Yellow Red underline] [a+ blue] [a+ red] is ok #but codelist "[a+ Yellow Red underline][a+ blue]" [a+ red] is not #(use punk::ansi::ta::split_codes_single) proc sgr_merge_singles {codelist args} { variable codestate_empty - set othercodes [list] - - set opts [dict create\ - -filter_fg 0\ - -filter_bg 0\ - -filter_reset 0\ - ] - dict for {k v} $args { + variable defaultopts_sgr_merge_singles + set opts $defaultopts_sgr_merge_singles + foreach {k v} $args { switch -- $k { -filter_fg - -filter_bg - -filter_reset { - dict set opts $k $v + tcl::dict::set opts $k $v } default { - error "sgr_merge unknown option '$k'. Known options [dict keys $opts]" + error "sgr_merge unknown option '$k'. Known options [tcl::dict::keys $opts]" } } } + set othercodes [list] set codestate $codestate_empty set codestate_initial $codestate_empty ;#keep a copy for resets. set did_reset 0 @@ -3741,19 +3736,19 @@ namespace eval punk::ansi { foreach c $codelist { #normalize 8bit to a token of the same length so our string operations on the code are the same and we can maintain a switch statement with literals rather than escapes #.. but preserve original c - #set cnorm [string map [list \x9b {8[} ] $c] - #switch -- [string index $cnorm 1][string index $cnorm end] {} + #set cnorm [tcl::string::map [list \x9b {8[} ] $c] + #switch -- [tcl::string::index $cnorm 1][string index $cnorm end] {} # {[m} - set cnorm [string map [list \x9b 8CSI "\x1b\[" 7CSI ] $c] - switch -- [string range $cnorm 0 3][string index $cnorm end] { + set cnorm [tcl::string::map [list \x9b 8CSI "\x1b\[" 7CSI ] $c] + switch -- [tcl::string::range $cnorm 0 3][tcl::string::index $cnorm end] { 7CSIm - 8CSIm { - #set params [string range $cnorm 2 end-1] ;#strip leading esc lb and trailing m - set params [string range $cnorm 4 end-1] ;#string leading XCSI and trailing m + #set params [tcl::string::range $cnorm 2 end-1] ;#strip leading esc lb and trailing m + set params [tcl::string::range $cnorm 4 end-1] ;#string leading XCSI and trailing m #some systems use colon for 256 colours or RGB or nonstandard subparameters #- it is therefore probably not ok to map to semicolon within SGR codes and treat the same. - # - will break mintty? set params [string map [list : {;}] $params] + # - will break mintty? set params [tcl::string::map [list : {;}] $params] set plist [split $params {;}] if {![llength $plist]} { #if there was nothing - it must be a reset - we need it in the list @@ -3772,10 +3767,10 @@ namespace eval punk::ansi { #review - what about \x1b\[000m #we need to accept/ignore leading zeros - we can't just pass to expr - as some tcl versions still see leading zero as octal - set codeint [string trimleft [lindex $paramsplit 0] 0] + set codeint [tcl::string::trimleft [lindex $paramsplit 0] 0] switch -- $codeint { "" - 0 { - if {![dict get $opts -filter_reset]} { + if {![tcl::dict::get $opts -filter_reset]} { set codestate $codestate_initial set did_reset 1 } @@ -3783,117 +3778,117 @@ namespace eval punk::ansi { 1 { #bold if {[llength $paramsplit] == 1} { - dict set codestate intensity $p + tcl::dict::set codestate intensity $p } else { if {[lindex $paramsplit 1] eq "2"} { - dict set codestate shadowed "1:2" ;#turn off also with 22 + tcl::dict::set codestate shadowed "1:2" ;#turn off also with 22 } } } 2 { #dim - dict set codestate intensity 2 + tcl::dict::set codestate intensity 2 } 3 { - dict set codestate italic 3 + tcl::dict::set codestate italic 3 } 4 { #REVIEW - merging extended (e.g 4:4) underline attributes suppresses all other SGR attributes on at least some terminals which don't support extended underlines if {[llength $paramsplit] == 1} { - dict set codestate underline 4 + tcl::dict::set codestate underline 4 } else { switch -- [lindex $paramsplit 1] { 0 { #no *extended* underline - #dict set codestate underline 24 - dict set codestate underextended 4:0 ;#will not turn off SGR standard underline if term doesn't support extended + #tcl::dict::set codestate underline 24 + tcl::dict::set codestate underextended 4:0 ;#will not turn off SGR standard underline if term doesn't support extended } 1 { - dict set codestate underextended 4:1 + tcl::dict::set codestate underextended 4:1 } 2 { - dict set codestate underextended 4:2 + tcl::dict::set codestate underextended 4:2 } 3 { - dict set codestate underextended "4:3" + tcl::dict::set codestate underextended "4:3" } 4 { - dict set codestate underextended "4:4" + tcl::dict::set codestate underextended "4:4" } 5 { - dict set codestate underextended "4:5" + tcl::dict::set codestate underextended "4:5" } } } } 5 - 6 { - dict set codestate blink $p + tcl::dict::set codestate blink $p } 7 { - dict set codestate reverse 7 + tcl::dict::set codestate reverse 7 } 8 { - dict set codestate hide 8 + tcl::dict::set codestate hide 8 } 9 { - dict set codestate strike 9 + tcl::dict::set codestate strike 9 } 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 { - dict set codestate font $p + tcl::dict::set codestate font $p } 20 { - dict set codestate gothic 20 + tcl::dict::set codestate gothic 20 } 21 { #ECMA-48 double underline - some terminals use as not-bold. For now we won't support that. - dict set codestate doubleunderline 21 + tcl::dict::set codestate doubleunderline 21 } 22 { #normal intensity - dict set codestate intensity 22 - dict set codestate shadowed "" + tcl::dict::set codestate intensity 22 + tcl::dict::set codestate shadowed "" } 23 { #? wikipedia mentions blackletter - review - dict set codestate italic 23 + tcl::dict::set codestate italic 23 } 24 { - dict set codestate underline 24 ;#off - dict set codestate underextended "4:0" ;#review + tcl::dict::set codestate underline 24 ;#off + tcl::dict::set codestate underextended "4:0" ;#review } 25 { - dict set codestate blink 25 ;#off + tcl::dict::set codestate blink 25 ;#off } 26 { #not known to be used in terminals.. could it be used with elastic tabstops? - review - dict set codestate proportional 26 + tcl::dict::set codestate proportional 26 } 27 { - dict set codestate reverse 27 ;#off + tcl::dict::set codestate reverse 27 ;#off } 28 { - dict set codestate hide 28 ;#reveal + tcl::dict::set codestate hide 28 ;#reveal } 29 { - dict set codestate strike 29;#off + tcl::dict::set codestate strike 29;#off } 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 { - dict set codestate fg $p ;#foreground colour + tcl::dict::set codestate fg $p ;#foreground colour } 38 { #256 colour or rgb #check if subparams supplied as colon separated - if {[string first : $p] < 0} { + if {[tcl::string::first : $p] < 0} { switch -- [lindex $plist $i+1] { 5 { #256 - 1 more param - dict set codestate fg "38\;5\;[lindex $plist $i+2]" + tcl::dict::set codestate fg "38\;5\;[lindex $plist $i+2]" incr i 2 } 2 { #rgb - dict set codestate fg "38\;2\;[lindex $plist $i+2]\;[lindex $plist $i+3]\;[lindex $plist $i+4]" + tcl::dict::set codestate fg "38\;2\;[lindex $plist $i+2]\;[lindex $plist $i+3]\;[lindex $plist $i+4]" incr i 4 } } @@ -3901,124 +3896,124 @@ namespace eval punk::ansi { #apparently subparameters can be left empty - and there are other subparams like transparency and colour-space #we should only need to pass it all through for the terminal to understand #review - dict set codestate fg $p + tcl::dict::set codestate fg $p } } 39 { - dict set codestate fg 39 ;#default foreground + tcl::dict::set codestate fg 39 ;#default foreground } 40 - 41 - 42 - 43 - 44 - 45 - 46 - 47 { - dict set codestate bg $p ;#background colour + tcl::dict::set codestate bg $p ;#background colour } 48 { #256 colour or rgb - if {[string first : $p] < 0} { + if {[tcl::string::first : $p] < 0} { switch -- [lindex $plist $i+1] { 5 { #256 - 1 more param - dict set codestate bg "48\;5\;[lindex $plist $i+2]" + tcl::dict::set codestate bg "48\;5\;[lindex $plist $i+2]" incr i 2 } 2 { #rgb - dict set codestate bg "48\;2\;[lindex $plist $i+2]\;[lindex $plist $i+3]\;[lindex $plist $i+4]" + tcl::dict::set codestate bg "48\;2\;[lindex $plist $i+2]\;[lindex $plist $i+3]\;[lindex $plist $i+4]" incr i 4 } } } else { - dict set codestate bg $p + tcl::dict::set codestate bg $p } } 49 { - dict set codestate bg 49 ;#default background + tcl::dict::set codestate bg 49 ;#default background } 50 { - dict set codestate proportional 50 ;#off - see 26 + tcl::dict::set codestate proportional 50 ;#off - see 26 } 51 - 52 { - dict set codestate frame_or_circle 51 + tcl::dict::set codestate frame_or_circle 51 } 53 { - dict set codestate overline 53 ;#not supported in terminals? pass through anyway + tcl::dict::set codestate overline 53 ;#not supported in terminals? pass through anyway } 54 { - dict set codestate frame_or_circle 54 ;#off + tcl::dict::set codestate frame_or_circle 54 ;#off } 55 { - dict set codestate overline 55; #off + tcl::dict::set codestate overline 55; #off } 58 { #nonstandard #256 colour or rgb - if {[string first : $p] < 0} { + if {[tcl::string::first : $p] < 0} { switch -- [lindex $plist $i+1] { 5 { #256 - 1 more param - dict set codestate underlinecolour "58\;5\;[lindex $plist $i+2]" + tcl::dict::set codestate underlinecolour "58\;5\;[lindex $plist $i+2]" incr i 2 } 2 { #rgb - dict set codestate underlinecolour "58\;2\;[lindex $plist $i+2]\;[lindex $plist $i+3]\;[lindex $plist $i+4]" + tcl::dict::set codestate underlinecolour "58\;2\;[lindex $plist $i+2]\;[lindex $plist $i+3]\;[lindex $plist $i+4]" incr i 4 } } } else { - dict set codestate underlinecolour $p + tcl::dict::set codestate underlinecolour $p } } 59 { #nonstandard - default underlinecolour - dict set codestate underlinecolour 59 + tcl::dict::set codestate underlinecolour 59 } 60 { - dict set codestate ideogram_underline 60 - dict set codestate ideogram_clear "" + tcl::dict::set codestate ideogram_underline 60 + tcl::dict::set codestate ideogram_clear "" } 61 { - dict set codestate ideogram_doubleunderline 61 - dict set codestate ideogram_clear "" + tcl::dict::set codestate ideogram_doubleunderline 61 + tcl::dict::set codestate ideogram_clear "" } 62 { - dict set codestate ideogram_overline 62 - dict set codestate ideogram_clear "" + tcl::dict::set codestate ideogram_overline 62 + tcl::dict::set codestate ideogram_clear "" } 63 { - dict set codestate ideogram_doubleoverline 63 - dict set codestate ideogram_clear "" + tcl::dict::set codestate ideogram_doubleoverline 63 + tcl::dict::set codestate ideogram_clear "" } 64 { - dict set codestate ideogram_stress 64 - dict set codestate ideogram_clear "" + tcl::dict::set codestate ideogram_stress 64 + tcl::dict::set codestate ideogram_clear "" } 65 { - dict set codestate ideogram_clear 65 + tcl::dict::set codestate ideogram_clear 65 #review - we still need to pass through the ideogram_clear in case something understands it - dict set codestate ideogram_underline "" - dict set codestate ideogram_doubleunderline "" - dict set codestate ideogram_overline "" - dict set codestate ideogram_doubleoverline "" + tcl::dict::set codestate ideogram_underline "" + tcl::dict::set codestate ideogram_doubleunderline "" + tcl::dict::set codestate ideogram_overline "" + tcl::dict::set codestate ideogram_doubleoverline "" } 73 { #mintty only? #can be combined with subscript - dict set codestate superscript 73 - dict set codestate nosupersub "" + tcl::dict::set codestate superscript 73 + tcl::dict::set codestate nosupersub "" } 74 { - dict set codestate subscript 74 - dict set codestate nosupersub "" + tcl::dict::set codestate subscript 74 + tcl::dict::set codestate nosupersub "" } 75 { - dict set codestate nosupersub 75 - dict set codestate superscript "" - dict set codestate subcript "" + tcl::dict::set codestate nosupersub 75 + tcl::dict::set codestate superscript "" + tcl::dict::set codestate subcript "" } 90 - 91 - 92 - 93 - 94 - 95 - 96 - 97 { - dict set codestate fg $p + tcl::dict::set codestate fg $p } 100 - 101 - 102 - 103 - 104 - 105 - 106 - 107 { - dict set codestate bg $p + tcl::dict::set codestate bg $p } } @@ -4033,20 +4028,22 @@ namespace eval punk::ansi { set codemerge "" set unmergeable "" ;# can merge with each other but not main set (for terminals not supporting extended codes) - if {[dict get $opts -filter_fg] || [dict get $opts -filter_bg]} { - dict for {k v} $codestate { + if {[tcl::dict::get $opts -filter_fg] || [tcl::dict::get $opts -filter_bg]} { + #safe jumptable test + #dict for {k v} $codestate {} + tcl::dict::for {k v} $codestate { switch -- $v { "" { } default { switch -- $k { bg { - if {![dict get $opts -filter_bg]} { + if {![tcl::dict::get $opts -filter_bg]} { append codemerge "${v}\;" } } fg { - if {![dict get $opts -filter_fg]} { + if {![tcl::dict::get $opts -filter_fg]} { append codemerge "${v}\;" } } @@ -4061,7 +4058,9 @@ namespace eval punk::ansi { } } } else { - dict for {k v} $codestate { + #safe jumptable test + #dict for {k v} $codestate {} + tcl::dict::for {k v} $codestate { switch -- $v { "" {} default { @@ -4086,9 +4085,9 @@ namespace eval punk::ansi { } #puts "+==> codelist:[ansistring VIEW $codelist] did_reset:$did_reset codemerge:[ansistring VIEW $codemerge] unmergeable:[ansistring VIEW $unmergeable]" if {$codemerge ne ""} { - set codemerge [string trimright $codemerge {;}] + set codemerge [tcl::string::trimright $codemerge {;}] if {$unmergeable ne ""} { - set unmergeable [string trimright $unmergeable {;}] + set unmergeable [tcl::string::trimright $unmergeable {;}] return "\x1b\[${codemerge}m\x1b\[${unmergeable}m[join $othercodes ""]" } else { return "\x1b\[${codemerge}m[join $othercodes ""]" @@ -4098,7 +4097,7 @@ namespace eval punk::ansi { #there were no SGR codes - not even resets return [join $othercodes ""] } else { - set unmergeable [string trimright $unmergeable {;}] + set unmergeable [tcl::string::trimright $unmergeable {;}] return "\x1b\[${unmergeable}m[join $othercodes ""]" } } @@ -4109,7 +4108,7 @@ namespace eval punk::ansi { #*** !doctools #[list_end] [comment {--- end definitions namespace punk::ansi::codetype ---}] } - namespace eval sequence_type { + tcl::namespace::eval sequence_type { proc is_Fe {code} { # C1 control codes if {[regexp {^\033\[[\u0040-\u005F]}]} { @@ -4134,14 +4133,14 @@ namespace eval punk::ansi { } -namespace eval punk::ansi::ta { +tcl::namespace::eval punk::ansi::ta { #*** !doctools #[subsection {Namespace punk::ansi::ta}] #[para] text ansi functions #[para] based on but not identical to the Perl Text Ansi module: #[para] https://github.com/perlancar/perl-Text-ANSI-Util/blob/master/lib/Text/ANSI/BaseUtil.pm #[list_begin definitions] - namespace path ::punk::ansi + tcl::namespace::path ::punk::ansi #handle both 7-bit and 8-bit csi #review - does codepage affect this? e.g ebcdic has 8bit csi in different position @@ -4174,9 +4173,9 @@ namespace eval punk::ansi::ta { variable re_standalones {(?:\x1bc|\x1b7|\x1b8|\x1bM|\x1bE|\x1bD|\x1bD|\x1bH|\x1b=|\x1b>|\x1b#3|\x1b#4|\x1b#5|\x1b#6|\x1b#8)} #if we don't split on altgraphics too and separate them out - it's easy to get into a horrible mess - variable re_altg0_group {(?:\x1b\(0)(?:(?!\x1b\(B).)*\x1b\(B} - variable re_altg0_open {(?:\x1b\(0)} - variable re_altg0_close {(?:\x1b\(B)} + variable re_g0_group {(?:\x1b\(0)(?:(?!\x1b\(B).)*\x1b\(B} + variable re_g0_open {(?:\x1b\(0)} + variable re_g0_close {(?:\x1b\(B)} # DCS "ESC P" or "0x90" is also terminated by ST set re_ST_open {(?:\033P|\u0090|\033X|\u0098|\033\^|\u009e|\033_|\u009f)} @@ -4198,7 +4197,7 @@ namespace eval punk::ansi::ta { #default for regexes is non-newline-sensitive matching - ie matches can span lines # -- --- --- --- - variable re_ansi_detect1 "${re_csi_code}|${re_esc_osc1}|${re_esc_osc2}|${re_esc_osc3}|${re_standalones}|${re_ST}|${re_altg0_open}|${re_altg0_close}" + variable re_ansi_detect1 "${re_csi_code}|${re_esc_osc1}|${re_esc_osc2}|${re_esc_osc3}|${re_standalones}|${re_ST}|${re_g0_open}|${re_g0_close}" # -- --- --- --- #handrafted TRIE version of above. Somewhat difficult to construct and maintain. TODO - find a regext TRIE generator that works with Tcl regexes #This does make things quicker - but it's too early to finalise the detect/split regexes (e.g missing \U0090 ) - will need to be redone. @@ -4207,28 +4206,47 @@ namespace eval punk::ansi::ta { - variable re_ansi_detect_open "${re_csi_open}|${re_osc_open}|${re_standalones}|${re_ST_open}|${re_altg0_open}" + variable re_ansi_detect_open "${re_csi_open}|${re_osc_open}|${re_standalones}|${re_ST_open}|${re_g0_open}" #may be same as detect - kept in case detect needs to diverge - #variable re_ansi_split "${re_csi_code}|${re_esc_osc1}|${re_esc_osc2}|${re_esc_osc3}|${re_standalones}|${re_ST}|${re_altg0_open}|${re_altg0_close}" + #variable re_ansi_split "${re_csi_code}|${re_esc_osc1}|${re_esc_osc2}|${re_esc_osc3}|${re_standalones}|${re_ST}|${re_g0_open}|${re_g0_close}" set re_ansi_split $re_ansi_detect #detect any ansi escapes #review - only detect 'complete' codes - or just use the opening escapes for performance? - proc detect {text} { + proc detect {text} [string map [list [list $re_ansi_detect]] { #*** !doctools #[call [fun detect] [arg text]] #[para]Return a boolean indicating whether Ansi codes were detected in text - #[para] - - variable re_ansi_detect - expr {[regexp $re_ansi_detect $text]} + #[para]Important caveat: + #[para] When text is a tcl list made from splitting (or lappending) some ansi string - individual elements may be braced or have certain chars escaped. (one example is if a list element contains an unbalanced brace) + #[para] This can cause square brackets that form part of the ansi being backslash escaped - and the regexp can fail to match + regexp $text + }] + + #can be used on dicts - but will check keys too. keys could also contain ansi and have escapes + proc detect_in_list {list} { + detect [join $list " "] + } + proc detect_in_list2 {list} { + foreach item $list { + if {[detect $item]} { + return 1 + } + } + return 0 } + proc detect_g0 {text} [string map [list [list $re_g0_group]] { + regexp $text + }] + #note - micro optimisation of inlining gives us *almost* nothing extra. + #left in place for a few such as detect/detect_g0 as we want them as fast as possible + # in general the technique doesn't seem particularly worthwhile for this set of functions. + #the performance is dominated by the complexity of the regexp proc detect2 {text} { - variable re_ansi_detect2 - expr {[regexp $re_ansi_detect2 $text]} + variable re_ansi_detect + expr {[regexp $re_ansi_detect $text]} } - proc detect_open {text} { variable re_ansi_detect_open @@ -4270,7 +4288,9 @@ namespace eval punk::ansi::ta { #*** !doctools #[call [fun length] [arg text]] #[para]Return the character length after stripping ansi codes - not the printing length - string length [stripansi $text] + + #we can use stripansiraw to avoid g0 conversion - as the length should remain the same + tcl::string::length [stripansiraw $text] } #todo - handle newlines #not in perl ta @@ -4284,10 +4304,92 @@ namespace eval punk::ansi::ta { #not in perl ta #returns just the plaintext portions in a list - proc split_at_codes {text} { - variable re_ansi_split - punk::ansi::internal::splitx $text ${re_ansi_split} + proc split_at_codes {str} [string map [list $re_ansi_split] { + #variable re_ansi_split + #punk::ansi::internal::splitx $str ${re_ansi_split} + punk::ansi::ta::Do_split_at_codes $str {} + }] + #it is faster to split this function out than to inline it into split_at_codes in tcl 8.7 - something to do with the use of the variable vs argument for the regexp + #literal inlining of the re in the main proc-body was slower too - but inlining it into the wrapper seems to work (a tiny bit) + #the difference is not often apparent when comparing timerate results from split_at_codes vs split_at_codes2 - + # - but in aggregate for something like textblock::periodic - we can get a bit over 5% faster (e.g 136ms vs 149ms) + proc Do_split_at_codes {str regexp} { + if {$str eq ""} { + return {} + } + #no infinite loop check on regexp like splitx does because we should have tested re_ansi_split during development + set list {} + set start 0 + while {[regexp -start $start -indices -- $regexp $str match submatch]} { + lassign $submatch subStart subEnd + lassign $match matchStart matchEnd + incr matchStart -1 + incr matchEnd + lappend list [tcl::string::range $str $start $matchStart] + if {$subStart >= $start} { + lappend list [tcl::string::range $str $subStart $subEnd] + } + set start $matchEnd + } + lappend list [tcl::string::range $str $start end] + return $list + + } + proc Do_split_at_codes_join {str regexp} { + if {$str eq ""} { + return {} + } + #no infinite loop check on regexp like splitx does because we should have tested re_ansi_split during development + set list {} + set start 0 + while {[regexp -start $start -indices -- $regexp $str match submatch]} { + lassign $submatch subStart subEnd + lassign $match matchStart matchEnd + incr matchStart -1 + incr matchEnd + lappend list [tcl::string::range $str $start $matchStart] + if {$subStart >= $start} { + lappend list [tcl::string::range $str $subStart $subEnd] + } + set start $matchEnd + } + lappend list [tcl::string::range $str $start end] + return [join $list ""] } + proc split_at_codes2 {str} [string map [list $re_ansi_split] { + #variable re_ansi_split + #punk::ansi::internal::splitx $str ${re_ansi_split} + + #set regexp $re_ansi_split + #set regexp {} + + #inline splitx to avoid regex checks + #from textutil::split::splitx + # Bugfix 476988 + if {$str eq ""} { + return {} + } + #if {[regexp $regexp {}]} { + # return -code error \ + # "splitting on regexp \"$re_ansi_split\" would cause infinite loop" + #} + #no infinite loop check on regexp like splitx does because we should have tested re_ansi_split during development + set list {} + set start 0 + while {[regexp -start $start -indices -- {} $str match submatch]} { + lassign $submatch subStart subEnd + lassign $match matchStart matchEnd + incr matchStart -1 + incr matchEnd + lappend list [tcl::string::range $str $start $matchStart] + if {$subStart >= $start} { + lappend list [tcl::string::range $str $subStart $subEnd] + } + set start $matchEnd + } + lappend list [tcl::string::range $str $start end] + return $list + }] # -- --- --- --- --- --- #Split $text to a list containing alternating ANSI colour codes and text. @@ -4307,7 +4409,10 @@ namespace eval punk::ansi::ta { set re "(?:${re_ansi_split})+" return [_perlish_split $re $text] } + #micro optimisations on split_codes to avoid function calls and make re var local tend to yield very little benefit (sub uS diff on calls that commonly take 10s/100s of uSeconds) + #like split_codes - but each ansi-escape is split out separately (with empty string of plaintext between codes so odd/even plain ansi still holds) + #- the slightly simpler regex than split_codes means that it will be slightly faster than keeping the codes grouped. proc split_codes_single {text} { variable re_ansi_split return [_perlish_split $re_ansi_split $text] @@ -4315,7 +4420,7 @@ namespace eval punk::ansi::ta { #review - tcl greedy expressions may match multiple in one element proc _perlish_split {re text} { - if {[string length $text] == 0} { + if {$text eq ""} { return {} } set list [list] @@ -4326,26 +4431,26 @@ namespace eval punk::ansi::ta { lassign $match matchStart matchEnd #puts "->start $start ->match $matchStart $matchEnd" if {$matchEnd < $matchStart} { - lappend list [string range $text $start $matchStart-1] [string index $text $matchStart] + lappend list [tcl::string::range $text $start $matchStart-1] [tcl::string::index $text $matchStart] incr start - if {$start >= [string length $text]} { + if {$start >= [tcl::string::length $text]} { break } continue } - lappend list [string range $text $start $matchStart-1] [string range $text $matchStart $matchEnd] + lappend list [tcl::string::range $text $start $matchStart-1] [tcl::string::range $text $matchStart $matchEnd] set start [expr {$matchEnd+1}] #? - if {$start >= [string length $text]} { + if {$start >= [tcl::string::length $text]} { break } } - return [lappend list [string range $text $start end]] + return [lappend list [tcl::string::range $text $start end]] } #experiment for coroutine generator proc _perlish_split_yield {re text} { - if {[string length $text] == 0} { + if {[tcl::string::length $text] == 0} { yield {} } set list [list] @@ -4356,27 +4461,27 @@ namespace eval punk::ansi::ta { lassign $match matchStart matchEnd #puts "->start $start ->match $matchStart $matchEnd" if {$matchEnd < $matchStart} { - yield [string range $text $start $matchStart-1] - yield [string index $text $matchStart] + yield [tcl::string::range $text $start $matchStart-1] + yield [tcl::string::index $text $matchStart] incr start - if {$start >= [string length $text]} { + if {$start >= [tcl::string::length $text]} { break } continue } - yield [string range $text $start $matchStart-1] - yield [string range $text $matchStart $matchEnd] + yield [tcl::string::range $text $start $matchStart-1] + yield [tcl::string::range $text $matchStart $matchEnd] set start [expr {$matchEnd+1}] #? - if {$start >= [string length $text]} { + if {$start >= [tcl::string::length $text]} { break } } - #return [lappend list [string range $text $start end]] - yield [string range $text $start end] + #return [lappend list [tcl::string::range $text $start end]] + yield [tcl::string::range $text $start end] } proc _perlish_split2 {re text} { - if {[string length $text] == 0} { + if {[tcl::string::length $text] == 0} { return {} } set list [list] @@ -4386,17 +4491,17 @@ namespace eval punk::ansi::ta { lassign $match matchStart matchEnd #puts "->start $start ->match $matchStart $matchEnd" if {$matchEnd < $matchStart} { - lappend list [string range $text $start $matchStart-1] [string index $text $matchStart] + lappend list [tcl::string::range $text $start $matchStart-1] [tcl::string::index $text $matchStart] incr start } else { - lappend list [string range $text $start $matchStart-1] [string range $text $matchStart $matchEnd] + lappend list [tcl::string::range $text $start $matchStart-1] [tcl::string::range $text $matchStart $matchEnd] set start [expr {$matchEnd+1}] } - if {$start >= [string length $text]} { + if {$start >= [tcl::string::length $text]} { break } } - return [lappend list [string range $text $start end]] + return [lappend list [tcl::string::range $text $start end]] } proc _ws_split {text} { regexp -all -inline {(?:\S+)|(?:\s+)} $text @@ -4407,15 +4512,15 @@ namespace eval punk::ansi::ta { #[list_end] [comment {--- end definitions namespace punk::ansi::ta ---}] } # -- --- --- --- --- --- --- --- --- --- --- -namespace eval punk::ansi::class { +tcl::namespace::eval punk::ansi::class { #assertions specifically for punk::ansi::class namespace - if {![llength [info commands ::punk::ansi::class::assert]]} { - namespace import ::punk::assertion::assert + if {![llength [tcl::info::commands ::punk::ansi::class::assert]]} { + tcl::namespace::import ::punk::assertion::assert punk::assertion::active 1 } - namespace eval renderer { - if {[llength [info commands ::punk::ansi::class::renderer::base_renderer]]} { + tcl::namespace::eval renderer { + if {[llength [tcl::info::commands ::punk::ansi::class::renderer::base_renderer]]} { #Can happen if package forget was used and we're reloading (a possibly different version) ? review ::punk::ansi::class::renderer::base_renderer destroy ;#will automatically destroy other classes such as class_cp437 that use this as a superclass } @@ -4433,17 +4538,17 @@ namespace eval punk::ansi::class { #-- make assert available -- # By pointing it to the assert imported into ::punk::ansi::class # (we could alternatively import assert *directly* from ::punk::assertion::assert - but we can't chain imports as setting active flag renames the command, breaking chained imports) - set nspath [namespace path] + set nspath [tcl::namespace::path] if {"::punk::ansi::class" ni $nspath} { lappend nspath ::punk::ansi::class } - namespace path $nspath + tcl::namespace::path $nspath #-- -- if {[llength $args] < 2} { error {usage: ?-width ? ?-wrap [1|0]? ?-overflow [1|0]? from_ansistring to_ansistring} } lassign [lrange $args end-1 end] from_ansistring to_ansistring - set opts [dict create\ + set opts [tcl::dict::create\ -width \uFFEF\ -wrap 1\ -overflow 0\ @@ -4458,21 +4563,21 @@ namespace eval punk::ansi::class { foreach {k v} $argsflags { switch -- $k { -width - -wrap - -overflow - -appendlines - -looplimit - -experimental { - dict set opts $k $v + tcl::dict::set opts $k $v } default { #don't use [self class] - or we'll get the superclass - error "[info object class [self]] unknown option '$k'. Known options: [dict keys $opts]" + error "[info object class [self]] unknown option '$k'. Known options: [tcl::dict::keys $opts]" } } } - set o_width [dict get $opts -width] - set o_wrap [dict get $opts -wrap] - set o_overflow [dict get $opts -overflow] - set o_appendlines [dict get $opts -appendlines] - set o_looplimit [dict get $opts -looplimit] - set o_cursor_column [dict get $opts -cursor_column] - set o_cursor_row [dict get $opts -cursor_row] + set o_width [tcl::dict::get $opts -width] + set o_wrap [tcl::dict::get $opts -wrap] + set o_overflow [tcl::dict::get $opts -overflow] + set o_appendlines [tcl::dict::get $opts -appendlines] + set o_looplimit [tcl::dict::get $opts -looplimit] + set o_cursor_column [tcl::dict::get $opts -cursor_column] + set o_cursor_row [tcl::dict::get $opts -cursor_row] set o_from_ansistring $from_ansistring set o_ns_from [info object namespace $o_from_ansistring] @@ -4513,7 +4618,7 @@ namespace eval punk::ansi::class { #if {![llength $from_ansisplits]} {$o_from_ansistring eval_in {my MakeSplit}} ;#!!todo - a better way to keep this method semi hidden but call from a 'friend' if {![llength $from_ansisplits]} { - namespace eval $o_ns_from {my MakeSplit} + tcl::namespace::eval $o_ns_from {my MakeSplit} } set eidx [llength $o_rendereditems] @@ -4526,7 +4631,7 @@ namespace eval punk::ansi::class { } if {$eidx == [llength $from_elements]} { #nothing new available - return [dict create type "" rendercount 0 start_count_unrendered 0 end_count_unrendered 0] + return [tcl::dict::create type "" rendercount 0 start_count_unrendered 0 end_count_unrendered 0] } set start_elements_unrendered [expr {[llength $from_elements] - [llength $o_rendereditems]}] @@ -4577,7 +4682,7 @@ namespace eval punk::ansi::class { if 0 { while {[llength $inputchunks]} { set overtext [lpop inputchunks 0] - if {![string length $overtext]} { + if {![tcl::string::length $overtext]} { continue } #set rinfo [overtype::renderline -info 1 -insert_mode 0 -autowrap_mode 1 -width $o_width -overflow 0 -cursor_column $col -cursor_row $row $undertext $overtext] @@ -4586,7 +4691,7 @@ namespace eval punk::ansi::class { $o_to_ansistring append $newtext - return [dict create type $type_rendered rendercount $rendercount start_count_unrendered $start_elements_unrendered end_count_unrendered $end_elements_unrendered] + return [tcl::dict::create type $type_rendered rendercount $rendercount start_count_unrendered $start_elements_unrendered end_count_unrendered $end_elements_unrendered] } } @@ -4599,7 +4704,7 @@ namespace eval punk::ansi::class { } } - if {[llength [info commands ::punk::ansi::class::class_ansistring]]} { + if {[llength [tcl::info::commands ::punk::ansi::class::class_ansistring]]} { ::punk::ansi::class::class_ansistring destroy } #As this is intended for column-based terminals - it has a different notion of string length, string index etc than for a plain string. @@ -4635,11 +4740,11 @@ namespace eval punk::ansi::class { #-- make assert available -- # By pointing it to the assert imported into ::punk::ansi::class # (we could alternatively import assert *directly* from ::punk::assertion::assert - but we can't chain imports as setting active flag renames the command, breaking imports) - set nspath [namespace path] + set nspath [tcl::namespace::path] if {"::punk::ansi::class" ni $nspath} { lappend nspath ::punk::ansi::class } - namespace path $nspath + tcl::namespace::path $nspath #-- -- #we choose not to generate an internal split-state for the initial string - which may potentially be large. @@ -4691,14 +4796,14 @@ namespace eval punk::ansi::class { if {![llength $o_ansisplits]} { append result "No internal splits. " append result \n "has ansi : [my has_ansi]" - append result \n "Tcl string length raw string: [string length $o_string]" + append result \n "Tcl string length raw string: [tcl::string::length $o_string]" } else { append result \n "has ansi : [my has_ansi]" append result \n "ansisplit list len: [llength $o_ansisplits]" append result \n "plaintext list len: [llength $o_ptlist]" append result \n "cached count : $o_count" - append result \n "Tcl string length raw string : [string length $o_string]" - append result \n "Tcl string length plaintext parts: [string length [join $o_ptlist ""]]" + append result \n "Tcl string length raw string : [tcl::string::length $o_string]" + append result \n "Tcl string length plaintext parts: [tcl::string::length [join $o_ptlist ""]]" if {[llength $o_ansisplits] %2 == 0} { append result \n -------------------------------------------------- append result \n Warning - ansisplits appears to be invalid length @@ -4816,7 +4921,7 @@ namespace eval punk::ansi::class { set re_diacritics {[\u0300-\u036f]+|[\u1ab0-\u1aff]+|[\u1dc0-\u1dff]+|[\u20d0-\u20ff]+|[\ufe20-\ufe2f]+} #we want length to return number of glyphs + normal controls such as newline.. not screen width. Has to be consistent with index function - return [string length [regsub -all $re_diacritics $plaintext ""]] + return [tcl::string::length [regsub -all $re_diacritics $plaintext ""]] } #This is the count of visible graphemes + non-ansi control chars. Not equal to column width or to the Tcl string length of the ansistripped string!!! @@ -4838,16 +4943,16 @@ namespace eval punk::ansi::class { if {[punk::ansi::ta::detect $o_string]} { my MakeSplit } else { - return [string length $o_string] + return [tcl::string::length $o_string] } } elseif {[llength $o_ansisplits] == 1} { #single split always means no ansi - return string length $o_string + return [tcl::string::length $o_string] } - return [string length [join $o_ptlist ""]] + return [tcl::string::length [join $o_ptlist ""]] } method length_raw {} { - return [string length $o_string] + return [tcl::string::length $o_string] } #channels for stream in/out.. these are vaguely analogous to the input/output between a shell and a PTY Slave - but this is not intended to be a full pseudoterminal @@ -4856,9 +4961,9 @@ namespace eval punk::ansi::class { #renderstream_from_render (public?) method rendertypes {} { - set classes [info commands ::punk::ansi::class::renderer::class_*] + set classes [tcl::info::commands ::punk::ansi::class::renderer::class_*] #strip off class_ - set ctypes [lmap v $classes {string range [namespace tail $v] 6 end}] + set ctypes [lmap v $classes {tcl::string::range [tcl::namespace::tail $v] 6 end}] } method rendertype {{rtype ""}} { if {$rtype eq ""} { @@ -4874,8 +4979,8 @@ namespace eval punk::ansi::class { } if {$o_renderer ne ""} { set oinfo [info object class $o_renderer] - set tail [namespace tail $oinfo] - set currenttype [string range $tail 6 end] + set tail [tcl::namespace::tail $oinfo] + set currenttype [tcl::string::range $tail 6 end] if {$rtype ne $currenttype} { puts "switch rendertype from $currenttype to $rtype - destroying renderer and creating a new one" $o_renderer destroy ;#what happens to data in target ansistring obj? when does it make sense to keep output and keep rendering vs clearing? @@ -4926,7 +5031,7 @@ namespace eval punk::ansi::class { if {$o_renderer eq ""} { error "No renderer. Call render methods first" } - return [dict create row [$o_renderer cursor_row] column [$o_renderer cursor_column]] + return [tcl::dict::create row [$o_renderer cursor_row] column [$o_renderer cursor_column]] } #--- @@ -4971,8 +5076,8 @@ namespace eval punk::ansi::class { #assertion - if o_ptlist is empty so is o_ansisplits lappend o_ansisplits $catstr } else { - lset o_ptlist end [string cat [lindex $o_ptlist end] $catstr] - lset o_ansisplits end [string cat [lindex $o_ansisplits end] $catstr] + lset o_ptlist end [tcl::string::cat [lindex $o_ptlist end] $catstr] + lset o_ansisplits end [tcl::string::cat [lindex $o_ansisplits end] $catstr] } set last_codestack [lindex $o_sgrstacks end] set last_gx0state [lindex $o_gx0states end] @@ -5046,9 +5151,9 @@ namespace eval punk::ansi::class { incr current_split_index ;#increment 2 of 2 } } - lset o_ptlist end [string cat [lindex $o_ptlist end] [lindex $new_pt_list 0]] + lset o_ptlist end [tcl::string::cat [lindex $o_ptlist end] [lindex $new_pt_list 0]] lappend o_ptlist {*}[lrange $new_pt_list 1 end] - lset o_ansisplits end [string cat [lindex $o_ansisplits end] [lindex $newsplits 0]] + lset o_ansisplits end [tcl::string::cat [lindex $o_ansisplits end] [lindex $newsplits 0]] lappend o_ansisplits {*}[lrange $newsplits 1 end] #if {$o_count eq ""} { @@ -5075,7 +5180,7 @@ namespace eval punk::ansi::class { upvar ${ns}::o_ansisplits new_ansisplits upvar ${ns}::o_count new_count if {![llength $new_ansisplits] || $new_count eq ""} { - namespace eval $ns {my MakeSplit} + tcl::namespace::eval $ns {my MakeSplit} } upvar ${ns}::o_ptlist new_ptlist upvar ${ns}::o_string new_string @@ -5084,9 +5189,9 @@ namespace eval punk::ansi::class { upvar ${ns}::o_gx0states new_gx0states upvar ${ns}::o_splitindex new_splitindex - lset o_ansisplits end [string cat [lindex $o_ansisplits end] [lindex $new_ansisplits 0]] + lset o_ansisplits end [tcl::string::cat [lindex $o_ansisplits end] [lindex $new_ansisplits 0]] lappend o_ansisplits {*}[lrange $new_ansisplits 1 end] - lset o_ptlist end [string cat [lindex $o_ptlist end] [lindex $new_ptlist 0]] + lset o_ptlist end [tcl::string::cat [lindex $o_ptlist end] [lindex $new_ptlist 0]] lappend o_ptlist {*}[lrange $new_ptlist 1 end] append o_string $new_string @@ -5124,7 +5229,7 @@ namespace eval punk::ansi::class { if {$o_string eq ""} { return "" } - #ansistring VIEW relies only on the raw ansi input as it is essentially just a string map. + #ansistring VIEW relies only on the raw ansi input as it is essentially just a tcl::string::map. #We don't need to force an ansisplit if we happen to have an unsplit initial string ansistring VIEW $o_string } @@ -5170,13 +5275,13 @@ namespace eval punk::ansi::class { append output [ansistring VIEW {*}$args $pt] #map DEC cursor_save/restore to CSI version - set code [string map [list \x1b7 \x1b\[s \x1b8 \x1b\[u ] $code] + set code [tcl::string::map [list \x1b7 \x1b\[s \x1b8 \x1b\[u ] $code] - set c1 [string index $code 0] - set c1c2 [string range $code 0 1] + set c1 [tcl::string::index $code 0] + set c1c2 [tcl::string::range $code 0 1] #set re_ST_open {(?:\033P|\u0090|\033X|\u0098|\033\^|\u009e|\033_|\u009f)} - set leadernorm [string range [string map [list\ + set leadernorm [tcl::string::range [tcl::string::map [list\ \x1b\[ 7CSI\ \x9b 8CSI\ \x1b\] 7OSC\ @@ -5188,13 +5293,13 @@ namespace eval punk::ansi::class { #we leave the tail of the code unmapped for now switch -- $leadernorm { 7CSI - 7OSC { - set codenorm [string cat $leadernorm [string range $code 2 end]] + set codenorm [tcl::string::cat $leadernorm [tcl::string::range $code 2 end]] } 7ESC { - set codenorm [string cat $leadernorm [string range $code 1 end]] + set codenorm [tcl::string::cat $leadernorm [tcl::string::range $code 1 end]] } 8CSI - 8OSC { - set codenorm [string cat $leadernorm [string range $code 1 end]] + set codenorm [tcl::string::cat $leadernorm [tcl::string::range $code 1 end]] } default { #we haven't made a mapping for this @@ -5204,9 +5309,9 @@ namespace eval punk::ansi::class { switch -- $leadernorm { {7CSI} - {8CSI} { - set param [string range $codenorm 4 end-1] - #puts stdout "--> CSI [string index $leadernorm 0] bit param:$param" - switch -- [string index $codenorm end] { + set param [tcl::string::range $codenorm 4 end-1] + #puts stdout "--> CSI [tcl::string::index $leadernorm 0] bit param:$param" + switch -- [tcl::string::index $codenorm end] { m { if {[punk::ansi::codetype::is_sgr_reset $code]} { set displaycode [ansistring VIEW $code] @@ -5215,8 +5320,8 @@ namespace eval punk::ansi::class { set displaycode [ansistring VIEW $code] if {[punk::ansi::codetype::has_sgr_leadingreset $code]} { #highlight the esc & leftbracket in white as indication there is a leading reset - set cposn [string first ";" $displaycode] - append output ${whiteb}[string range $displaycode 0 $cposn]$RST${greenb}[string range $displaycode $cposn+1 end]$RST + set cposn [tcl::string::first ";" $displaycode] + append output ${whiteb}[tcl::string::range $displaycode 0 $cposn]$RST${greenb}[tcl::string::range $displaycode $cposn+1 end]$RST } else { append output ${greenb}$displaycode$RST } @@ -5225,18 +5330,18 @@ namespace eval punk::ansi::class { A - B { #row move set displaycode [ansistring VIEW $code] - set displaycode [string map [list A "A$arrow_up" B "B$arrow_down"] $displaycode] + set displaycode [tcl::string::map [list A "A$arrow_up" B "B$arrow_down"] $displaycode] append output ${cyanb}$displaycode$RST } C - D - G { - #set num [string range $codenorm 4 end-1] + #set num [tcl::string::range $codenorm 4 end-1] set displaycode [ansistring VIEW $code] - set displaycode [string map [list C "C$arrow_right" D "D$arrow_left" G "G$arrow_lr"] $displaycode] + set displaycode [tcl::string::map [list C "C$arrow_right" D "D$arrow_left" G "G$arrow_lr"] $displaycode] append output ${cyanb}$displaycode$RST } H - f { - set params [string range $codenorm 4 end-1] + set params [tcl::string::range $codenorm 4 end-1] lassign [split $params {;}] row col #lassign $matchinfo _match row col set displaycode [ansistring VIEW $code] @@ -5247,7 +5352,7 @@ namespace eval punk::ansi::class { #row and col move set map [list H "H${arrow_lr}${arrow_du}" f "${arrow_lr}${arrow_du}"] } - set displaycode [string map $map $displaycode] + set displaycode [tcl::string::map $map $displaycode] append output ${cyanb}$displaycode$RST } s { @@ -5262,7 +5367,7 @@ namespace eval punk::ansi::class { } } 7GFX { - switch -- [string index $codenorm 4] { + switch -- [tcl::string::index $codenorm 4] { "0" { append output ${GX}GX+$RST } @@ -5333,7 +5438,40 @@ namespace eval punk::ansi::class { } } } -namespace eval punk::ansi::ansistring { +tcl::namespace::eval punk::ansi { + proc stripansi {text} [string map [list $::punk::ansi::ta::re_ansi_split] { + #*** !doctools + #[call [fun stripansi] [arg text] ] + #[para]Return a string with ansi codes stripped out + #[para]Alternate graphics chars are replaced with modern unicode equivalents (e.g boxdrawing glyphs) + + #using detect costs us a couple of uS - but saves time on plain text + #we should probably leave this for caller - otherwise it ends up being called more than necessary + #if {![::punk::ansi::ta::detect $text]} { + # return $text + #} + + #alternate graphics codes are not the norm + # - so save a few uS in the common case by only calling convert_g0 if we detect + if {[punk::ansi::ta::detect_g0 $text]} { + set text [convert_g0 $text];#Convert ansi borders to unicode line drawing instead of ascii letters + } + punk::ansi::ta::Do_split_at_codes_join $text {} + }] + + proc stripansiraw {text} [string map [list $::punk::ansi::ta::re_ansi_split] { + #*** !doctools + #[call [fun stripansi] [arg text] ] + #[para]Return a string with ansi codes stripped out + #[para]Alternate graphics modes will be stripped rather than converted to unicode - exposing the raw ascii characters as they appear without graphics mode. + #[para]ie instead of a horizontal line you may see: qqqqqq + + #join [::punk::ansi::ta::split_at_codes $text] "" + punk::ansi::ta::Do_split_at_codes_join $text {} + }] +} + +tcl::namespace::eval punk::ansi::ansistring { #*** !doctools #[subsection {Namespace punk::ansi::ansistring}] #[para]punk::ansi::ansistring ensemble - ansi-aware string operations @@ -5341,9 +5479,9 @@ namespace eval punk::ansi::ansistring { #[para]Just as working with other forms of markup such as HTML - you simply need to be aware of the tradeoffs and design accordingly. #[list_begin definitions] - namespace path [list ::punk::ansi ::punk::ansi::ta] - namespace ensemble create - namespace export length trim trimleft trimright INDEX COUNT VIEW VIEWCODES VIEWSTYLE INDEXABSOLUTE INDEXCOLUMNS COLUMNINDEX NEW + tcl::namespace::path [list ::punk::ansi ::punk::ansi::ta] + tcl::namespace::ensemble create + tcl::namespace::export length trim trimleft trimright INDEX COUNT VIEW VIEWCODES VIEWSTYLE INDEXABSOLUTE INDEXCOLUMNS COLUMNINDEX NEW #todo - expose _splits_ methods so caller can work efficiently with the splits themselves #we need to consider whether these can be agnostic towards splits from split_codes vs split_codes_single @@ -5461,14 +5599,14 @@ namespace eval punk::ansi::ansistring { #modern (c0 seem to have more terminal/font support - C1 can show 8bit c1 codes - but also seems to be limited support) #Goal is not to map every control character? - #Map of which elements we want to convert - done this way so we can see names of control's that are included: - ease of maintenance compared to just creating the string map directly + #Map of which elements we want to convert - done this way so we can see names of control's that are included: - ease of maintenance compared to just creating the tcl::string::map directly #ETX -ctrl-c #EOT ctrl-d (EOF?) #SYN ctrl-v #SUB ctrl-z #CAN ctrl-x #FS ctrl-\ (SIGQUIT) - set visuals_interesting [dict create\ + set visuals_interesting [tcl::dict::create\ NUL [list \x00 \u2400]\ ETX [list \x03 \u2403]\ EOT [list \x04 \u2404]\ @@ -5484,7 +5622,7 @@ namespace eval punk::ansi::ansistring { APC [list \x9f \ue03f]\ ] #it turns out we need pretty much everything for debugging - set visuals_c0 [dict create\ + set visuals_c0 [tcl::dict::create\ NUL [list \x00 \u2400]\ SOH [list \x01 \u2401]\ STX [list \x02 \u2402]\ @@ -5548,7 +5686,7 @@ namespace eval punk::ansi::ansistring { set obm \u27e6 ;set cbm \u27e7 ;#square double brackets from Miscellaneous Mathematical Symbols-A #this private range so rarely supported in fonts - and visuals are unknown, so we will make up some 2-letter codes for now - #set visuals_c1 [dict create\ + #set visuals_c1 [tcl::dict::create\ # BPH [list \x82 "${ob8}\ue022 $cb8"]\ # NBH [list \x83 "${ob8}\ue023 $cb8"]\ # IND [list \x84 "${ob8}\ue024 $cb8"]\ @@ -5582,7 +5720,7 @@ namespace eval punk::ansi::ansistring { #these 2 letter codes only need to disambiguate within the c1 set - they're not great. #these sit within the Latin-1 Supplement block - set visuals_c1 [dict create\ + set visuals_c1 [tcl::dict::create\ PAD [list \x80 "${ob8}PD$cb8"]\ HOP [list \x81 "${ob8}HP$cb8"]\ BPH [list \x82 "${ob8}BP$cb8"]\ @@ -5617,19 +5755,19 @@ namespace eval punk::ansi::ansistring { ] - set hack [dict create] - dict set hack BOM1 [list \uFEFF "${obm}\U1f4a3$cbm"] ;#byte order mark/ ZWNBSP (ZWNBSP usage generally deprecated) - a picture of a bomb(2wide glyph) + set hack [tcl::dict::create] + tcl::dict::set hack BOM1 [list \uFEFF "${obm}\U1f4a3$cbm"] ;#byte order mark/ ZWNBSP (ZWNBSP usage generally deprecated) - a picture of a bomb(2wide glyph) #review - other boms? Encoding dependent? - dict set hack DCS [list \x90 "${ob8}\u2328 $cb8"] ;#keyboard from Miscellaneous Technical - 1 wide + pad. - dict set hack SOS [list \x98 "${ob8}\u2380 $cb8"] ;#Insertion Symbol from Miscellaneous Technical - 1 wide + pad - dict set hack ST [list \x9c "${ob8}\u2383 $cb8"] ;#Emphasis Symbol from Miscellaneous Technical - 1 wide + pad (graphically related to \u2380) - dict set hack CSI [list \x9b "${ob8}\u2386 $cb8"] ;#Enter Symbol from Miscellaneous Technical - 1 wide + pad - dict set hack OSC [list \x9d "${ob8}\u2b55$cb8"] ;#bright red ring from Miscellaneous Symbols and Arrows - 2 wide (OSC could be used for clipboard or other potentially security sensitive functions) - dict set hack PM [list \x9e "${ob8}PM$cb8"] - dict set hack APC [list \x9f "${ob8}\U1f534$cb8"] ;#bright red ball from Miscellaneoust Symbols and Pictographs - 2 wide (APC also noted as a potential security risk) + tcl::dict::set hack DCS [list \x90 "${ob8}\u2328 $cb8"] ;#keyboard from Miscellaneous Technical - 1 wide + pad. + tcl::dict::set hack SOS [list \x98 "${ob8}\u2380 $cb8"] ;#Insertion Symbol from Miscellaneous Technical - 1 wide + pad + tcl::dict::set hack ST [list \x9c "${ob8}\u2383 $cb8"] ;#Emphasis Symbol from Miscellaneous Technical - 1 wide + pad (graphically related to \u2380) + tcl::dict::set hack CSI [list \x9b "${ob8}\u2386 $cb8"] ;#Enter Symbol from Miscellaneous Technical - 1 wide + pad + tcl::dict::set hack OSC [list \x9d "${ob8}\u2b55$cb8"] ;#bright red ring from Miscellaneous Symbols and Arrows - 2 wide (OSC could be used for clipboard or other potentially security sensitive functions) + tcl::dict::set hack PM [list \x9e "${ob8}PM$cb8"] + tcl::dict::set hack APC [list \x9f "${ob8}\U1f534$cb8"] ;#bright red ball from Miscellaneoust Symbols and Pictographs - 2 wide (APC also noted as a potential security risk) - set debug_visuals [dict merge $visuals_c0 $visuals_c1 $hack] + set debug_visuals [tcl::dict::merge $visuals_c0 $visuals_c1 $hack] #for repeated interaction with the same ANSI string - a mechanism to store state is more efficient proc NEW {string} { @@ -5650,7 +5788,7 @@ namespace eval punk::ansi::ansistring { } set string [lindex $args end] - set defaults [dict create\ + set defaults [tcl::dict::create\ -esc 1\ -cr 1\ -lf 0\ @@ -5661,57 +5799,58 @@ namespace eval punk::ansi::ansistring { ] set argopts [lrange $args 0 end-1] if {[llength $argopts] % 2 != 0} { - error "ansistring VIEW options must be option-value pairs, received '$argopts'. Known opts [dict keys $defaults]" + error "ansistring VIEW options must be option-value pairs, received '$argopts'. Known opts [tcl::dict::keys $defaults]" } - set opts [dict merge $defaults $argopts] + set opts [tcl::dict::merge $defaults $argopts] # -- --- --- --- --- - set opt_esc [dict get $opts -esc] - set opt_cr [dict get $opts -cr] - set opt_lf [dict get $opts -lf] - set opt_vt [dict get $opts -vt] - set opt_ht [dict get $opts -ht] - set opt_bs [dict get $opts -bs] - set opt_sp [dict get $opts -sp] + set opt_esc [tcl::dict::get $opts -esc] + set opt_cr [tcl::dict::get $opts -cr] + set opt_lf [tcl::dict::get $opts -lf] + set opt_vt [tcl::dict::get $opts -vt] + set opt_ht [tcl::dict::get $opts -ht] + set opt_bs [tcl::dict::get $opts -bs] + set opt_sp [tcl::dict::get $opts -sp] # -- --- --- --- --- - set visuals_opt [dict create] + set visuals_opt $debug_visuals if {$opt_esc} { - dict set visuals_opt ESC [list \x1b \u241b] + tcl::dict::set visuals_opt ESC [list \x1b \u241b] } if {$opt_cr} { - dict set visuals_opt CR [list \x0d \u240d] + tcl::dict::set visuals_opt CR [list \x0d \u240d] } if {$opt_lf == 1} { - dict set visuals_opt LF [list \x0a \u240a] + tcl::dict::set visuals_opt LF [list \x0a \u240a] } if {$opt_lf == 2} { - dict set visuals_opt LF [list \x0a \u240a\n] + tcl::dict::set visuals_opt LF [list \x0a \u240a\n] } if {$opt_vt} { - dict set visuals_opt VT [list \x0b \u240b] + tcl::dict::set visuals_opt VT [list \x0b \u240b] } if {$opt_ht} { - dict set visuals_opt HT [list \x09 \u2409] + tcl::dict::set visuals_opt HT [list \x09 \u2409] } if {$opt_bs} { - dict set visuals_opt BS [list \x08 \u2408] + tcl::dict::set visuals_opt BS [list \x08 \u2408] } if {$opt_sp} { - dict set visuals_opt SP [list \x20 \u2420] + tcl::dict::set visuals_opt SP [list \x20 \u2420] } - set visuals [dict merge $visuals_opt $debug_visuals] - set charmap [list] - dict for {nm chars} $visuals { - lappend charmap {*}$chars - } - return [string map $charmap $string] + #set visuals [tcl::dict::merge $visuals_opt $debug_visuals] + #set charmap [list] + #tcl::dict::for {nm chars} $visuals_opt { + # lappend charmap {*}$chars + #} + #return [tcl::string::map $charmap $string] + return [tcl::string::map [concat {*}[dict values $visuals_opt]] $string] #test of ISO2047 - 7bit - limited set, limited support, somewhat obscure glyphs - #return [string map [list \033 \U2296 \007 \U237E] $string] + #return [tcl::string::map [list \033 \U2296 \007 \U237E] $string] } #The implementation of viewcodes,viewstyle is more efficiently done in an object for the case where repeated calls of various methods can re-use the internal splits. @@ -5763,7 +5902,7 @@ namespace eval punk::ansi::ansistring { set string [regsub -all $re_diacritics $string ""] #we want length to return number of glyphs.. not screen width. Has to be consistent with index function - string length [stripansi $string] + tcl::string::length [stripansi $string] } #included as a test/verification - slightly slower. #grapheme split version may end up being used once it supports unicode grapheme clusters @@ -5773,7 +5912,7 @@ namespace eval punk::ansi::ansistring { } proc length {string} { - string length [stripansi $string] + tcl::string::length [stripansi $string] } proc _splits_trimleft {sclist} { @@ -5785,7 +5924,7 @@ namespace eval punk::ansi::ansistring { if {$pt eq "" || [regexp {^\s+$} $pt]} { lappend outlist "" $ansiblock } else { - lappend outlist [string trimleft $pt] $ansiblock + lappend outlist [tcl::string::trimleft $pt] $ansiblock set intext 1 } } else { @@ -5796,7 +5935,7 @@ namespace eval punk::ansi::ansistring { if {$pt eq "" || [regexp {^\s+$} $pt]} { lappend outlist "" } else { - lappend outlist [string trimleft $pt] + lappend outlist [tcl::string::trimleft $pt] set intext 1 } } else { @@ -5816,7 +5955,7 @@ namespace eval punk::ansi::ansistring { if {$pt eq "" || [regexp {^\s+$} $pt]} { lappend outlist "" $ansiblock } else { - lappend outlist [string trimright $pt] $ansiblock + lappend outlist [tcl::string::trimright $pt] $ansiblock set intext 1 } } else { @@ -5827,7 +5966,7 @@ namespace eval punk::ansi::ansistring { if {$pt eq "" || [regexp {^\s+$} $pt]} { lappend outlist "" } else { - lappend outlist [string trimright $pt] + lappend outlist [tcl::string::trimright $pt] set intext 1 } } else { @@ -5853,7 +5992,7 @@ namespace eval punk::ansi::ansistring { if {$pt eq "" || [regexp {^\s+$} $pt]} { append out $ansiblock } else { - append out [string trimleft $pt]$ansiblock + append out [tcl::string::trimleft $pt]$ansiblock set intext 1 } } else { @@ -5900,20 +6039,20 @@ namespace eval punk::ansi::ansistring { #todo - end-x +/-x+/-x etc set original_index $index - set index [string map [list _ ""] $index] + set index [tcl::string::map [list _ ""] $index] #short-circuit some trivial cases - if {[string is integer -strict $index]} { + if {[tcl::string::is integer -strict $index]} { if {$index < 0} {return ""} #this only short-circuits an index greater than length including ansi-chars #we don't want to spend cycles stripping ansi for this test so code below will still have to handle index just larger than content-length but still less than entire length - if {$index > [string length $string]} {return ""} + if {$index > [tcl::string::length $string]} {return ""} } else { - if {[string match end* $index]} { + if {[tcl::string::match end* $index]} { #for end- we will probably have to blow a few cycles stripping first and calculate the length if {$index ne "end"} { - set op [string index $index 3] - set offset [string range $index 4 end] - if {$op ni {+ -} || ![string is integer -strict $offset]} {error "bad index '$index': must be integer?\[+-\]integer? or end?\[+-\]integer?"} + set op [tcl::string::index $index 3] + set offset [tcl::string::range $index 4 end] + if {$op ni {+ -} || ![tcl::string::is integer -strict $offset]} {error "bad index '$index': must be integer?\[+-\]integer? or end?\[+-\]integer?"} if {$op eq "+" && $offset != 0} { return "" } @@ -5934,7 +6073,7 @@ namespace eval punk::ansi::ansistring { } else { #we are trying to avoid evaluating unbraced expr of potentially insecure origin regexp {^([+-]{0,1})(.*)} $index _match sign tail ;#should always match - even empty string - if {[string is integer -strict $tail]} { + if {[tcl::string::is integer -strict $tail]} { #plain +- if {$op eq "-"} { #return nothing for negative indices as per Tcl's lindex etc @@ -5943,7 +6082,7 @@ namespace eval punk::ansi::ansistring { set index $tail } else { if {[regexp {(.*)([+-])(.*)} $index _match a op b]} { - if {[string is integer -strict $a] && [string is integer -strict $b]} { + if {[tcl::string::is integer -strict $a] && [tcl::string::is integer -strict $b]} { if {$op eq "-"} { set index [expr {$a - $b}] } else { @@ -5976,13 +6115,13 @@ namespace eval punk::ansi::ansistring { if {$pt ne ""} { set graphemes [punk::char::grapheme_split $pt] set low [expr {$high + 1}] ;#last high - #incr high [string length $pt] + #incr high [tcl::string::length $pt] incr high [llength $graphemes] } if {$pt ne "" && ($index >= $low && $index <= $high)} { set pt_found $pt_index - #set char [string index $pt $index-$low] + #set char [tcl::string::index $pt $index-$low] set char [lindex $graphemes $index-$low] break } @@ -6019,10 +6158,10 @@ namespace eval punk::ansi::ansistring { set payload_len -1 ;# -1 as token to indicate we haven't calculated it yet (only want to call it once at most) set testindices [list] foreach index $args { - if {[string is integer -strict $index]} { + if {[tcl::string::is integer -strict $index]} { if {$index < 0} { lappend testindices "" - } elseif {$index > [string length $string]} { + } elseif {$index > [tcl::string::length $string]} { #this only short-circuits an index greater than length including ansi-chars #we don't want to spend cycles stripping ansi for this test so code below will still have to handle index just larger than content-length but still less than entire length lappend testindices "" @@ -6030,12 +6169,12 @@ namespace eval punk::ansi::ansistring { lappend testindices $index } } else { - if {[string match end* $index]} { + if {[tcl::string::match end* $index]} { #for end- we will probably have to blow a few cycles stripping first and calculate the length if {$index ne "end"} { - set op [string index $index 3] - set offset [string range $index 4 end] - if {$op ni {+ -} || ![string is integer -strict $offset]} {error "bad index '$index': must be integer?\[+-\]integer? or end?\[+-\]integer?"} + set op [tcl::string::index $index 3] + set offset [tcl::string::range $index 4 end] + if {$op ni {+ -} || ![tcl::string::is integer -strict $offset]} {error "bad index '$index': must be integer?\[+-\]integer? or end?\[+-\]integer?"} if {$op eq "+" && $offset != 0} { lappend testindices "" continue @@ -6060,7 +6199,7 @@ namespace eval punk::ansi::ansistring { } else { #we are trying to avoid evaluating unbraced expr of potentially insecure origin regexp {^([+-]{0,1})(.*)} $index _match sign tail ;#should always match - even empty string - if {[string is integer -strict $tail]} { + if {[tcl::string::is integer -strict $tail]} { #plain +- if {$op eq "-"} { #return nothing for negative indices as per Tcl's lindex etc @@ -6071,7 +6210,7 @@ namespace eval punk::ansi::ansistring { lappend testindices $index } else { if {[regexp {(.*)([+-])(.*)} $index _match a op b]} { - if {[string is integer -strict $a] && [string is integer -strict $b]} { + if {[tcl::string::is integer -strict $a] && [tcl::string::is integer -strict $b]} { if {$op eq "-"} { set index [expr {$a - $b}] } else { @@ -6199,7 +6338,7 @@ namespace eval punk::ansi::ansistring { set col2 "" foreach {pt code} $ansisplits { if {$pt ne ""} { - if {[string last \n $pt] < 0} { + if {[tcl::string::last \n $pt] < 0} { set graphemes [punk::char::grapheme_split $pt] set lowindex [expr {$highindex + 1}] ;#last high set lowc [expr {$highc + 1}] @@ -6238,7 +6377,7 @@ namespace eval punk::ansi::ansistring { #[list_end] [comment {--- end definitions namespace punk::ansi::ta ---}] } -namespace eval punk::ansi::internal { +tcl::namespace::eval punk::ansi::internal { proc splitn {str {len 1}} { #from textutil::split::splitn if {$len <= 0} { @@ -6248,11 +6387,11 @@ namespace eval punk::ansi::internal { return [split $str {}] } set result [list] - set max [string length $str] + set max [tcl::string::length $str] set i 0 set j [expr {$len -1}] while {$i < $max} { - lappend result [string range $str $i $j] + lappend result [tcl::string::range $str $i $j] incr i $len incr j $len } @@ -6261,10 +6400,10 @@ namespace eval punk::ansi::internal { proc splitx {str {regexp {[\t \r\n]+}}} { #from textutil::split::splitx # Bugfix 476988 - if {[string length $str] == 0} { + if {$str eq ""} { return {} } - if {[string length $regexp] == 0} { + if {$regexp eq ""} { return [::split $str ""] } if {[regexp $regexp {}]} { @@ -6274,17 +6413,19 @@ namespace eval punk::ansi::internal { set list {} set start 0 while {[regexp -start $start -indices -- $regexp $str match submatch]} { - foreach {subStart subEnd} $submatch break - foreach {matchStart matchEnd} $match break + #foreach {subStart subEnd} $submatch break + lassign $submatch subStart subEnd + #foreach {matchStart matchEnd} $match break + lassign $match matchStart matchEnd incr matchStart -1 incr matchEnd - lappend list [string range $str $start $matchStart] + lappend list [tcl::string::range $str $start $matchStart] if {$subStart >= $start} { - lappend list [string range $str $subStart $subEnd] + lappend list [tcl::string::range $str $subStart $subEnd] } set start $matchEnd } - lappend list [string range $str $start end] + lappend list [tcl::string::range $str $start end] return $list } @@ -6308,11 +6449,11 @@ namespace eval punk::ansi::internal { return $2hex } proc hex2str {2digithexchars} { - set 2digithexchars [string map [list _ ""] $2digithexchars] ;#compatibility with tcl tip 551 (compatibility in the sense that users might expect to be able to use underscores and it's nice to support the syntax here too - not that it's required) + set 2digithexchars [tcl::string::map [list _ ""] $2digithexchars] ;#compatibility with tcl tip 551 (compatibility in the sense that users might expect to be able to use underscores and it's nice to support the syntax here too - not that it's required) if {$2digithexchars eq ""} { return "" } - if {[string length $2digithexchars] % 2 != 0} { + if {[tcl::string::length $2digithexchars] % 2 != 0} { error "hex2str requires an even number of hex digits (2 per character)" } set 2str "" @@ -6325,7 +6466,7 @@ namespace eval punk::ansi::internal { # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ ## Ready -package provide punk::ansi [namespace eval punk::ansi { +package provide punk::ansi [tcl::namespace::eval punk::ansi { variable version set version 0.1.1 }] diff --git a/src/bootsupport/modules/punk/args-0.1.0.tm b/src/bootsupport/modules/punk/args-0.1.0.tm index cd4a9fa..e148a2a 100644 --- a/src/bootsupport/modules/punk/args-0.1.0.tm +++ b/src/bootsupport/modules/punk/args-0.1.0.tm @@ -186,11 +186,20 @@ ## Requirements # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ + +#All ensemble commands are slower in a safe interp as they aren't compiled the same way +#https://core.tcl-lang.org/tcl/tktview/1095bf7f75 +#as this is needed in safe interps too, and the long forms tcl::dict::for tcl::string::map are no slower in a normal interp - we use the long form here. +#(As at 2024-06 There are many tcl8.6/8.7 interps in use which are affected by this and it's unknown when/whether it will be fixed) +#ensembles: array binary clock dict info namespace string +#possibly file too, although that is generally hidden/modified in a safe interp +#chan,encoding always seems to use invokeStk1 anyway - yet there are performance improvements using ::tcl::chan::names etc +#interestingly in tcl8.7 at least - tcl::namespace::eval $somens $somescript is slightly faster than namespace eval even in unsafe interp + #*** !doctools #[subsection dependencies] #[para] packages used by punk::args #[list_begin itemized] - package require Tcl 8.6- #*** !doctools #[item] [package {Tcl 8.6-}] @@ -210,11 +219,11 @@ package require Tcl 8.6- # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # oo::class namespace # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -namespace eval punk::args::class { +tcl::namespace::eval punk::args::class { #*** !doctools #[subsection {Namespace punk::args::class}] #[para] class definitions - if {[info commands [namespace current]::interface_sample1] eq ""} { + if {[tcl::info::commands [tcl::namespace::current]::interface_sample1] eq ""} { #*** !doctools #[list_begin enumerated] @@ -243,13 +252,13 @@ namespace eval punk::args::class { # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # Base namespace # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -namespace eval punk::args { - namespace export {[a-z]*} +tcl::namespace::eval punk::args { + tcl::namespace::export {[a-z]*} variable argspec_cache variable argspecs variable id_counter - set argspec_cache [dict create] - set argspecs [dict create] + set argspec_cache [tcl::dict::create] + set argspecs [tcl::dict::create] set id_counter 0 #*** !doctools @@ -257,20 +266,24 @@ namespace eval punk::args { #[para] Core API functions for punk::args #[list_begin definitions] + proc Get_argspecs {optionspecs args} { variable argspec_cache variable argspecs + variable initial_optspec_defaults + variable initial_valspec_defaults #ideally we would use a fast hash algorithm to produce a short key with low collision probability. #something like md5 would be ok (this is non cryptographic) - but md5 uses open and isn't usable by default in a safe interp. #review - check if there is a built-into-tcl way to do this quickly #for now we will just key using the whole string set cache_key $optionspecs - if {[dict exists $argspec_cache $cache_key]} { - return [dict get $argspec_cache $cache_key] + if {[tcl::dict::exists $argspec_cache $cache_key]} { + return [tcl::dict::get $argspec_cache $cache_key] } - set optionspecs [string map [list \r\n \n] $optionspecs] - set optspec_defaults [dict create\ + set optionspecs [tcl::string::map [list \r\n \n] $optionspecs] + #probably faster to inline a literal dict create in the proc than to use a namespace variable + set optspec_defaults [tcl::dict::create\ -type string\ -optional 1\ -allow_ansi 1\ @@ -279,7 +292,7 @@ namespace eval punk::args { -nocase 0\ -multiple 0\ ] - set valspec_defaults [dict create\ + set valspec_defaults [tcl::dict::create\ -type string\ -optional 0\ -allow_ansi 1\ @@ -287,6 +300,7 @@ namespace eval punk::args { -strip_ansi 0\ -multiple 0\ ] + #checks with no default #-minlen -maxlen -range @@ -295,10 +309,11 @@ namespace eval punk::args { #todo - detect if anything in the spec uses -allow_ansi 0, -validate_without_ansi 1 or -strip_ansi 1 and set a flag indicating if punk::ansi::ta::detect should be run on the argslist set opt_required [list] set val_required [list] - set arg_info [dict create] - set opt_defaults [dict create] + set arg_info [tcl::dict::create] + set arg_checks [tcl::dict::create] + set opt_defaults [tcl::dict::create] set opt_names [list] ;#defined opts - set val_defaults [dict create] + set val_defaults [tcl::dict::create] set opt_solos [list] #first process dashed and non-dashed argspecs without regard to whether non-dashed are at the beginning or end set val_names [list] @@ -309,21 +324,21 @@ namespace eval punk::args { set linelist [split $optionspecs \n] set lastindent "" foreach ln $linelist { - if {[string trim $ln] eq ""} {continue} + if {[tcl::string::trim $ln] eq ""} {continue} regexp {(\s*).*} $ln _all lastindent break ;#break at first non-empty } #puts "indent1:[ansistring VIEW $lastindent]" set in_record 0 foreach rawline $linelist { - set recordsofar [string cat $linebuild $rawline] - if {![info complete $recordsofar]} { + set recordsofar [tcl::string::cat $linebuild $rawline] + if {![tcl::info::complete $recordsofar]} { #append linebuild [string trimleft $rawline] \n if {$in_record} { - if {[string length $lastindent]} { + if {[tcl::string::length $lastindent]} { #trim only the whitespace corresponding to last indent - not all whitespace on left - if {[string first $lastindent $rawline] == 0} { - set trimmedline [string range $rawline [string length $lastindent] end] + if {[tcl::string::first $lastindent $rawline] == 0} { + set trimmedline [tcl::string::range $rawline [tcl::string::length $lastindent] end] append linebuild $trimmedline \n } else { append linebuild $rawline \n @@ -340,10 +355,10 @@ namespace eval punk::args { } } else { set in_record 0 - if {[string length $lastindent]} { + if {[tcl::string::length $lastindent]} { #trim only the whitespace corresponding to last indent - not all whitespace on left - if {[string first $lastindent $rawline] == 0} { - set trimmedline [string range $rawline [string length $lastindent] end] + if {[tcl::string::first $lastindent $rawline] == 0} { + set trimmedline [tcl::string::range $rawline [tcl::string::length $lastindent] end] append linebuild $trimmedline } else { append linebuild $rawline @@ -361,19 +376,19 @@ namespace eval punk::args { set val_max -1 ;#-1 for no limit set spec_id "" foreach ln $records { - set trimln [string trim $ln] - switch -- [string index $trimln 0] { + set trimln [tcl::string::trim $ln] + switch -- [tcl::string::index $trimln 0] { "" - # {continue} } set linespecs [lassign $trimln argname] if {$argname ne "*id" && [llength $linespecs] %2 != 0} { error "punk::args::get_dict - bad optionspecs line for record '$argname' Remaining items on line must be in paired option-value format - received '$linespecs'" } - set firstchar [string index $argname 0] - set secondchar [string index $argname 1] + set firstchar [tcl::string::index $argname 0] + set secondchar [tcl::string::index $argname 1] if {$firstchar eq "*" && $secondchar ne "*"} { set starspecs $linespecs - switch -- [string range $argname 1 end] { + switch -- [tcl::string::range $argname 1 end] { id { #id line must have single entry - a unique id assigned by the user - an id will be allocated if no id line present or the value is "auto" if {[llength $starspecs] != 1} { @@ -386,7 +401,7 @@ namespace eval punk::args { set spec_id $starspecs } proc { - #allow arbitrary + #allow arbitrary - review set proc_info $starspecs } opts { @@ -398,22 +413,50 @@ namespace eval punk::args { } -minlen - -maxlen - -range - -choices - -choicelabels { #review - only apply to certain types? - dict set optspec_defaults $k $v + tcl::dict::set optspec_defaults $k $v } -nominlen - -nomaxlen - -norange - -nochoices - -nochoicelabels { - dict unset optspec_defaults $k + if {$v} { + tcl::dict::unset optspec_defaults $k + } + } + -type { + switch -- $v { + int - integer { + set v int + } + char - character { + set v char + } + bool - boolean { + set v bool + } + dict - dictionary { + set v dict + } + none - any - ansistring { + + } + default { + #todo - disallow unknown types unless prefixed with custom- + } + } + tcl::dict::set optspec_defaults $k $v } - -type - -optional - -allow_ansi - -validate_without_ansi - -strip_ansi - -multiple { #allow overriding of defaults for options that occur later - dict set optspec_defaults $k $v + tcl::dict::set optspec_defaults $k $v } default { - error "punk::args::Get_argspecs - unrecognised key '$k' in *opts line. Known keys: -anyopts" + set known { -any -anyopts -minlen -maxlen -range -choices -choicelabels\ + -nominlen -nomaxlen -norange -nochoices -nochoicelabels\ + -type -optional -allow_ansi -validate_without_ansi -strip_ansi -multiple\ + } + error "punk::args::Get_argspecs - unrecognised key '$k' in *opts line. Known keys: $known" } } } @@ -431,80 +474,119 @@ namespace eval punk::args { } -minlen - -maxlen - -range - -choices - -choicelabels { #review - only apply to certain types? - dict set valspec_defaults $k $v + tcl::dict::set valspec_defaults $k $v } -nominlen - -nomaxlen - -norange - -nochoices - -nochoicelabels { - dict unset valspec_defaults $k + if {$v} { + tcl::dict::unset valspec_defaults $k + } } - -type - + -type { + switch -- $v { + int - integer { + set v int + } + char - character { + set v char + } + bool - boolean { + set v bool + } + dict - dictionary { + set v dict + } + default { + #todo - disallow unknown types unless prefixed with custom- + } + } + tcl::dict::set valspec_defaults $k $v + } + -optional - -allow_ansi - -validate_without_ansi - -strip_ansi - -multiple { - dict set valspec_defaults $k $v + tcl::dict::set valspec_defaults $k $v } default { - error "punk::args::Get_argspecs - unrecognised key '$k' in *opts line. Known keys: -anyopts" + set known { -min -minvalues -max -maxvalues\ + -minlen -maxlen -range -choices -choicelabels\ + -nominlen -nomaxlen -norange -nochoices -nochoicelabels\ + -type -optional -allow_ansi -validate_without_ansi -strip_ansi -multiple\ + } + error "punk::args::Get_argspecs - unrecognised key '$k' in *values line. Known keys: $known" } } } } default { - error "punk::args::Get_argspecs - unrecognised * line in. Expected *proc *opts or *values - use **name if paramname needs to be *name" + error "punk::args::Get_argspecs - unrecognised * line in '$ln'. Expected *proc *opts or *values - use **name if paramname needs to be *name" } } continue } elseif {$firstchar eq "-"} { set argspecs $linespecs - dict set argspecs -ARGTYPE option + tcl::dict::set argspecs -ARGTYPE option lappend opt_names $argname set is_opt 1 } else { if {$firstchar eq "*"} { #allow basic ** escaping for literal argname that begins with * - set argname [string range $argname 1 end] + set argname [tcl::string::range $argname 1 end] } set argspecs $linespecs - dict set argspecs -ARGTYPE value + tcl::dict::set argspecs -ARGTYPE value lappend val_names $argname set is_opt 0 } #assert - we only get here if it is a value or flag specification line. #assert argspecs has been set to the value of linespecs - set merged $optspec_defaults + if {$is_opt} { + set spec_merged $optspec_defaults + } else { + set spec_merged $valspec_defaults + } foreach {spec specval} $argspecs { #literal-key switch - bytecompiled to jumpTable switch -- $spec { -type { #normalize here so we don't have to test during actual args parsing in main function - switch -- [string tolower $specval] { + switch -- [tcl::string::tolower $specval] { int - integer { - dict set merged -type int + tcl::dict::set spec_merged -type int } bool - boolean { - dict set merged -type bool + tcl::dict::set spec_merged -type bool } char - character { - dict set merged -type char + tcl::dict::set spec_merged -type char + } + dict - dictionary { + tcl::dict::set spec_merged -type dict } "" - none { if {$is_opt} { - dict set merged -type none - dict set merged -default 0 ;#-default 0 can still be overridden if -default appears after -type - we'll allow it. + tcl::dict::set spec_merged -type none + tcl::dict::set spec_merged -default 0 ;#-default 0 can still be overridden if -default appears after -type - we'll allow it. lappend opt_solos $argname } else { #-solo only valid for flags error "punk::args::get_dict - invalid -type 'none' for positional argument positional argument '$argname'" } } + any - ansistring { + tcl::dict::set spec_merged -type any + } default { - dict set merged -type [string tolower $specval] + #allow custom unknown types through for now. Todo - require unknown types to begin with custom- REVIEW + tcl::dict::set spec_merged -type [tcl::string::tolower $specval] } } } -default - -solo - -range - -choices - -choicelabels - -minlen - -maxlen - -nocase - -optional - -multiple - -validate_without_ansi - -allow_ansi - -strip_ansi - -help - -ARGTYPE { - dict set merged $spec $specval + #review -solo 1 vs -type none ? + tcl::dict::set spec_merged $spec $specval } default { set known_argspecs [list -default -type -range -choices -nocase -optional -multiple -validate_without_ansi -allow_ansi -strip_ansi -help] @@ -512,42 +594,47 @@ namespace eval punk::args { } } } - set argspecs $merged - #if {$is_opt} { - set argchecks [dict remove $argspecs -type -default -multiple -strip_ansi -validate_without_ansi -allow_ansi] ;#leave things like -range -minlen - #} else { - # set argchecks [dict remove $argspecs -type -default -multiple -strip_ansi -validate_without_ansi -allow_ansi] ;#leave things like -range -minlen - #} - dict set arg_info $argname $argspecs - dict set arg_checks $argname $argchecks - if {![dict get $argspecs -optional]} { + set argspecs $spec_merged + if {$is_opt} { + set argchecks [tcl::dict::remove $argspecs -type -default -multiple -strip_ansi -validate_without_ansi -allow_ansi] ;#leave things like -range -minlen + } else { + set argchecks [tcl::dict::remove $argspecs -type -default -multiple -strip_ansi -validate_without_ansi -allow_ansi] ;#leave things like -range -minlen + } + tcl::dict::set arg_info $argname $argspecs + tcl::dict::set arg_checks $argname $argchecks + #review existence of -default overriding -optional + if {![tcl::dict::get $argspecs -optional] && ![tcl::dict::exists $argspecs -default]} { if {$is_opt} { lappend opt_required $argname } else { lappend val_required $argname } } - if {[dict exists $argspecs -default]} { + if {[tcl::dict::exists $argspecs -default]} { if {$is_opt} { - dict set opt_defaults $argname [dict get $argspecs -default] + tcl::dict::set opt_defaults $argname [tcl::dict::get $argspecs -default] } else { - dict set val_defaults $argname [dict get $argspecs -default] + tcl::dict::set val_defaults $argname [tcl::dict::get $argspecs -default] } } } #confirm any valnames before last don't have -multiple key foreach valname [lrange $val_names 0 end-1] { - if {[dict get $arg_info $valname -multiple]} { + if {[tcl::dict::get $arg_info $valname -multiple]} { error "bad key -multiple on argument spec for '$valname'. Only the last value argument specification can be marked -multiple" } } - if {$spec_id eq "" || [string tolower $spec_id] eq "auto"} { + if {$spec_id eq "" || [tcl::string::tolower $spec_id] eq "auto"} { variable id_counter set spec_id "autoid_[incr id_counter]" } - set result [dict create\ + + set opt_checks_defaults [tcl::dict::remove $optspec_defaults -type -default -multiple -strip_ansi -validate_without_ansi -allow_ansi] ;#leave things like -range -minlen + set val_checks_defaults [tcl::dict::remove $valspec_defaults -type -default -multiple -strip_ansi -validate_without_ansi -allow_ansi] ;#leave things like -range -minlen + + set result [tcl::dict::create\ id $spec_id\ arg_info $arg_info\ arg_checks $arg_checks\ @@ -557,49 +644,161 @@ namespace eval punk::args { opt_any $opt_any\ opt_solos $opt_solos\ optspec_defaults $optspec_defaults\ - valspec_defaults $valspec_defaults\ + opt_checks_defaults $opt_checks_defaults\ val_defaults $val_defaults\ val_required $val_required\ val_names $val_names\ val_min $val_min\ val_max $val_max\ valspec_defaults $valspec_defaults\ + val_checks_defaults $val_checks_defaults\ proc_info $proc_info\ ] - dict set argspec_cache $cache_key $result - dict set argspecs $spec_id $optionspecs + tcl::dict::set argspec_cache $cache_key $result + tcl::dict::set argspecs $spec_id $optionspecs + #puts "xxx:$result" return $result } proc get_spec {id} { variable argspecs - if {[dict exists $argspecs $id]} { - return [dict get $argspecs $id] + if {[tcl::dict::exists $argspecs $id]} { + return [tcl::dict::get $argspecs $id] } return } proc get_spec_ids {{match *}} { variable argspecs - return [dict keys $argspecs $match] + return [tcl::dict::keys $argspecs $match] } #for use within get_dict only #This mechanism gets less-than-useful results for oo methods #e.g {$obj} proc Get_caller {} { - set cmdinfo [dict get [info frame -3] cmd] + set cmdinfo [tcl::dict::get [tcl::info::frame -3] cmd] #puts "-->$cmdinfo" + #puts "-->[tcl::info::frame -3]" set caller [regexp -inline {\S+} $cmdinfo] if {$caller eq "namespace"} { - set caller "punk::args::get_dict called from namespace" + set cmdinfo "punk::args::get_dict called from namespace" } - return $caller + return $cmdinfo } - proc err {msg args} { + proc arg_error {msg spec_dict {badarg ""}} { + set errmsg $msg + if {![catch {package require textblock}]} { + if {[catch { + append errmsg \n + set procname [punk::lib::dict_getdef $spec_dict proc_info -name ""] + set prochelp [punk::lib::dict_getdef $spec_dict proc_info -help ""] - } + set t [textblock::class::table new [a+ web-yellow]Usage[a]] + + set blank_header_col [list ""] + if {$procname ne ""} { + lappend blank_header_col "" + set procname_display [a+ web-white]$procname[a] + } else { + set procname_display "" + } + if {$prochelp ne ""} { + lappend blank_header_col "" + set prochelp_display [a+ web-white]$prochelp[a] + } else { + set prochelp_display "" + } + $t add_column -headers $blank_header_col -minwidth 3 + $t add_column -headers $blank_header_col + $t add_column -headers $blank_header_col + $t add_column -headers $blank_header_col + $t add_column -headers $blank_header_col + if {"$procname$prochelp" eq ""} { + $t configure_header 0 -values {Arg Type Default Multiple Help} + } elseif {$procname eq ""} { + $t configure_header 0 -colspans {1 4 0 0 0} -values [list Description: $prochelp_display] + $t configure_header 1 -values {Arg Type Default Multiple Help} + } elseif {$prochelp eq ""} { + $t configure_header 0 -colspans {1 4 0 0 0} -values [list PROC/METHOD: $procname_display] + $t configure_header 1 -values {Arg Type Default Multiple Help} + } else { + $t configure_header 0 -colspans {1 4 0 0 0} -values [list PROC/METHOD: $procname_display] + $t configure_header 1 -colspans {1 4 0 0 0} -values [list Description: $prochelp_display] + $t configure_header 2 -values {Arg Type Default Multiple Help} + } + set c_default [a+ web-white Web-limegreen] + set c_badarg [a+ web-crimson] + set greencheck [a+ web-limegreen]\u2713[a] + + foreach arg [dict get $spec_dict opt_names] { + set arginfo [dict get $spec_dict arg_info $arg] + if {[dict exists $arginfo -default]} { + #set default $c_default[dict get $arginfo -default] + set default [dict get $arginfo -default] + } else { + set default "" + } + set help [punk::lib::dict_getdef $arginfo -help ""] + if {[dict exists $arginfo -choices]} { + if {$help ne ""} {append help \n} + append help "Choices: [dict get $arginfo -choices]" + } + if {[punk::lib::dict_getdef $arginfo -multiple 0]} { + set multiple $greencheck + } else { + set multiple "" + } + $t add_row [list $arg [dict get $arginfo -type] $default $multiple $help] + if {$arg eq $badarg} { + $t configure_row [expr {[$t row_count]-1}] -ansibase $c_badarg + } + } + foreach arg [dict get $spec_dict val_names] { + set arginfo [dict get $spec_dict arg_info $arg] + if {[dict exists $arginfo -default]} { + set default [dict get $arginfo -default] + } else { + set default "" + } + set help [punk::lib::dict_getdef $arginfo -help ""] + if {[dict exists $arginfo -choices]} { + if {$help ne ""} {append help \n} + append help "Choices: [dict get $arginfo -choices]" + } + if {[punk::lib::dict_getdef $arginfo -multiple 0]} { + set multiple $greencheck + } else { + set multiple "" + } + $t add_row [list $arg [dict get $arginfo -type] $default $multiple $help] + if {$arg eq $badarg} { + $t configure_row [expr {[$t row_count]-1}] -ansibase $c_badarg + } + } + + + $t configure -show_hseps 0 -show_header 1 -ansibase_body [a+ web-white] -ansibase_header [a+ brightyellow] -ansiborder_header [a+ brightyellow] -ansiborder_body [a+ brightyellow] + $t configure -maxwidth 80 + append errmsg [$t print] + $t destroy + } errM]} { + catch {$t destroy} + append errmsg \n + append errmsg "(additional error in punk::args::arg_error when attempting to display usage)" \n + append errmsg "$errM" \n + append errmsg "$::errorInfo" + + } + } else { + #todo - something boring + } + #add PUNK to the tail end of the more usual -errorcode {TCL WRONGARGS} so we maintain reasonable compat with things looking for TCL WRONGARGS - but also differentiate it. + #Also,we're polite enough in the errorInfo, nothing wrong with a Clint Eastwood style errorCode ;) + return -code error -errorcode {TCL WRONGARGS PUNK} $errmsg + } + #generally we expect values to contain leading dashes only if -- specified. Otherwise no reliable way determine difference between bad flags and values #If no eopts (--) specified we stop looking for opts at the first nondash encountered in a position we'd expect a dash - so without eopt, values could contain dashes - but not in first position after flags. #only supports -flag val pairs, not solo options @@ -681,9 +880,12 @@ namespace eval punk::args { set argspecs [Get_argspecs $optionspecs] - dict with argspecs {} ;#turn keys into vars + tcl::dict::with argspecs {} ;#turn keys into vars #puts "-arg_info->$arg_info" - set flagsreceived [list] + set flagsreceived [list] ;#for checking if required flags satisfied + #secondary purpose: + #for -multple true, we need to ensure we can differentiate between a default value and a first of many that happens to match the default. + #-default value must not be appended to if argname not yet in flagsreceived set opts $opt_defaults if {[set eopts [lsearch -exact $rawargs "--"]] >= 0} { @@ -692,17 +894,80 @@ namespace eval punk::args { set maxidx [expr {[llength $arglist]-1}] for {set i 0} {$i <= $maxidx} {incr i} { set a [lindex $arglist $i] - if {![string match -* $a]} { + if {![tcl::string::match -* $a]} { #we can't treat as first positional arg - as it comes before the eopt indicator -- - error "punk::args::get_dict bad options for [Get_caller]. Expected flag (leading -) at position $i got:$rawargs" + arg_error "punk::args::get_dict bad options for [Get_caller]. Expected flag (leading -) at position $i got:$rawargs" $argspecs } - #TODO! - if {[dict get $arg_info $a -type] ne "none"} { - if {[incr i] > $maxidx} { - error "punk::args::get_dict bad options for [Get_caller]. No value supplied for last option $a which is not marked with -solo 1" + + if {![catch {tcl::prefix match -message "options for %caller%. Unexpected option" $opt_names $a } fullopt]} { + if {[tcl::dict::get $arg_info $fullopt -type] ne "none"} { + #non-solo + set flagval [lindex $arglist $i+1] + if {[dict get $arg_info $fullopt -multiple]} { + #don't lappend to default - we need to replace if there is a default + #review - what if user sets first value that happens to match a default? + if {$fullopt ni $flagsreceived && [tcl::dict::exists $opt_defaults $fullopt] && ([tcl::dict::get $opt_defaults $fullopt] eq [tcl::dict::get $opts $fullopt])} { + #first occurrence of this flag, whilst stored value matches default + tcl::dict::set opts $fullopt $flagval + } else { + tcl::dict::lappend opts $fullopt $flagval + } + } else { + tcl::dict::set opts $fullopt $flagval + } + #incr i to skip flagval + if {[incr i] > $maxidx} { + arg_error "punk::args::get_dict bad options for [Get_caller]. No value supplied for last option $fullopt at index [expr {$i-1}] which is not marked with -type none" $argspecs $fullopt + } + } else { + #type none (solo-flag) + if {[tcl::dict::get $arg_info $fullopt -multiple]} { + if {[tcl::dict::get $opts $fullopt] == 0} { + #review - what if default at time opt was specified is not zero? we will end up with more items in the list than the number of times the flag was specified + tcl::dict::set opts $fullopt 1 + } else { + tcl::dict::lappend opts $fullopt 1 + } + } else { + tcl::dict::set opts $fullopt 1 + } + } + lappend flagsreceived $fullopt ;#dups ok + } else { + if {$opt_any} { + set newval [lindex $arglist $i+1] + #opt was unspecified but is allowed due to *opt -any 1 - 'adhoc/passthrough' option + tcl::dict::set arg_info $a $optspec_defaults ;#use default settings for unspecified opt + tcl::dict::set arg_checks $a $opt_checks_defaults + if {[tcl::dict::get $arg_info $a -type] ne "none"} { + if {[tcl::dict::get $arg_info $a -multiple]} { + tcl::dict::lappend opts $a $newval + } else { + tcl::dict::set opts $a $newval + } + lappend flagsreceived $a ;#adhoc flag as supplied + if {[incr i] > $maxidx} { + arg_error "punk::args::get_dict bad options for [Get_caller]. No value supplied for last adhoc option $a at index [expr {$i-1}] which is not marked with -type none" $argspecs $a + } + } else { + #review -we can't provide a way to allow unspecified -type none flags through reliably/unambiguously unless all remaining unspecified options are -type none + if {[tcl::dict::get $arg_info $a -multiple]} { + if {![tcl::dict::exists $opts $a]} { + tcl::dict::set opts $a 1 + } else { + tcl::dict::lappend opts $a 1 + } + } else { + tcl::dict::set opts $a 1 + } + } + } else { + #delay Get_caller so only called in the unhappy path + set errmsg [tcl::string::map [list %caller% [Get_caller]] $fullopt] + arg_error $errmsg $argspecs $fullopt } } - lappend flagsreceived $a ;#dups ok + } } else { if {[lsearch $rawargs -*] >= 0} { @@ -714,36 +979,46 @@ namespace eval punk::args { set maxidx [expr {[llength $rawargs]-1}] for {set i 0} {$i <= $maxidx} {incr i} { set a [lindex $rawargs $i] - if {![string match -* $a]} { + #we can automatically rule out arguments containing whitespace from the set of simple flags beginning with a dash + #This helps for example when first value is a dict or list in which the first item happens to begin with a dash + #explicit -- still safer in many cases, but this is a reasonable and fast enough test + if {![tcl::string::match -* $a] || [regexp {\s+} $a]} { #assume beginning of positional args incr i -1 break } if {![catch {tcl::prefix match -message "options for %caller%. Unexpected option" $opt_names $a } fullopt]} { - if {[dict get $arg_info $fullopt -type] ne "none"} { + if {[tcl::dict::get $arg_info $fullopt -type] ne "none"} { #non-solo set flagval [lindex $rawargs $i+1] if {[dict get $arg_info $fullopt -multiple]} { - dict lappend opts $fullopt $flagval + #don't lappend to default - we need to replace if there is a default + #review - what if user sets first value that happens to match a default? + if {$fullopt ni $flagsreceived && [tcl::dict::exists $opt_defaults $fullopt] && ([tcl::dict::get $opt_defaults $fullopt] eq [tcl::dict::get $opts $fullopt])} { + #first occurrence of this flag, whilst stored value matches default + tcl::dict::set opts $fullopt $flagval + } else { + tcl::dict::lappend opts $fullopt $flagval + } } else { - dict set opts $fullopt $flagval + tcl::dict::set opts $fullopt $flagval } #incr i to skip flagval if {[incr i] > $maxidx} { - error "punk::args::get_dict bad options for [Get_caller]. No value supplied for last option $fullopt at index [expr {$i-1}] which is not marked with -type none" + arg_error "punk::args::get_dict bad options for [Get_caller]. No value supplied for last option $fullopt at index [expr {$i-1}] which is not marked with -type none" $argspecs $fullopt } } else { #type none (solo-flag) - if {[dict get $arg_info $fullopt -multiple]} { - if {[dict get $opts $fullopt] == 0} { + if {[tcl::dict::get $arg_info $fullopt -multiple]} { + if {[tcl::dict::get $opts $fullopt] == 0} { #review - what if default at time opt was specified is not zero? we will end up with more items in the list than the number of times the flag was specified - dict set opts $fullopt 1 + tcl::dict::set opts $fullopt 1 } else { - dict lappend opts $fullopt 1 + tcl::dict::lappend opts $fullopt 1 } } else { - dict set opts $fullopt 1 + tcl::dict::set opts $fullopt 1 } } lappend flagsreceived $fullopt ;#dups ok @@ -751,33 +1026,34 @@ namespace eval punk::args { if {$opt_any} { set newval [lindex $rawargs $i+1] #opt was unspecified but is allowed due to *opt -any 1 - 'adhoc/passthrough' option - dict set arg_info $a $optspec_defaults ;#use default settings for unspecified opt - if {[dict get $arg_info $a -type] ne "none"} { - if {[dict get $arg_info $a -multiple]} { - dict lappend opts $a $newval + tcl::dict::set arg_info $a $optspec_defaults ;#use default settings for unspecified opt + tcl::dict::set arg_checks $a $opt_checks_defaults + if {[tcl::dict::get $arg_info $a -type] ne "none"} { + if {[tcl::dict::get $arg_info $a -multiple]} { + tcl::dict::lappend opts $a $newval } else { - dict set opts $a $newval + tcl::dict::set opts $a $newval } lappend flagsreceived $a ;#adhoc flag as supplied if {[incr i] > $maxidx} { - error "punk::args::get_dict bad options for [Get_caller]. No value supplied for last adhoc option $a at index [expr {$i-1}] which is not marked with -type none" + arg_error "punk::args::get_dict bad options for [Get_caller]. No value supplied for last adhoc option $a at index [expr {$i-1}] which is not marked with -type none" $argspecs $a } } else { #review -we can't provide a way to allow unspecified -type none flags through reliably/unambiguously unless all remaining unspecified options are -type none - if {[dict get $arg_info $a -multiple]} { - if {![dict exists $opts $a]} { - dict set opts $a 1 + if {[tcl::dict::get $arg_info $a -multiple]} { + if {![tcl::dict::exists $opts $a]} { + tcl::dict::set opts $a 1 } else { - dict lappend opts $a 1 + tcl::dict::lappend opts $a 1 } } else { - dict set opts $a 1 + tcl::dict::set opts $a 1 } } } else { #delay Get_caller so only called in the unhappy path - set errmsg [string map [list %caller% [Get_caller]] $fullopt] - error $errmsg + set errmsg [tcl::string::map [list %caller% [Get_caller]] $fullopt] + arg_error $errmsg $argspecs $fullopt } } } @@ -800,20 +1076,26 @@ namespace eval punk::args { break } if {$valname ne ""} { - if {[dict get $arg_info $valname -multiple]} { - dict lappend values_dict $valname $val + if {[tcl::dict::get $arg_info $valname -multiple]} { + if {[tcl::dict::exists $val_defaults $valname] && ([tcl::dict::get $val_defaults $valname] eq [tcl::dict::get $values_dict $valname])} { + #current stored val equals defined default - don't include default in the list we build up + tcl::dict::set values_dict $valname $val + } else { + tcl::dict::lappend values_dict $valname $val + } set in_multiple $valname } else { - dict set values_dict $valname $val + tcl::dict::set values_dict $valname $val } lappend valnames_received $valname } else { if {$in_multiple ne ""} { - dict lappend values_dict $in_multiple $val + tcl::dict::lappend values_dict $in_multiple $val #name already seen } else { - dict set values_dict $validx $val - dict set arg_info $validx $valspec_defaults + tcl::dict::set values_dict $validx $val + tcl::dict::set arg_info $validx $valspec_defaults + tcl::dict::set arg_checks $validx $val_checks_defaults lappend valnames_received $validx } } @@ -823,14 +1105,14 @@ namespace eval punk::args { if {$val_max == -1} { #only check min if {$num_values < $val_min} { - error "bad number of trailing values for [Get_caller]. Got $num_values values. Expected at least $val_min" + arg_error "bad number of trailing values for [Get_caller]. Got $num_values values. Expected at least $val_min" $argspecs } } else { if {$num_values < $val_min || $num_values > $val_max} { if {$val_min == $val_max} { - error "bad number of trailing values for [Get_caller]. Got $num_values values. Expected exactly $val_min" + arg_error "bad number of trailing values for [Get_caller]. Got $num_values values. Expected exactly $val_min" $argspecs } else { - error "bad number of trailing values for [Get_caller]. Got $num_values values. Expected between $val_min and $val_max inclusive" + arg_error "bad number of trailing values for [Get_caller]. Got $num_values values. Expected between $val_min and $val_max inclusive" $argspecs } } } @@ -844,33 +1126,48 @@ namespace eval punk::args { #The aim is to allow a wrapper function to specify a default value for an option (and/or other changes/restrictions to a particular option or two) - without having to re-specify all the options for the underlying function. #without full respecification in the wrapper - we can't know that a supplied prefix is unambiguous at the next level #For this reason we need to pass on full-length opts for any defined options in the wrapper even if anyopts is true - if {[llength [set missing [struct::set difference $opt_required $flagsreceived]]]} { - error "Required option missing for [Get_caller]. missing flags $missing are marked with -optional false - so must be present in full-length form" + + #safe interp note - cannot avoid struct::set difference ensemble as it could be c or tcl implementation and we don't have an option to call directly? + #example timing difference: + #struct::set difference {x} {a b} + #normal interp 0.18 u2 vs safe interp 9.4us + #if {[llength [set missing [struct::set difference $opt_required $flagsreceived]]]} { + # error "Required option missing for [Get_caller]. missing flags $missing are marked with -optional false - so must be present in full-length form" + #} + #if {[llength [set missing [struct::set difference $val_required $valnames_received]]]} { + # error "Required value missing for [Get_caller]. missing values $missing marked with -optional false - so must be present" + #} + #for now (2024-06) punk::lib::ldiff is a better compromise across normal/safe interps e.g 0.7/0.8us + if {[llength [set missing [punk::lib::ldiff $opt_required $flagsreceived]]]} { + arg_error "Required option missing for [Get_caller]. missing flags: '$missing' are marked with -optional false - so must be present in full-length form" $argspecs } - if {[llength [set missing [struct::set difference $val_required $valnames_received]]]} { - error "Required value missing for [Get_caller]. missing values $missing marked with -optional false - so must be present" + if {[llength [set missing [punk::lib::ldiff $val_required $valnames_received]]]} { + arg_error "Required value missing for [Get_caller]. missing values: '$missing' marked with -optional false - so must be present" $argspecs } + + + #todo - allow defaults outside of choices/ranges #check types,ranges,choices - set opts_and_values [dict merge $opts $values_dict] - #set combined_defaults [dict merge $val_defaults $opt_defaults] ;#can be no key collisions - we don't allow a value key beginning with dash - opt names must begin with dash + set opts_and_values [tcl::dict::merge $opts $values_dict] + #set combined_defaults [tcl::dict::merge $val_defaults $opt_defaults] ;#can be no key collisions - we don't allow a value key beginning with dash - opt names must begin with dash #puts "---opts_and_values:$opts_and_values" #puts "---arg_info:$arg_info" - dict for {argname v} $opts_and_values { - set thisarg [dict get $arg_info $argname] - #set thisarg_keys [dict keys $thisarg] - set thisarg_checks [dict get $arg_checks $argname] - set is_multiple [dict get $thisarg -multiple] - set is_allow_ansi [dict get $thisarg -allow_ansi] - set is_validate_without_ansi [dict get $thisarg -validate_without_ansi] - set is_strip_ansi [dict get $thisarg -strip_ansi] - set has_default [dict exists $thisarg -default] + tcl::dict::for {argname v} $opts_and_values { + set thisarg [tcl::dict::get $arg_info $argname] + #set thisarg_keys [tcl::dict::keys $thisarg] + set thisarg_checks [tcl::dict::get $arg_checks $argname] + set is_multiple [tcl::dict::get $thisarg -multiple] + set is_allow_ansi [tcl::dict::get $thisarg -allow_ansi] + set is_validate_without_ansi [tcl::dict::get $thisarg -validate_without_ansi] + set is_strip_ansi [tcl::dict::get $thisarg -strip_ansi] + set has_default [tcl::dict::exists $thisarg -default] if {$has_default} { - set defaultval [dict get $thisarg -default] + set defaultval [tcl::dict::get $thisarg -default] } - set type [dict get $thisarg -type] - set has_choices [dict exists $thisarg -choices] + set type [tcl::dict::get $thisarg -type] + set has_choices [tcl::dict::exists $thisarg -choices] if {$is_multiple} { set vlist $v @@ -880,6 +1177,7 @@ namespace eval punk::args { if {!$is_allow_ansi} { #allow_ansi 0 package require punk::ansi + #do not run ta::detect on a list foreach e $vlist { if {[punk::ansi::ta::detect $e]} { error "Option $argname for [Get_caller] contains ansi - but -allow_ansi is false. Received: '$e'" @@ -912,24 +1210,27 @@ namespace eval punk::args { #puts "argname:$argname v:$v is_default:$is_default" #we want defaults to pass through - even if they don't pass the checks that would be required for a specified value #If the caller manually specified a value that happens to match the default - we don't detect that as any different from an unspecified value - Review. + #arguments that are at their default are not subject to type and other checks if {$is_default == 0} { switch -- $type { any {} string { - if {[dict size $thisarg_checks]} { + if {[tcl::dict::size $thisarg_checks]} { foreach e_check $vlist_check { - dict for {checkopt checkval} $thisarg_checks { + #safe jumptable test + #dict for {checkopt checkval} $thisarg_checks {} + tcl::dict::for {checkopt checkval} $thisarg_checks { switch -- $checkopt { -minlen { # -1 for disable is as good as zero - if {[string length $e_check] < $checkval} { - error "Option $argname for [Get_caller] requires string with -minlen $checkval. Received len:[string length $e_check] value:'$e_check'" + if {[tcl::string::length $e_check] < $checkval} { + arg_error "Option $argname for [Get_caller] requires string with -minlen $checkval. Received len:[tcl::string::length $e_check] value:'$e_check'" $argspecs $argname } } -maxlen { if {$checkval ne "-1"} { - if {[string length $e_check] > $checkval} { - error "Option $argname for [Get_caller] requires string with -maxlen $checkval. Received len:[string length $e_check] value:'$e_check'" + if {[tcl::string::length $e_check] > $checkval} { + arg_error "Option $argname for [Get_caller] requires string with -maxlen $checkval. Received len:[tcl::string::length $e_check] value:'$e_check'" $argspecs $argname } } } @@ -942,37 +1243,39 @@ namespace eval punk::args { package require ansi } int { - if {[dict exists $thisarg -range]} { - lassign [dict get $thisarg -range] low high + if {[tcl::dict::exists $thisarg -range]} { + lassign [tcl::dict::get $thisarg -range] low high foreach e $vlist e_check $vlist_check { - if {![string is integer -strict $e_check]} { - error "Option $argname for [Get_caller] requires type 'integer'. Received: '$e'" + if {![tcl::string::is integer -strict $e_check]} { + arg_error "Option $argname for [Get_caller] requires type 'integer'. Received: '$e'" $argspecs $argname } if {$e_check < $low || $e_check > $high} { - error "Option $argname for [Get_caller] must be integer between $low and $high. Received: '$e'" + arg_error "Option $argname for [Get_caller] must be integer between $low and $high. Received: '$e'" $argspecs $argname } } } else { foreach e_check $vlist_check { - if {![string is integer -strict $e_check]} { - error "Option $argname for [Get_caller] requires type 'integer'. Received: '$e_check'" + if {![tcl::string::is integer -strict $e_check]} { + arg_error "Option $argname for [Get_caller] requires type 'integer'. Received: '$e_check'" $argspecs $argname } } } } double { foreach e $vlist e_check $vlist_check { - if {![string is double -strict $e_check]} { + if {![tcl::string::is double -strict $e_check]} { error "Option $argname for [Get_caller] requires type 'double'. Received: '$e'" } - if {[dict size $thisarg_checks]} { - dict for {checkopt checkval} $thisarg_checks { + if {[tcl::dict::size $thisarg_checks]} { + #safe jumptable test + #dict for {checkopt checkval} $thisarg_checks {} + tcl::dict::for {checkopt checkval} $thisarg_checks { switch -- $checkopt { -range { #todo - small-value double comparisons with error-margin? review lassign $checkval low high if {$e_check < $low || $e_check > $high} { - error "Option $argname for [Get_caller] must be between $low and $high. Received: '$e'" + arg_error "Option $argname for [Get_caller] must be between $low and $high. Received: '$e'" $argspecs $argname } } } @@ -982,8 +1285,15 @@ namespace eval punk::args { } bool { foreach e_check $vlist_check { - if {![string is boolean -strict $e_check]} { - error "Option $argname for [Get_caller] requires type 'boolean'. Received: '$e_check'" + if {![tcl::string::is boolean -strict $e_check]} { + arg_error "Option $argname for [Get_caller] requires type 'boolean'. Received: '$e_check'" $argspecs $argname + } + } + } + dict { + foreach e_check $vlist_check { + if {[llength $e_check] %2 != 0} { + arg_error "Option $argname for [Get_caller] requires type 'dict' - must be key value pairs. Received: '$e_check'" $argspecs $argname } } } @@ -1001,8 +1311,8 @@ namespace eval punk::args { wordchar - xdigit { foreach e $vlist e_check $vlist_check { - if {![string is $type $e_check]} { - error "Option $argname for [Get_caller] requires type '$type'. Received: '$e'" + if {![tcl::string::is $type $e_check]} { + arg_error "Option $argname for [Get_caller] requires type '$type'. Received: '$e'" $argspecs $argname } } } @@ -1011,73 +1321,73 @@ namespace eval punk::args { existingfile - existingdirectory { foreach e $vlist e_check $vlist_check { - if {!([string length $e_check]>0 && ![regexp {[\"*?<>\;]} $e_check])} { + if {!([tcl::string::length $e_check]>0 && ![regexp {[\"*?<>\;]} $e_check])} { #what about special file names e.g on windows NUL ? - error "Option $argname for [Get_caller] requires type '$type'. Received: '$e' which doesn't look like it could be a file or directory" + arg_error "Option $argname for [Get_caller] requires type '$type'. Received: '$e' which doesn't look like it could be a file or directory" $argspecs $argname } } if {$type eq "existingfile"} { foreach e $vlist e_check $vlist_check { if {![file exists $e_check]} { - error "Option $argname for [Get_caller] requires type '$type'. Received: '$e' which is not an existing file" + arg_error "Option $argname for [Get_caller] requires type '$type'. Received: '$e' which is not an existing file" $argspecs $argname } } } elseif {$type eq "existingdirectory"} { foreach e $vlist e_check $vlist_check { if {![file isdirectory $e_check]} { - error "Option $argname for [Get_caller] requires type '$type'. Received: '$e' which is not an existing directory" + arg_error "Option $argname for [Get_caller] requires type '$type'. Received: '$e' which is not an existing directory" $argspecs $argname } } } } char { foreach e $vlist e_check $vlist_check { - if {[string length $e_check] != 1} { - error "Option $argname for [Get_caller] requires type 'character'. Received: '$e' which is not a single character" + if {[tcl::string::length $e_check] != 1} { + arg_error "Option $argname for [Get_caller] requires type 'character'. Received: '$e' which is not a single character" $argspecs $argname } } } } if {$has_choices} { #todo -choicelabels - set choices [dict get $thisarg -choices] - set nocase [dict get $thisarg -nocase] + set choices [tcl::dict::get $thisarg -choices] + set nocase [tcl::dict::get $thisarg -nocase] foreach e $vlist e_check $vlist_check { if {$nocase} { set casemsg "(case insensitive)" - set choices_test [string tolower $choices] - set v_test [string tolower $e_check] + set choices_test [tcl::string::tolower $choices] + set v_test [tcl::string::tolower $e_check] } else { set casemsg "(case sensitive)" set v_test $e_check set choices_test $choices } if {$v_test ni $choices_test} { - error "Option $argname for [Get_caller] must be one of the listed values $choices $casemsg. Received: '$e'" + arg_error "Option $argname for [Get_caller] must be one of the listed values $choices $casemsg. Received: '$e'" $argspecs $argname } } } } if {$is_strip_ansi} { set stripped_list [lmap e $vlist {punk::ansi::stripansi $e}] ;#no faster or slower, but more concise than foreach - if {[dict get $thisarg -multiple]} { - if {[dict get $thisarg -ARGTYPE] eq "option"} { - dict set opts $argname $stripped_list + if {[tcl::dict::get $thisarg -multiple]} { + if {[tcl::dict::get $thisarg -ARGTYPE] eq "option"} { + tcl::dict::set opts $argname $stripped_list } else { - dict set values_dict $argname $stripped_list + tcl::dict::set values_dict $argname $stripped_list } } else { - if {[dict get $thisarg -ARGTYPE] eq "option"} { - dict set opts $argname [lindex $stripped_list 0] + if {[tcl::dict::get $thisarg -ARGTYPE] eq "option"} { + tcl::dict::set opts $argname [lindex $stripped_list 0] } else { - dict set values_dict [lindex $stripped_list 0] + tcl::dict::set values_dict [lindex $stripped_list 0] } } } } #maintain order of opts $opts values $values as caller may use lassign. - return [dict create opts $opts values $values_dict] + return [tcl::dict::create opts $opts values $values_dict] } #proc sample1 {p1 args} { @@ -1099,9 +1409,9 @@ namespace eval punk::args { # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # Secondary API namespace # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -namespace eval punk::args::lib { - namespace export * - namespace path [namespace parent] +tcl::namespace::eval punk::args::lib { + tcl::namespace::export * + tcl::namespace::path [tcl::namespace::parent] #*** !doctools #[subsection {Namespace punk::args::lib}] #[para] Secondary functions that are part of the API @@ -1126,7 +1436,7 @@ namespace eval punk::args::lib { # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ #*** !doctools #[section Internal] -namespace eval punk::args::system { +tcl::namespace::eval punk::args::system { #*** !doctools #[subsection {Namespace punk::args::system}] #[para] Internal functions that are not part of the API @@ -1136,7 +1446,7 @@ namespace eval punk::args::system { } # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ ## Ready -package provide punk::args [namespace eval punk::args { +package provide punk::args [tcl::namespace::eval punk::args { variable pkg punk::args variable version set version 0.1.0 diff --git a/src/bootsupport/modules/punk/assertion-0.1.0.tm b/src/bootsupport/modules/punk/assertion-0.1.0.tm index 64ecbbd..bee5a41 100644 --- a/src/bootsupport/modules/punk/assertion-0.1.0.tm +++ b/src/bootsupport/modules/punk/assertion-0.1.0.tm @@ -69,11 +69,11 @@ package require Tcl 8.6- # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # oo::class namespace # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -namespace eval punk::assertion::class { +tcl::namespace::eval punk::assertion::class { #*** !doctools #[subsection {Namespace punk::assertion::class}] #[para] class definitions - if {[info commands [namespace current]::interface_sample1] eq ""} { + if {[tcl::info::commands [tcl::namespace::current]::interface_sample1] eq ""} { #*** !doctools #[list_begin enumerated] @@ -100,16 +100,16 @@ namespace eval punk::assertion::class { # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ #keep 2 namespaces for assertActive and assertInactive so there is introspection available via namespace origin -namespace eval punk::assertion::primary { - - namespace export * +tcl::namespace::eval punk::assertion::primary { + #tcl::namespace::export {[a-z]*} + tcl::namespace::export assertActive assertInactive proc assertActive {expr args} { set code [catch {uplevel 1 [list expr $expr]} res] if {$code} { return -code $code $res } - if {![string is boolean -strict $res]} { + if {![tcl::string::is boolean -strict $res]} { return -code error "invalid boolean expression: $expr" } @@ -124,28 +124,40 @@ namespace eval punk::assertion::primary { upvar ::punk::assertion::CallbackCmd CallbackCmd # Might want to catch this - namespace eval :: $CallbackCmd [list $msg] + tcl::namespace::eval :: $CallbackCmd [list $msg] } proc assertInactive args {} } -namespace eval punk::assertion::secondary { - namespace export * +tcl::namespace::eval punk::assertion::secondary { + tcl::namespace::export * #we need to actually define these procs here, (not import then re-export) - or namespace origin will report the original source namespace - which isn't what we want. - proc assertActive {expr args} [info body ::punk::assertion::primary::assertActive] + proc assertActive {expr args} [tcl::info::body ::punk::assertion::primary::assertActive] proc assertInactive args {} } - # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # Base namespace # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -namespace eval punk::assertion { +tcl::namespace::eval punk::assertion { variable CallbackCmd [list return -code error] - namespace import ::punk::assertion::primary::assertActive + + #puts --------AAA + #*very* slow in safe interp - why? + #tcl::namespace::import ::punk::assertion::primary::assertActive + + proc do_ns_import {} { + uplevel 1 [list tcl::namespace::import ::punk::assertion::primary::assertActive] + } + do_ns_import + #puts --------BBB rename assertActive assert - namespace export * +} + + +tcl::namespace::eval punk::assertion { + tcl::namespace::export * #variable xyz #*** !doctools @@ -177,7 +189,7 @@ namespace eval punk::assertion { set n [llength $args] if {$n > 1} { return -code error "wrong # args: should be\ - \"[lindex [info level 0] 0] ?command?\"" + \"[lindex [tcl::info::level 0] 0] ?command?\"" } if {$n} { set cb [lindex $args 0] @@ -187,41 +199,41 @@ namespace eval punk::assertion { } proc active {{on_off ""}} { - set nscaller [uplevel 1 [list namespace current]] - set which_assert [namespace eval $nscaller {namespace which assert}] + set nscaller [uplevel 1 [list tcl::namespace::current]] + set which_assert [tcl::namespace::eval $nscaller {tcl::namespace::which assert}] #puts "nscaller:'$nscaller'" #puts "which_assert: $which_assert" if {$on_off eq ""} { if {$which_assert eq ""} {return 0} - set assertorigin [namespace origin $which_assert] + set assertorigin [tcl::namespace::origin $which_assert] #puts "ns which assert: $which_assert" #puts "ns origin assert: $assertorigin" - return [expr {"assertActive" eq [namespace tail $assertorigin]}] + return [expr {"assertActive" eq [tcl::namespace::tail $assertorigin]}] } - if {![string is boolean -strict $on_off]} { + if {![tcl::string::is boolean -strict $on_off]} { error "invalid boolean value : $on_off" } else { - set info_command [namespace eval $nscaller {info commands assert}] + set info_command [tcl::namespace::eval $nscaller {tcl::info::commands assert}] if {$on_off} { #Enable it in calling namespace if {"assert" eq $info_command} { #There is an assert command reachable - due to namespace path etc, it could be in another namespace entirely - (not necessarily in an ancestor namespace of the namespace's tree structure) if {$which_assert eq [punk::assertion::system::nsjoin ${nscaller} assert]} { - namespace eval $nscaller { - set assertorigin [namespace origin assert] + tcl::namespace::eval $nscaller { + set assertorigin [tcl::namespace::origin assert] set assertorigin_ns [punk::assertion::system::nsprefix $assertorigin] switch -- $assertorigin_ns { ::punk::assertion { #original import - switch to primary origin rename assert {} - namespace import ::punk::assertion::primary::assertActive + tcl::namespace::import ::punk::assertion::primary::assertActive rename assertActive assert } ::punk::assertion::primary - ::punk::assertion::secondary { #keep using from same origin ns rename assert {} - namespace import ${assertorigin_ns}::assertActive + tcl::namespace::import ${assertorigin_ns}::assertActive rename assertActive assert } default { @@ -232,10 +244,10 @@ namespace eval punk::assertion { return 1 } else { #assert is available, but isn't in the calling namespace - we should enable it in a way that is distinguishable from case where assert was explicitly imported to this namespace - namespace eval $nscaller { - set assertorigin [namespace origin assert] - if {[string match ::punk::assertion::* $assertorigin]} { - namespace import ::punk::assertion::secondary::assertActive + tcl::namespace::eval $nscaller { + set assertorigin [tcl::namespace::origin assert] + if {[tcl::string::match ::punk::assertion::* $assertorigin]} { + tcl::namespace::import ::punk::assertion::secondary::assertActive rename assertActive assert } else { error "The reachable assert command at '$which_assert' is not from punk::assertion package. Import punk::assertion::assert - or use the enable mechanism from the package associated with $assertorigin" @@ -254,20 +266,20 @@ namespace eval punk::assertion { if {"assert" eq $info_command} { if {$which_assert eq [punk::assertion::system::nsjoin ${nscaller} assert]} { #assert is present in callers NS - namespace eval $nscaller { - set assertorigin [namespace origin assert] + tcl::namespace::eval $nscaller { + set assertorigin [tcl::namespace::origin assert] set assertorigin_ns [punk::assertion::system::nsprefix $assertorigin] switch -glob -- $assertorigin_ns { ::punk::assertion { #original import rename assert {} - namespace import punk::assertion::primary::assertInactive + tcl::namespace::import punk::assertion::primary::assertInactive rename assertInactive assert } ::punk::assertion::primary - ::punk::assertion::secondary { #keep using from same origin ns rename assert {} - namespace import ${assertorigin_ns}::assertInactive + tcl::namespace::import ${assertorigin_ns}::assertInactive rename assertInactive assert } default { @@ -278,11 +290,11 @@ namespace eval punk::assertion { return 0 } else { #assert not present in callers NS - first install of secondary (if assert is from punk::assertion::*) - namespace eval $nscaller { - set assertorigin [namespace origin assert] + tcl::namespace::eval $nscaller { + set assertorigin [tcl::namespace::origin assert] set assertorigin_ns [punk::assertion::system::nsprefix $assertorigin] - if {[string match ::punk::assertion::* $assertorigin]} { - namespace import ::punk::assertion::secondary::assertInactive + if {[tcl::string::match ::punk::assertion::* $assertorigin]} { + tcl::namespace::import ::punk::assertion::secondary::assertInactive rename assertInactive assert } else { error "The reachable assert command at '$which_assert' is not from punk::assertion package. Import punk::assertion::assert - or use the enable mechanism from the package associated with $assertorigin" @@ -310,9 +322,9 @@ namespace eval punk::assertion { # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # Secondary API namespace # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -namespace eval punk::assertion::lib { - namespace export * - namespace path [namespace parent] +tcl::namespace::eval punk::assertion::lib { + tcl::namespace::export * + tcl::namespace::path [tcl::namespace::parent] #*** !doctools #[subsection {Namespace punk::assertion::lib}] #[para] Secondary functions that are part of the API @@ -337,7 +349,7 @@ namespace eval punk::assertion::lib { # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ #*** !doctools #[section Internal] -namespace eval punk::assertion::system { +tcl::namespace::eval punk::assertion::system { #*** !doctools #[subsection {Namespace punk::assertion::system}] #[para] Internal functions that are not part of the API @@ -346,33 +358,33 @@ namespace eval punk::assertion::system { #nsprefix/nstail are string functions - they do not concern themselves with what namespaces are present in the system proc nsprefix {{nspath {}}} { #normalize the common case of :::: - set nspath [string map [list :::: ::] $nspath] - set rawprefix [string range $nspath 0 end-[string length [nstail $nspath]]] + set nspath [tcl::string::map [list :::: ::] $nspath] + set rawprefix [tcl::string::range $nspath 0 end-[tcl::string::length [nstail $nspath]]] if {$rawprefix eq "::"} { return $rawprefix } else { - if {[string match *:: $rawprefix]} { - return [string range $rawprefix 0 end-2] + if {[tcl::string::match *:: $rawprefix]} { + return [tcl::string::range $rawprefix 0 end-2] } else { return $rawprefix } - #return [string trimright $rawprefix :] + #return [tcl::string::trimright $rawprefix :] } } #see also punk::ns - keep in sync proc nstail {nspath args} { #normalize the common case of :::: - set nspath [string map [list :::: ::] $nspath] - set mapped [string map [list :: \u0FFF] $nspath] + set nspath [tcl::string::map [list :::: ::] $nspath] + set mapped [tcl::string::map [list :: \u0FFF] $nspath] set parts [split $mapped \u0FFF] set defaults [list -strict 0] - set opts [dict merge $defaults $args] - set strict [dict get $opts -strict] + set opts [tcl::dict::merge $defaults $args] + set strict [tcl::dict::get $opts -strict] if {$strict} { foreach p $parts { - if {[string match :* $p]} { + if {[tcl::string::match :* $p]} { error "nstail unpaired colon ':' in $nspath" } } @@ -381,7 +393,7 @@ namespace eval punk::assertion::system { return [lindex $parts end] } proc nsjoin {prefix name} { - if {[string match ::* $name]} { + if {[tcl::string::match ::* $name]} { if {"$prefix" ne ""} { error "nsjoin: won't join non-empty prefix to absolute namespace path '$name'" } @@ -400,7 +412,7 @@ namespace eval punk::assertion::system { } # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ ## Ready -package provide punk::assertion [namespace eval punk::assertion { +package provide punk::assertion [tcl::namespace::eval punk::assertion { variable pkg punk::assertion variable version set version 0.1.0 diff --git a/src/bootsupport/modules/punk/cap-0.1.0.tm b/src/bootsupport/modules/punk/cap-0.1.0.tm index 8488bbc..68d3252 100644 --- a/src/bootsupport/modules/punk/cap-0.1.0.tm +++ b/src/bootsupport/modules/punk/cap-0.1.0.tm @@ -48,12 +48,12 @@ package require oolib # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -namespace eval punk::cap { - variable pkgcapsdeclared [dict create] - variable pkgcapsaccepted [dict create] - variable caps [dict create] +tcl::namespace::eval punk::cap { + variable pkgcapsdeclared [tcl::dict::create] + variable pkgcapsaccepted [tcl::dict::create] + variable caps [tcl::dict::create] namespace eval class { - if {[info commands [namespace current]::interface_caphandler.registry] eq ""} { + if {[tcl::info::commands ::punk::cap::class::interface_caphandler.registry] eq ""} { #*** !doctools #[subsection {Namespace punk::cap::class}] #[para] class definitions @@ -62,7 +62,7 @@ namespace eval punk::cap { # [para] [emph {handler_classes}] # [list_begin enumerated] - oo::class create [namespace current]::interface_caphandler.registry { + oo::class create ::punk::cap::class::interface_caphandler.registry { #*** !doctools #[enum] CLASS [class interface_caphandler.registry] #[list_begin definitions] @@ -83,7 +83,7 @@ namespace eval punk::cap { #[list_end] } - oo::class create [namespace current]::interface_caphandler.sysapi { + oo::class create ::punk::cap::class::interface_caphandler.sysapi { #*** !doctools #[enum] CLASS [class interface_caphandler.sysapi] #[list_begin definitions] @@ -103,7 +103,7 @@ namespace eval punk::cap { # [list_begin enumerated] #Provider classes - oo::class create [namespace current]::interface_capprovider.registration { + oo::class create ::punk::cap::class::interface_capprovider.registration { #*** !doctools # [enum] CLASS [class interface_cappprovider.registration] # [para]Your provider package will need to instantiate this object under a sub-namespace called [namespace capsystem] within your package namespace. @@ -140,7 +140,7 @@ namespace eval punk::cap { # [list_end] } - oo::class create [namespace current]::interface_capprovider.provider { + oo::class create ::punk::cap::class::interface_capprovider.provider { #*** !doctools # [enum] CLASS [class interface_capprovider.provider] # [para] Your provider package will need to instantiate this directly under it's own namespace with the command name of [emph {provider}] @@ -157,7 +157,7 @@ namespace eval punk::cap { #*** !doctools #[call class::interface_capprovider.provider [method constructor] [arg providerpkg]] variable provider_pkg - if {$providerpkg in [list "" "::"]} { + if {$providerpkg in {"" "::"}} { error "interface_capprovider.provider constructor error. Invalid provider '$providerpkg'" } if {![namespace exists ::$providerpkg]} { @@ -165,12 +165,12 @@ namespace eval punk::cap { } set registrationobj ::${providerpkg}::capsystem::capprovider.registration - if {[info commands $registrationobj] eq ""} { + if {[tcl::info::commands $registrationobj] eq ""} { error "capprovider.provider constructor error. Missing capprovider.registration interface at '$obj' (command not found) interface_capprovider.regstration instantiation must precede interface_capprovider.provider" } - set provider_pkg [string trim $providerpkg ""] - + #review - what are we trying to achieve here? + set provider_pkg [tcl::string::trim $providerpkg ""] } method register {{capabilityname_glob *}} { #*** !doctools @@ -232,13 +232,13 @@ namespace eval punk::cap { #such unregistered capabilitynames may be used just to flag something, or have datamembers significant to callers cooperatively interested in that capname. #we allow registering a capability with an empty handler (capnamespace) - but this means another handler could be registered later. proc register_capabilityname {capname capnamespace} { - puts stderr "REGISTER_CAPABILITYNAME $capname $capnamespace" + #puts stderr "REGISTER_CAPABILITYNAME $capname $capnamespace" variable caps variable pkgcapsdeclared variable pkgcapsaccepted if {$capnamespace ne ""} { #normalize with leading :: in case caller passed in package name rather than fully qualified namespace - if {![string match ::* $capnamespace]} { + if {![tcl::string::match ::* $capnamespace]} { set capnamespace ::$capnamespace } } @@ -250,20 +250,21 @@ namespace eval punk::cap { return } #assertion: capnamespace may or may not be empty string, capname may or may not already exist in caps dict, caps $capname providers may have existing entries. - dict set caps $capname handler $capnamespace - if {![dict exists $caps $capname providers]} { - dict set caps $capname providers [list] + tcl::dict::set caps $capname handler $capnamespace + if {![tcl::dict::exists $caps $capname providers]} { + tcl::dict::set caps $capname providers [list] } - if {[llength [set providers [dict get $caps $capname providers]]]} { + if {[llength [set providers [tcl::dict::get $caps $capname providers]]]} { #some provider(s) were in place before the handler was registered if {[set capreg [punk::cap::capsystem::get_caphandler_registry $capname]] ne ""} { foreach pkg $providers { - set fullcapabilitylist [dict get $pkgcapsdeclared $pkg] - foreach capspec $fullcapabilitylist { + set fullcapabilitylist [tcl::dict::get $pkgcapsdeclared $pkg] + set capname_capabilitylist [lsearch -all -inline -index 0 $fullcapabilitylist $capname] + foreach capspec $capname_capabilitylist { lassign $capspec cn capdict - if {$cn ne $capname} { - continue - } + #if {$cn ne $capname} { + # continue + #} if {[catch {$capreg pkg_register $pkg $capdict $fullcapabilitylist} do_register]} { puts stderr "punk::cap::register_capabilityname '$capname' '$capnamespace' failed to register provider package '$pkg' - possible error in handler or provider" puts stderr "error message:" @@ -271,22 +272,22 @@ namespace eval punk::cap { set do_register 0 } - set list_accepted [dict get $pkgcapsaccepted $pkg] + set list_accepted [tcl::dict::get $pkgcapsaccepted $pkg] if {$do_register} { if {$capspec ni $list_accepted} { - dict lappend pkgcapsaccepted $pkg $capspec + tcl::dict::lappend pkgcapsaccepted $pkg $capspec } } else { set posn [lsearch $list_accepted $capspec] if {$posn >=0} { set list_accepted [lreplace $list_accepted $posn $posn] - dict set pkgcapsaccepted $pkg $list_accepted + tcl::dict::set pkgcapsaccepted $pkg $list_accepted } } } #check if any accepted for this cap and remove from caps as necessary set count 0 - foreach accepted_capspec [dict get $pkgcapsaccepted $pkg] { + foreach accepted_capspec [tcl::dict::get $pkgcapsaccepted $pkg] { if {[lindex $accepted_capspec 0] eq $capname} { incr count } @@ -295,7 +296,7 @@ namespace eval punk::cap { set pkgposn [lsearch $providers $pkg] if {$pkgposn >= 0} { set updated_providers [lreplace $providers $posn $posn] - dict set caps $capname providers $updated_providers + tcl::dict::set caps $capname providers $updated_providers } } } @@ -309,14 +310,14 @@ namespace eval punk::cap { # [call [fun capability_exists] [arg capname]] # Return a boolean indicating if the named capability exists (0|1) variable caps - return [dict exists $caps $capname] + return [tcl::dict::exists $caps $capname] } proc capability_has_handler {capname} { #*** !doctools # [call [fun capability_has_handler] [arg capname]] #Return a boolean indicating if the named capability has a handler package installed (0|1) variable caps - return [expr {[dict exists $caps $capname handler] && [dict get $caps $capname handler] ne ""}] + return [expr {[tcl::dict::exists $caps $capname handler] && [tcl::dict::get $caps $capname handler] ne ""}] } proc capability_get_handler {capname} { #*** !doctools @@ -324,8 +325,8 @@ namespace eval punk::cap { #Return the base namespace of the active handler package for the named capability. #[para] The base namespace for a handler will always be the package name, but prefixed with :: variable caps - if {[dict exists $caps $capname]} { - return [dict get $caps $capname handler] + if {[tcl::dict::exists $caps $capname]} { + return [tcl::dict::get $caps $capname handler] } return "" } @@ -338,8 +339,8 @@ namespace eval punk::cap { } proc get_providers {capname} { variable caps - if {[dict exists $caps $capname]} { - return [dict get $caps $capname providers] + if {[tcl::dict::exists $caps $capname]} { + return [tcl::dict::get $caps $capname providers] } return [list] } @@ -356,26 +357,26 @@ namespace eval punk::cap { foreach {k v} $args { switch -- $k { -nowarnings { - dict set opts $k $v + tcl::dict::set opts $k $v } default { - error "Unrecognized option $k. Known options [dict keys $opts]" + error "Unrecognized option $k. Known options [tcl::dict::keys $opts]" } } } - set warnings [expr {! [dict get $opts -nowarnings]}] + set warnings [expr {! [tcl::dict::get $opts -nowarnings]}] - if {[string match ::* $pkg]} { - set pkg [string range $pkg 2 end] + if {[tcl::string::match ::* $pkg]} { + set pkg [tcl::string::range $pkg 2 end] } - if {[dict exists $pkgcapsaccepted $pkg]} { - set pkg_already_accepted [dict get $pkgcapsaccepted $pkg] + if {[tcl::dict::exists $pkgcapsaccepted $pkg]} { + set pkg_already_accepted [tcl::dict::get $pkgcapsaccepted $pkg] } else { set pkg_already_accepted [list] } package require $pkg set providerapi ::${pkg}::provider - if {[info commands $providerapi] eq ""} { + if {[tcl::info::commands $providerapi] eq ""} { error "register_package error. pkg '$pkg' doesn't seem to be a punk::cap capability provider (no object found at $providerapi)" } set defined_caps [$providerapi capabilities] @@ -397,13 +398,13 @@ namespace eval punk::cap { if {[llength $capname] !=1} { puts stderr "register_package error. pkg: '$pkg' An entry in the capability list doesn't appear to have a single-word name. Problematic entry:'$capspec'" set reason "First element of capspec not a single-word name" - lappend errorlist [dict create msg $reason capspec $capspec] + lappend errorlist [tcl::dict::create msg $reason capspec $capspec] continue } if {[expr {[llength $capdict] %2 != 0}]} { puts stderr "register_package error. pkg:'$pkg' The second element for capname:'$capname' doesn't appear to be a valid dict. Problematic entry: '$capspec'" set reason "The second element of the capspec isn't a valid dict" - lappend errorlist [dict create msg $reason capspec $capspec] + lappend errorlist [tcl::dict::create msg $reason capspec $capspec] continue } if {$capspec in $pkg_already_accepted} { @@ -411,13 +412,13 @@ namespace eval punk::cap { if {$warnings} { puts stderr "WARNING: register_package pkg $pkg already has capspec marked as accepted: $capspec" } - lappend warninglist [dict create msg "pkg $pkg already has this capspec marked as accepted" capspec $capspec] + lappend warninglist [tcl::dict::create msg "pkg $pkg already has this capspec marked as accepted" capspec $capspec] continue } - if {[dict exists $caps $capname]} { - set cap_pkgs [dict get $caps $capname providers] + if {[tcl::dict::exists $caps $capname]} { + set cap_pkgs [tcl::dict::get $caps $capname providers] } else { - dict set caps $capname [dict create handler "" providers [list]] + dict set caps $capname [tcl::dict::create handler "" providers [list]] set cap_pkgs [list] } #todo - if there's a caphandler - call it's init/validation callback for the pkg @@ -429,31 +430,31 @@ namespace eval punk::cap { if {$do_register} { if {$pkg ni $cap_pkgs} { lappend cap_pkgs $pkg - dict set caps $capname providers $cap_pkgs + tcl::dict::set caps $capname providers $cap_pkgs } - dict lappend pkgcapsaccepted $pkg $capspec ;#if pkg is being registered prior to handler-registration - the handler may undo this entry + tcl::dict::lappend pkgcapsaccepted $pkg $capspec ;#if pkg is being registered prior to handler-registration - the handler may undo this entry } } #another call to register_pkg with same pkg may have been made (most likely with different capname) so we must append - but check not already present #dict lappend pkgcapsdeclared $pkg $capabilitylist - if {[dict exists $pkgcapsdeclared $pkg]} { + if {[tcl::dict::exists $pkgcapsdeclared $pkg]} { #review - untested - set mergecapspecs [dict get $pkgcapsdeclared $pkg] + set mergecapspecs [tcl::dict::get $pkgcapsdeclared $pkg] foreach spec $capabilitylist { if {$spec ni $mergecapspecs} { lappend mergecapspecs $spec } } - dict set pkgcapsdeclared $pkg $mergecapspecs + tcl::dict::set pkgcapsdeclared $pkg $mergecapspecs } else { - dict set pkgcapsdeclared $pkg $capabilitylist + tcl::dict::set pkgcapsdeclared $pkg $capabilitylist } set resultdict [list num_capabilities $capabilitylist_count num_accepted $accepted_count] if {[llength $errorlist]} { - dict set resultdict errors $errorlist + tcl::dict::set resultdict errors $errorlist } if {[llength $warninglist]} { - dict set resultdict warnings $warninglist + tcl::dict::set resultdict warnings $warninglist } return $resultdict } diff --git a/src/bootsupport/modules/punk/cap/handlers/templates-0.1.0.tm b/src/bootsupport/modules/punk/cap/handlers/templates-0.1.0.tm index ab101e1..2926b23 100644 --- a/src/bootsupport/modules/punk/cap/handlers/templates-0.1.0.tm +++ b/src/bootsupport/modules/punk/cap/handlers/templates-0.1.0.tm @@ -61,7 +61,7 @@ namespace eval punk::cap::handlers::templates { set path [dict get $capdict path] - set cname [string map [list . _] $capname] + set cname [string map {. _} $capname] set multivendor_package_whitelist [list punk::mix::templates] @@ -85,8 +85,19 @@ namespace eval punk::cap::handlers::templates { module { set provide_statement [package ifneeded $pkg [package require $pkg]] set tmfile [lindex $provide_statement end] + if {[interp issafe]} { + #default safe interp can't use file exists/normalize etc.. but safe interp may have a policy/alias set allowing file access to certain paths - so test if file exists is usable + if {[catch {file exists $tmfile} tm_exists]} { + puts stderr "punk::cap::handlers::templates::capsystem::pkg_register WARNING (expected in most safe interps) - unable to determine base folder for package '$pkg' which is attempting to register with punk::cap as a provider of '$capname' capability" + flush stderr + return 0 + } + } else { + set tm_exists [file exists $tmfile] + } if {![file exists $tmfile]} { puts stderr "punk::cap::handlers::templates::capsystem::pkg_register WARNING - unable to determine base folder for package '$pkg' which is attempting to register with punk::cap as a provider of '$capname' capability" + flush stderr return 0 } @@ -215,7 +226,7 @@ namespace eval punk::cap::handlers::templates { method pkg_unregister {pkg} { upvar ::punk::cap::handlers::templates::handled_caps hcaps foreach capname $hcaps { - set cname [string map [list . _] $capname] + set cname [string map {. _} $capname] upvar ::punk::cap::handlers::templates::provider_info_$cname my_provider_info dict unset my_provider_info $pkg #destroy api objects? @@ -238,7 +249,7 @@ namespace eval punk::cap::handlers::templates { constructor {capname} { variable capabilityname variable cname - set cname [string map [list . _] $capname] + set cname [string map {. _} $capname] set capabilityname $capname } method folders {args} { @@ -635,6 +646,7 @@ namespace eval punk::cap::handlers::templates { #and a name determining command -command_get_item_name method _get_itemdict {args} { set argd [punk::args::get_dict { + *proc -name _get_itemdict *opts -anyopts 0 -startdir -default "" -templatefolder_subdir -optional 0 @@ -646,6 +658,7 @@ namespace eval punk::cap::handlers::templates { } $args] set opts [dict get $argd opts] set globsearches [dict get $argd values globsearches]; #note that in this case our globsearch won't reduce the machine's effort in scannning the filesystem - as we need to search on the renamed results + #puts stderr "=-=============>globsearches:$globsearches" # -- --- --- --- --- --- --- --- --- set opt_startdir [dict get $opts -startdir] set opt_templatefolder_subdir [dict get $opts -templatefolder_subdir] diff --git a/src/bootsupport/modules/punk/char-0.1.0.tm b/src/bootsupport/modules/punk/char-0.1.0.tm index d4bd4c4..e8752c0 100644 --- a/src/bootsupport/modules/punk/char-0.1.0.tm +++ b/src/bootsupport/modules/punk/char-0.1.0.tm @@ -71,10 +71,10 @@ package require textutil::wcswidth #Note that ansi escapes can begin with \033\[ (\u001b\[) or the single character "Control Sequence Introducer" 0x9b # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -namespace eval punk::char { - namespace export * +tcl::namespace::eval punk::char { + tcl::namespace::export * - variable grapheme_widths [dict create] + variable grapheme_widths [tcl::dict::create] # -- -------------------------------------------------------------------------- variable encmimens ;#namespace of mime package providing reversemapencoding and mapencoding functions #tcllib mime requires tcl::chan::memchan,events,core and/or Trf @@ -115,23 +115,23 @@ namespace eval punk::char { set out "" set i 1 append out " " - dict for {k v} $dict { + tcl::dict::for {k v} $dict { #single chars are wrapped with \033(0 and \033(B ie total length 7 - if {[string length $v] == 7} { + if {[tcl::string::length $v] == 7} { set v " $v " - } elseif {[string length $v] == 2} { + } elseif {[tcl::string::length $v] == 2} { set v "$v " - } elseif {[string length $v] == 0} { + } elseif {[tcl::string::length $v] == 0} { set v " " } append out "$k $v " if {$i > 0 && $i % 8 == 0} { - set out [string range $out 0 end-2] + set out [tcl::string::range $out 0 end-2] append out \n " " } incr i } - set out [string trimright $out " "] + set out [tcl::string::trimright $out " "] return $out } @@ -146,18 +146,18 @@ namespace eval punk::char { append out " " set i 1 - dict for {k charinfo} $unicode_dict { - set char [dict get $charinfo char] - if {[string length $char] == 0} { + tcl::dict::for {k charinfo} $unicode_dict { + set char [tcl::dict::get $charinfo char] + if {[tcl::string::length $char] == 0} { set displayv " " - } elseif {[string length $char] == 1} { + } elseif {[tcl::string::length $char] == 1} { set displayv " $char " } else { set displayv $char } append out "$k $displayv " if {$i > 0 && $i % 8 == 0} { - set out [string range $out 0 end-2] + set out [tcl::string::range $out 0 end-2] append out \n " " } incr i @@ -167,7 +167,7 @@ namespace eval punk::char { proc page_names {{search *}} { set all_names [list] set d [page_names_dict $search] - dict for {k v} $d { + tcl::dict::for {k v} $d { if {$k ni $all_names} { lappend all_names $k } @@ -183,7 +183,7 @@ namespace eval punk::char { set d [page_names_dict $namesearch] set out "" - dict for {k v} $d { + tcl::dict::for {k v} $d { append out "$k $v" \n } return [linesort $out] @@ -194,32 +194,32 @@ namespace eval punk::char { } set encnames [encoding names] foreach enc $encnames { - dict set d $enc [list] + tcl::dict::set d $enc [list] } variable encmimens set mimenames [array get ${encmimens}::reversemap] - dict for {mname encname} $mimenames { + tcl::dict::for {mname encname} $mimenames { if {$encname in $encnames} { - set enclist [dict get $d $encname] + set enclist [tcl::dict::get $d $encname] if {$mname ni $enclist} { - dict lappend d $encname $mname + tcl::dict::lappend d $encname $mname } } } foreach enc [lsort $encnames] { set mime_enc [${encmimens}::mapencoding $enc] if {$mime_enc ne ""} { - set enclist [dict get $d $enc] + set enclist [tcl::dict::get $d $enc] if {$mime_enc ni $enclist} { - dict lappend d $enc $mime_enc + tcl::dict::lappend d $enc $mime_enc } } } - set dresult [dict create] + set dresult [tcl::dict::create] if {$search ne "*"} { - dict for {k v} $d { - if {[string match -nocase $search $k] || ([lsearch -nocase $v $search]) >= 0} { - dict set dresult $k $v + tcl::dict::for {k v} $d { + if {[tcl::string::match -nocase $search $k] || ([lsearch -nocase $v $search]) >= 0} { + tcl::dict::set dresult $k $v } } } else { @@ -228,11 +228,11 @@ namespace eval punk::char { return $dresult } proc page8 {encname args} { - dict set args -cols 8 + tcl::dict::set args -cols 8 tailcall page $encname {*}$args } proc page16 {encname args} { - dict set args -cols 16 + tcl::dict::set args -cols 16 tailcall page $encname {*}$args } @@ -246,9 +246,9 @@ namespace eval punk::char { -range {0 256}\ -cols 16\ ] - set opts [dict merge $defaults $args] + set opts [tcl::dict::merge $defaults $args] # -- --- --- --- --- --- --- --- --- - set cols [dict get $opts -cols] + set cols [tcl::dict::get $opts -cols] # -- --- --- --- --- --- --- --- --- set d_bytedisplay [basedict_display] @@ -263,28 +263,28 @@ namespace eval punk::char { set out "" set i 1 append out " " - dict for {k rawchar} $d_page { + tcl::dict::for {k rawchar} $d_page { set num [expr {"0x$k"}] #see if ascii equivalent exists and has a name if {$rawchar eq $invalid} { set displayv "$invalid" } else { set bytedisplay "" - if {[dict exists $d_asciiposn $rawchar]} { - set asciiposn [dict get $d_asciiposn $rawchar] - set bytedisplay [dict get $d_bytedisplay $asciiposn] + if {[tcl::dict::exists $d_asciiposn $rawchar]} { + set asciiposn [tcl::dict::get $d_asciiposn $rawchar] + set bytedisplay [tcl::dict::get $d_bytedisplay $asciiposn] } if {$bytedisplay eq $invalid} { # set displayv " $rawchar " } else { - set displaylen [string length $bytedisplay] + set displaylen [tcl::string::length $bytedisplay] if {$displaylen == 2} { set displayv "$bytedisplay " } elseif {$displaylen == 3} { set displayv $bytedisplay } else { - if {[string length $rawchar] == 0} { + if {[tcl::string::length $rawchar] == 0} { set displayv " " } else { #presumed 1 @@ -296,12 +296,12 @@ namespace eval punk::char { append out "$k $displayv " if {$i > 0 && $i % $cols == 0} { - set out [string range $out 0 end-2] + set out [tcl::string::range $out 0 end-2] append out \n " " } incr i } - set out [string trimright $out " "] + set out [tcl::string::trimright $out " "] return $out } @@ -357,8 +357,8 @@ namespace eval punk::char { set out "" set mimenamesdict [page_names_dict] foreach encname [encoding names] { - if {[dict exists $mimenamesdict $encname]} { - set alt "([dict get $mimenamesdict $encname])" + if {[tcl::dict::exists $mimenamesdict $encname]} { + set alt "([tcl::dict::get $mimenamesdict $encname])" } else { set alt "" } @@ -383,43 +383,43 @@ namespace eval punk::char { proc pagedict_raw {encname} { variable invalid ;# ="???" set encname [encname $encname] - set d [dict create] + set d [tcl::dict::create] for {set i 0} {$i < 256} {incr i} { set k [format %02x $i] - #dict set d $k [encoding convertfrom $encname [format %c $i]] + #tcl::dict::set d $k [encoding convertfrom $encname [format %c $i]] set ch [format %c $i] ; #jmn if {[decodable $ch $encname]} { #set encchar [encoding convertto $encname $ch] - #dict set d $k [encoding convertfrom $encchar] - dict set d $k [encoding convertfrom $encname $ch] + #tcl::dict::set d $k [encoding convertfrom $encchar] + tcl::dict::set d $k [encoding convertfrom $encname $ch] } else { - dict set d $k $invalid ;#use replacement so we can detect difference from actual "?" + tcl::dict::set d $k $invalid ;#use replacement so we can detect difference from actual "?" } } return $d } proc asciidict {} { variable invalid - set d [dict create] + set d [tcl::dict::create] set a128 [asciidict128] for {set i 0} {$i < 256} {incr i} { set k [format %02x $i] if {$i <= 127} { - dict set d $k [dict get $a128 $k] + tcl::dict::set d $k [tcl::dict::get $a128 $k] } else { # - dict set d $k $invalid + tcl::dict::set d $k $invalid } if {$i <=32} { #no point emitting the lower control characters raw to screen - emit the short-names defined in the 'ascii' proc - dict set d $k [dict get $a128 $k] + tcl::dict::set d $k [tcl::dict::get $a128 $k] } else { if {$i == 0x9b} { - dict set d $k CSI ;#don't emit the ansi 'Control Sequence Introducer' - or it will be interpreted by the console and affect the layout. + tcl::dict::set d $k CSI ;#don't emit the ansi 'Control Sequence Introducer' - or it will be interpreted by the console and affect the layout. } else { - dict set d $k [format %c $i] + tcl::dict::set d $k [format %c $i] } } } @@ -427,22 +427,22 @@ namespace eval punk::char { } proc basedict_display {} { - set d [dict create] + set d [tcl::dict::create] set a128 [asciidict128] for {set i 0} {$i < 256} {incr i} { set k [format %02x $i] if {$i <=32} { #no point emitting the lower control characters raw to screen - emit the short-names defined in the 'ascii' proc - dict set d $k [dict get $a128 $k] + tcl::dict::set d $k [tcl::dict::get $a128 $k] } else { if {$i == 0x9b} { - dict set d $k CSI ;#don't emit the ansi 'Control Sequence Introducer' - or it will be interpreted by the console and affect the layout. + tcl::dict::set d $k CSI ;#don't emit the ansi 'Control Sequence Introducer' - or it will be interpreted by the console and affect the layout. } elseif {$i == 0x9c} { - dict set d $k OSC + tcl::dict::set d $k OSC } else { - #dict set d $k [encoding convertfrom [encoding system] [format %c $i]] + #tcl::dict::set d $k [encoding convertfrom [encoding system] [format %c $i]] #don't use encoding convertfrom - we want the value independent of the current encoding system. - dict set d $k [format %c $i] + tcl::dict::set d $k [format %c $i] } } } @@ -450,20 +450,20 @@ namespace eval punk::char { } proc basedict_encoding_system {} { #result depends on 'encoding system' currently in effect - set d [dict create] + set d [tcl::dict::create] for {set i 0} {$i < 256} {incr i} { set k [format %02x $i] - dict set d $k [encoding convertfrom [encoding system] [format %c $i]] + tcl::dict::set d $k [encoding convertfrom [encoding system] [format %c $i]] } return $d } proc basedict {} { #this gives same result independent of current value of 'encoding system' - set d [dict create] + set d [tcl::dict::create] for {set i 0} {$i < 256} {incr i} { set k [format %02x $i] - dict set d $k [format %c $i] + tcl::dict::set d $k [format %c $i] } return $d } @@ -474,22 +474,22 @@ namespace eval punk::char { -range {0 255}\ -charset ""\ ] - set opts [dict merge $defaults $args] + set opts [tcl::dict::merge $defaults $args] # -- --- --- --- --- --- --- --- --- --- - set range [dict get $opts -range] - set charset [dict get $opts -charset] + set range [tcl::dict::get $opts -range] + set charset [tcl::dict::get $opts -charset] # -- --- --- --- --- --- --- --- --- --- if {$charset ne ""} { if {$charset ni [charset_names]} { error "unknown charset '$charset' - use 'charset_names' to get list" } - set setinfo [dict get $charsets $charset] - set ranges [dict get $setinfo ranges] - set charset_dict [dict create] + set setinfo [tcl::dict::get $charsets $charset] + set ranges [tcl::dict::get $setinfo ranges] + set charset_dict [tcl::dict::create] foreach r $ranges { - set start [dict get $r start] - set end [dict get $r end] - #set charset_dict [dict merge $charset_dict [char_range_dict $start $end]] + set start [tcl::dict::get $r start] + set end [tcl::dict::get $r end] + #set charset_dict [tcl::dict::merge $charset_dict [char_range_dict $start $end]] break } @@ -498,10 +498,10 @@ namespace eval punk::char { set end [lindex $range 1] } - set d [dict create] + set d [tcl::dict::create] for {set i $start} {$i <= $end} {incr i} { set k [format %02x $i] - dict set d $k [encoding convertfrom $encname [format %c $i]] + tcl::dict::set d $k [encoding convertfrom $encname [format %c $i]] } return $d } @@ -516,14 +516,14 @@ namespace eval punk::char { #review - use terminal to display actual supported DEC specials vs using dict at: punk::ansi::map_special_graphics which maps to known unicode equivalents proc asciidict2 {} { - set d [dict create] - dict for {k v} [basedict_display] { - if {[string length $v] == 1} { + set d [tcl::dict::create] + tcl::dict::for {k v} [basedict_display] { + if {[tcl::string::length $v] == 1} { set num [expr {"0x$k"}] - #dict set d $k "\033(0[subst \\u00$k]\033(B" - dict set d $k "\033(0[format %c $num]\033(B" + #tcl::dict::set d $k "\033(0[subst \\u00$k]\033(B" + tcl::dict::set d $k "\033(0[format %c $num]\033(B" } else { - dict set d $k $v + tcl::dict::set d $k $v } } return $d @@ -540,7 +540,7 @@ namespace eval punk::char { if {($encname eq "ascii")} { #8.6 fails to round-trip convert 0x7f del character despite it being in the ascii range (review Why?? what else doesn't round-trip but should?) #just strip it out of the string as we are only after a boolean answer and if s is only a single del char empty string will result in true - set s [string map [list [format %c 0x7f] ""] $s] + set s [tcl::string::map [list [format %c 0x7f] ""] $s] } string eq $s [encoding convertfrom $encname [encoding convertto $encname $s]] } @@ -630,14 +630,14 @@ namespace eval punk::char { # -- -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- # Unicode character sets - some hardcoded - some loadable from data files # -- -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- - variable charinfo [dict create] - variable charsets [dict create] + variable charinfo [tcl::dict::create] + variable charsets [tcl::dict::create] # -- -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- # Aggregate character sets (ones that pick various ranges from underlying unicode ranges) # -- -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- - dict set charsets "WGL4" [list altname "Windows Glyph List 4" ranges [list\ + tcl::dict::set charsets "WGL4" [list altname "Windows Glyph List 4" ranges [list\ {start 0 end 127 name "basic latin"}\ {start 128 end 255 name "latin-1 supplement"}\ {start 256 end 383 name "Latin Extended-A"}\ @@ -668,202 +668,202 @@ namespace eval punk::char { #The base page 0-256 8bit range - values don't have specific characters or descriptions - as they are codepage dependent #we will fill this here for completeness - but with placeholders # -- -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- - dict set charsets "8bit" [list ranges [list {start 0 end 127 name ascii} {start 128 end 255 name 8bit-ascii}] description "8bit extended ascii range" settype "other"] + tcl::dict::set charsets "8bit" [list ranges [list {start 0 end 127 name ascii} {start 128 end 255 name 8bit-ascii}] description "8bit extended ascii range" settype "other"] for {set i 0} {$i < 256} {incr i} { - dict set charinfo $i [list desc "codepage-dependent" short "byte_[format %02x $i]"] + tcl::dict::set charinfo $i [list desc "codepage-dependent" short "byte_[format %02x $i]"] } # -- -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- # Unicode ranges # -- -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- # -- -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- - dict set charsets "greek" [list ranges [list {start 880 end 1023} {start 7936 end 8191}] description "Greek and Coptic" settype "other"] + tcl::dict::set charsets "greek" [list ranges [list {start 880 end 1023} {start 7936 end 8191}] description "Greek and Coptic" settype "other"] # -- -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- - dict set charsets "Block Elements" [list ranges [list {start 9600 end 9631}] description "Block Elements" settype "other"] - dict set charinfo 9600 [list desc "Upper Half Block" short "blocke_up_half"] - dict set charinfo 9601 [list desc "Lower One Eighth Block" short "blocke_lw_1_8th"] - dict set charinfo 9602 [list desc "Lower One Quarter Block" short "blocke_lw_1_qtr"] + tcl::dict::set charsets "Block Elements" [list ranges [list {start 9600 end 9631}] description "Block Elements" settype "other"] + tcl::dict::set charinfo 9600 [list desc "Upper Half Block" short "blocke_up_half"] + tcl::dict::set charinfo 9601 [list desc "Lower One Eighth Block" short "blocke_lw_1_8th"] + tcl::dict::set charinfo 9602 [list desc "Lower One Quarter Block" short "blocke_lw_1_qtr"] # -- -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- - dict set charsets "Dingbats" [list ranges [list {start 9984 end 10175 }] description "Unicode Dingbats" settype "tcl_supplemented"] - dict set charinfo 9984 [list desc "Black Safety Scissors" short "dingbats_black_safety_scissors"] + tcl::dict::set charsets "Dingbats" [list ranges [list {start 9984 end 10175 }] description "Unicode Dingbats" settype "tcl_supplemented"] + tcl::dict::set charinfo 9984 [list desc "Black Safety Scissors" short "dingbats_black_safety_scissors"] #... - dict set charinfo 10175 [list desc "Double Curly Loop" short "dingbats_double_curly_loop"] + tcl::dict::set charinfo 10175 [list desc "Double Curly Loop" short "dingbats_double_curly_loop"] # -- -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- #variation selectors 0xFe01 - 0xFE0F - dict set charsets "Variation Selectors" [list ranges [list {start 65024 end 65039}] description "Variation Selectors" note "combining character with previous char - variant glyph display" settype "tcl_supplemented"] - dict set charinfo 65024 [list desc "Variation Selector-1" short "VS1"] - dict set charinfo 65025 [list desc "Variation Selector-2" short "VS2"] - dict set charinfo 65026 [list desc "Variation Selector-3" short "VS3"] - dict set charinfo 65027 [list desc "Variation Selector-4" short "VS4"] - dict set charinfo 65027 [list desc "Variation Selector-5" short "VS5"] - dict set charinfo 65029 [list desc "Variation Selector-6" short "VS6"] - dict set charinfo 65030 [list desc "Variation Selector-7" short "VS7"] - dict set charinfo 65031 [list desc "Variation Selector-8" short "VS8"] - dict set charinfo 65032 [list desc "Variation Selector-9" short "VS9"] - dict set charinfo 65033 [list desc "Variation Selector-10" short "VS10"] - dict set charinfo 65034 [list desc "Variation Selector-11" short "VS11"] - dict set charinfo 65035 [list desc "Variation Selector-12" short "VS12"] - dict set charinfo 65036 [list desc "Variation Selector-13" short "VS13"] - dict set charinfo 65037 [list desc "Variation Selector-14" short "VS14"] - dict set charinfo 65038 [list desc "Variation Selector-15 text variation" short "VS15"] ;#still an image - just more suitable for text-presentation e.g word-processing doc - dict set charinfo 65039 [list desc "Variation Selector-16 emoji variation" short "VS16"] + tcl::dict::set charsets "Variation Selectors" [list ranges [list {start 65024 end 65039}] description "Variation Selectors" note "combining character with previous char - variant glyph display" settype "tcl_supplemented"] + tcl::dict::set charinfo 65024 [list desc "Variation Selector-1" short "VS1"] + tcl::dict::set charinfo 65025 [list desc "Variation Selector-2" short "VS2"] + tcl::dict::set charinfo 65026 [list desc "Variation Selector-3" short "VS3"] + tcl::dict::set charinfo 65027 [list desc "Variation Selector-4" short "VS4"] + tcl::dict::set charinfo 65027 [list desc "Variation Selector-5" short "VS5"] + tcl::dict::set charinfo 65029 [list desc "Variation Selector-6" short "VS6"] + tcl::dict::set charinfo 65030 [list desc "Variation Selector-7" short "VS7"] + tcl::dict::set charinfo 65031 [list desc "Variation Selector-8" short "VS8"] + tcl::dict::set charinfo 65032 [list desc "Variation Selector-9" short "VS9"] + tcl::dict::set charinfo 65033 [list desc "Variation Selector-10" short "VS10"] + tcl::dict::set charinfo 65034 [list desc "Variation Selector-11" short "VS11"] + tcl::dict::set charinfo 65035 [list desc "Variation Selector-12" short "VS12"] + tcl::dict::set charinfo 65036 [list desc "Variation Selector-13" short "VS13"] + tcl::dict::set charinfo 65037 [list desc "Variation Selector-14" short "VS14"] + tcl::dict::set charinfo 65038 [list desc "Variation Selector-15 text variation" short "VS15"] ;#still an image - just more suitable for text-presentation e.g word-processing doc + tcl::dict::set charinfo 65039 [list desc "Variation Selector-16 emoji variation" short "VS16"] # -- -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- # emoticons https://www.unicode.org/charts/PDF/U1F600.pdf - dict set charsets "Emoticons" [list ranges [list {start 128512 end 128591}] description "Emoticons" settype "tcl_supplemented"] - dict set charinfo 128512 [list desc "Grinning Face" short "emoticon_gface"] - dict set charinfo 128513 [list desc "Grinning Face with Smiling Eyes" short "emoticon_gface_smile_eyes"] - dict set charinfo 128514 [list desc "Face with Tears of Joy" short "emoticon_face_tears_joy"] + tcl::dict::set charsets "Emoticons" [list ranges [list {start 128512 end 128591}] description "Emoticons" settype "tcl_supplemented"] + tcl::dict::set charinfo 128512 [list desc "Grinning Face" short "emoticon_gface"] + tcl::dict::set charinfo 128513 [list desc "Grinning Face with Smiling Eyes" short "emoticon_gface_smile_eyes"] + tcl::dict::set charinfo 128514 [list desc "Face with Tears of Joy" short "emoticon_face_tears_joy"] #todo - dict set charinfo 128590 [list desc "Person with Pouting Face" short "emoticon_person_pout"] + tcl::dict::set charinfo 128590 [list desc "Person with Pouting Face" short "emoticon_person_pout"] # -- -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- - dict set charsets "Box Drawing" [list ranges [list {start 9472 end 9599}] description "Box Drawing" settype "tcl_supplemented"] - dict set charinfo 9472 [list desc "Box Drawings Light Horizontal" short "boxd_lhz"] - dict set charinfo 9473 [list desc "Box Drawings Heavy Horizontal" short "boxd_hhz"] - dict set charinfo 9474 [list desc "Box Drawings Light Vertical" short "boxd_lv"] - dict set charinfo 9475 [list desc "Box Drawings Heavy Vertical" short "boxd_hv"] - dict set charinfo 9476 [list desc "Box Drawings Light Triple Dash Horizontal" short "boxd_ltdshhz"] - dict set charinfo 9477 [list desc "Box Drawings Heavy Triple Dash Horizontal" short "boxd_htdshhz"] - dict set charinfo 9478 [list desc "Box Drawings Light Triple Dash Vertical" short "boxd_ltdshv"] - dict set charinfo 9479 [list desc "Box Drawings Heavy Triple Dash Vertical" short "boxd_htdshv"] - dict set charinfo 9480 [list desc "Box Drawings Light Quadruple Dash Horizontal" short "boxd_lqdshhz"] - dict set charinfo 9481 [list desc "Box Drawings Heavy Quadruple Dash Horizontal" short "boxd_hqdshhz"] - dict set charinfo 9482 [list desc "Box Drawings Light Quadruple Dash Vertical" short "boxd_lqdshv"] - dict set charinfo 9483 [list desc "Box Drawings Heavy Quadruple Dash Vertical" short "boxd_hqdshv"] - dict set charinfo 9484 [list desc "Box Drawings Light Down and Right" short "boxd_ldr"] - dict set charinfo 9485 [list desc "Box Drawings Down Light and Right Heavy" short "boxd_dlrh"] - dict set charinfo 9486 [list desc "Box Drawings Down Heavy and Right Light" short "boxd_dhrl"] - dict set charinfo 9487 [list desc "Box Drawings Heavy Down and Right" short "boxd_hdr"] - dict set charinfo 9488 [list desc "Box Drawings Light Down and Left" short "boxd_ldl"] - dict set charinfo 9489 [list desc "Box Drawings Down Light and Left Heavy" short "boxd_dllh"] - dict set charinfo 9490 [list desc "Box Drawings Down Heavy and Left Light" short "boxd_dhll"] - dict set charinfo 9491 [list desc "Box Drawings Heavy Down and Left" short "boxd_hdl"] - dict set charinfo 9492 [list desc "Box Drawings Light Up and Right" short "boxd_lur"] - dict set charinfo 9493 [list desc "Box Drawings Up Light and Right Heavy" short "boxd_ulrh"] - dict set charinfo 9494 [list desc "Box Drawings Up Heavy and Right Light" short "boxd_uhrl"] - dict set charinfo 9495 [list desc "Box Drawings Heavy Up and Right" short "boxd_hur"] - dict set charinfo 9496 [list desc "Box Drawings Light Up and Left" short "boxd_lul"] - dict set charinfo 9497 [list desc "Box Drawings Up Light and Left Heavy" short "boxd_ullh"] - dict set charinfo 9498 [list desc "Box Drawings Up Heavy and Left Light" short "boxd_uhll"] - dict set charinfo 9499 [list desc "Box Drawings Heavy Up and Left" short "boxd_hul"] - dict set charinfo 9500 [list desc "Box Drawings Light Vertical and Right" short "boxd_lvr"] - dict set charinfo 9501 [list desc "Box Drawings Vertical Light and Right Heavy" short "boxd_vlrh"] - dict set charinfo 9502 [list desc "Box Drawings Up Heavy and Right Down Light" short "boxd_uhrdl"] - dict set charinfo 9503 [list desc "Box Drawings Down Heavy and Right Up Light" short "boxd_dhrul"] - dict set charinfo 9504 [list desc "Box Drawings Vertical Heavy and Right Light" short "boxd_vhrl"] - dict set charinfo 9505 [list desc "Box Drawings Down Light and Right Up Heavy" short "boxd_dlruh"] - dict set charinfo 9506 [list desc "Box Drawings Up Light and Right Down Heavy" short "boxd_ulrdh"] - dict set charinfo 9507 [list desc "Box Drawings Heavy Vertical and Right" short "boxd_hvr"] - dict set charinfo 9508 [list desc "Box Drawings Light Vertical and Left" short "boxd_lvl"] - dict set charinfo 9509 [list desc "Box Drawings Vertical Light and Left Heavy" short "boxd_vllh"] - dict set charinfo 9510 [list desc "Box Drawings Up Heavy and Let Down Light" short "boxd_uhldl"] - dict set charinfo 9511 [list desc "Box Drawings Down Heavy and Left Up Light" short "boxd_dhlul"] - dict set charinfo 9512 [list desc "Box Drawings Vertical Heavy and Left Light" short "boxd_vhll"] - dict set charinfo 9513 [list desc "Box Drawings Down Light and left Up Heavy" short "boxd_dlluh"] - dict set charinfo 9514 [list desc "Box Drawings Up Light and Left Down Heavy" short "boxd_ulldh"] - dict set charinfo 9515 [list desc "Box Drawings Heavy Vertical and Left" short "boxd_hvl"] - dict set charinfo 9516 [list desc "Box Drawings Light Down and Horizontal" short "boxd_ldhz"] - dict set charinfo 9517 [list desc "Box Drawings Left Heavy and Right Down Light" short "boxd_lhrdl"] - dict set charinfo 9518 [list desc "Box Drawings Right Heavy and Left Down Light" short "boxd_rhldl"] - dict set charinfo 9519 [list desc "Box Drawings Down Light and Horizontal Heavy" short "boxd_dlhzh"] - dict set charinfo 9520 [list desc "Box Drawings Down Heavy and Horizontal Light" short "boxd_dhhzl"] - dict set charinfo 9521 [list desc "Box Drawings Right Light and Left Down Heavy" short "boxd_rlldh"] - dict set charinfo 9522 [list desc "Box Drawings Left Light and Right Down Heavy" short "boxd_llrdh"] - dict set charinfo 9523 [list desc "Box Drawings Heavy Down and Horizontal" short "boxd_hdhz"] - dict set charinfo 9524 [list desc "Box Drawings Light Up and Horizontal" short "boxd_luhz"] - dict set charinfo 9525 [list desc "Box Drawings Left Heavy and Right Up Light" short "boxd_lhrul"] - dict set charinfo 9526 [list desc "Box Drawings Right Heavy and Left Up Light" short "boxd_rhlul"] - dict set charinfo 9527 [list desc "Box Drawings Up Light and Horizontal Heavy" short "boxd_ulhzh"] - dict set charinfo 9528 [list desc "Box Drawings Up Heavy and Horizontal Light" short "boxd_uhhzl"] - dict set charinfo 9529 [list desc "Box Drawings Right Light and Left Up Heavy" short "boxd_rlluh"] - dict set charinfo 9530 [list desc "Box Drawings Left Light and Right Up Heavy" short "boxd_llruh"] - dict set charinfo 9531 [list desc "Box Drawings Heavy Up and Horizontal" short "boxd_huhz"] - dict set charinfo 9532 [list desc "Box Drawings Light Vertical and Horizontal" short "boxd_lvhz"] - dict set charinfo 9533 [list desc "Box Drawings Left Heavy and Right Vertical Light" short "boxd_lhrvl"] - dict set charinfo 9534 [list desc "Box Drawings Right Heavy and Left Vertical Light" short "boxd_rhlvl"] - dict set charinfo 9535 [list desc "Box Drawings Vertical Light and Horizontal Heavy" short "boxd_vlhzh"] - dict set charinfo 9536 [list desc "Box Drawings Up Heavy and Down Horizontal Light" short "boxd_uhdhzl"] - dict set charinfo 9537 [list desc "Box Drawings Down Heavy and Up Horizontal Light" short "boxd_dhuhzl"] - dict set charinfo 9538 [list desc "Box Drawings Vertical Heavy and Horizontal Light" short "boxd_vhhzl"] - dict set charinfo 9539 [list desc "Box Drawings Left Up Heavy and Right Down Light" short "boxd_luhrdl"] - dict set charinfo 9540 [list desc "Box Drawings Right Up Heavy and Left Down Light" short "boxd_ruhldl"] - dict set charinfo 9541 [list desc "Box Drawings Left Down Heavy and Right Up Light" short "boxd_ldhrul"] - dict set charinfo 9542 [list desc "Box Drawings Right Down Heavy and Left Up Light" short "boxd_rdhlul"] - dict set charinfo 9543 [list desc "Box Drawings Down Light and Up Horizontal Heavy" short "boxd_dluhzh"] - dict set charinfo 9544 [list desc "Box Drawings Up Light and Down Horizontal Heavy" short "boxd_dldhzh"] - dict set charinfo 9545 [list desc "Box Drawings Right Light and Left Vertical Heavy" short "boxd_rllvh"] - dict set charinfo 9546 [list desc "Box Drawings Left Light and Right Vertical Heavy" short "boxd_llrvh"] - dict set charinfo 9547 [list desc "Box Drawings Heavy Vertical and Horizontal" short "boxd_hvhz"] - dict set charinfo 9548 [list desc "Box Drawings Light Double Dash Horizontal" short "boxd_lddshhz"] - dict set charinfo 9549 [list desc "Box Drawings Heavy Double Dash Horizontal" short "boxd_hddshhz"] - dict set charinfo 9550 [list desc "Box Drawings Light Double Dash Vertical" short "boxd_lddshv"] - dict set charinfo 9551 [list desc "Box Drawings Heavy Double Dash Vertical" short "boxd_hddshv"] - dict set charinfo 9552 [list desc "Box Drawings Double Horizontal" short "boxd_dhz"] - dict set charinfo 9553 [list desc "Box Drawings Double Vertical" short "boxd_dv"] - dict set charinfo 9554 [list desc "Box Drawings Down Single and Right Double" short "boxd_dsrd"] - dict set charinfo 9555 [list desc "Box Drawings Down Double and Right Single" short "boxd_ddrs"] - dict set charinfo 9556 [list desc "Box Drawings Double Down and Right" short "boxd_ddr"] - dict set charinfo 9557 [list desc "Box Drawings Down Single and Left Double" short "boxd_dsld"] - dict set charinfo 9558 [list desc "Box Drawings Down Double and Left Single" short "boxd_ddls"] - dict set charinfo 9559 [list desc "Box Drawings Double Down and Left" short "boxd_ddl"] - dict set charinfo 9560 [list desc "Box Drawings Up Single and Right Double" short "boxd_usrd"] - dict set charinfo 9561 [list desc "Box Drawings Up Double and Right Single" short "boxd_udrs"] - dict set charinfo 9562 [list desc "Box Drawings Double Up and Right" short "boxd_dur"] - dict set charinfo 9563 [list desc "Box Drawings Up Single and Left Double" short "boxd_usld"] - dict set charinfo 9564 [list desc "Box Drawings Up Double and Left Single" short "boxd_udls"] - dict set charinfo 9565 [list desc "Box Drawings Double Up and Left" short "boxd_dul"] - dict set charinfo 9566 [list desc "Box Drawings Vertical Single and Right Double" short "boxd_vsrd"] - dict set charinfo 9567 [list desc "Box Drawings Vertical Double and Right Single" short "boxd_vdrs"] - dict set charinfo 9568 [list desc "Box Drawings Double Vertical and Right" short "boxd_dvr"] - dict set charinfo 9569 [list desc "Box Drawings Vertical Single and Left Double" short "boxd_vsld"] - dict set charinfo 9570 [list desc "Box Drawings Vertical Double and Left Single" short "boxd_vdls"] - dict set charinfo 9571 [list desc "Box Drawings Double Vertical and Left" short "boxd_dvl"] - dict set charinfo 9572 [list desc "Box Drawings Down Single and Horizontal Double" short "boxd_dshzd"] - dict set charinfo 9573 [list desc "Box Drawings Down Double and Horizontal Single" short "boxd_ddhzs"] - dict set charinfo 9574 [list desc "Box Drawings Double Down and Horizontal" short "boxd_ddhz"] - dict set charinfo 9575 [list desc "Box Drawings Up Single and Horizontal Double" short "boxd_ushzd"] - dict set charinfo 9576 [list desc "Box Drawings Up Double and Horizontal Single" short "boxd_udhzs"] - dict set charinfo 9577 [list desc "Box Drawings Double Up and Horizontal" short "boxd_duhz"] - dict set charinfo 9578 [list desc "Box Drawings Vertical Single and Horizontal Double" short "boxd_vshzd"] - dict set charinfo 9579 [list desc "Box Drawings Vertical Double and Horizontal Single" short "boxd_vdhzs"] - dict set charinfo 9580 [list desc "Box Drawings Double Vertical and Horizontal" short "boxd_dvhz"] - dict set charinfo 9581 [list desc "Box Drawings Light Arc Down and Right" short "boxd_ladr"] - dict set charinfo 9582 [list desc "Box Drawings Light Arc Down and Left" short "boxd_ladl"] - dict set charinfo 9583 [list desc "Box Drawings Light Arc Up and Left" short "boxd_laul"] - dict set charinfo 9584 [list desc "Box Drawings Light Arc Up and Right" short "boxd_laur"] - dict set charinfo 9585 [list desc "Box Drawings Light Diagonal Upper Right To Lower Left" short "boxd_ldgurll"] - dict set charinfo 9586 [list desc "Box Drawings Light Diagonal Upper Left To Lower Right" short "boxd_ldgullr"] - dict set charinfo 9587 [list desc "Box Drawings Light Diagonal Cross" short "boxd_ldc"] - dict set charinfo 9588 [list desc "Box Drawings Light Left" short "boxd_ll"] - dict set charinfo 9589 [list desc "Box Drawings Light Up" short "boxd_lu"] - dict set charinfo 9590 [list desc "Box Drawings Light Right" short "boxd_lr"] - dict set charinfo 9591 [list desc "Box Drawings Light Down" short "boxd_ld"] - dict set charinfo 9592 [list desc "Box Drawings Heavy Left" short "boxd_hl"] - dict set charinfo 9593 [list desc "Box Drawings Heavy Up" short "boxd_hu"] - dict set charinfo 9594 [list desc "Box Drawings Heavy Right" short "boxd_hr"] - dict set charinfo 9595 [list desc "Box Drawings Heavy Down" short "boxd_hd"] - dict set charinfo 9596 [list desc "Box Drawings Light Left and Heavy Right" short "boxd_llhr"] - dict set charinfo 9597 [list desc "Box Drawings Light Up and Heavy Down" short "boxd_luhd"] - dict set charinfo 9598 [list desc "Box Drawings Heavy Left and Light Right" short "boxd_hllr"] - dict set charinfo 9599 [list desc "Box Drawings Heavy Up and Light Down" short "boxd_huld"] - - - dict set charsets "Halfwidth and Fullwidth Forms" [list ranges [list {start 65280 end 65519}] description "Halfwidth and Fullwidth Forms (variants)" settype "tcl_supplemental"] - dict set charsets "ascii_fullwidth" [list ranges [list {start 65281 end 65374}] description "Ascii 21 to 7E fullwidth" parentblock "halfwidth_and_fullwidth_forms" settype "other"] - - dict set charsets "Specials" [list ranges [list {start 65520 end 65535}] description "Specials" settype "tcl_supplemental"] - - dict set charsets "noncharacters" [list ranges [list\ + tcl::dict::set charsets "Box Drawing" [list ranges [list {start 9472 end 9599}] description "Box Drawing" settype "tcl_supplemented"] + tcl::dict::set charinfo 9472 [list desc "Box Drawings Light Horizontal" short "boxd_lhz"] + tcl::dict::set charinfo 9473 [list desc "Box Drawings Heavy Horizontal" short "boxd_hhz"] + tcl::dict::set charinfo 9474 [list desc "Box Drawings Light Vertical" short "boxd_lv"] + tcl::dict::set charinfo 9475 [list desc "Box Drawings Heavy Vertical" short "boxd_hv"] + tcl::dict::set charinfo 9476 [list desc "Box Drawings Light Triple Dash Horizontal" short "boxd_ltdshhz"] + tcl::dict::set charinfo 9477 [list desc "Box Drawings Heavy Triple Dash Horizontal" short "boxd_htdshhz"] + tcl::dict::set charinfo 9478 [list desc "Box Drawings Light Triple Dash Vertical" short "boxd_ltdshv"] + tcl::dict::set charinfo 9479 [list desc "Box Drawings Heavy Triple Dash Vertical" short "boxd_htdshv"] + tcl::dict::set charinfo 9480 [list desc "Box Drawings Light Quadruple Dash Horizontal" short "boxd_lqdshhz"] + tcl::dict::set charinfo 9481 [list desc "Box Drawings Heavy Quadruple Dash Horizontal" short "boxd_hqdshhz"] + tcl::dict::set charinfo 9482 [list desc "Box Drawings Light Quadruple Dash Vertical" short "boxd_lqdshv"] + tcl::dict::set charinfo 9483 [list desc "Box Drawings Heavy Quadruple Dash Vertical" short "boxd_hqdshv"] + tcl::dict::set charinfo 9484 [list desc "Box Drawings Light Down and Right" short "boxd_ldr"] + tcl::dict::set charinfo 9485 [list desc "Box Drawings Down Light and Right Heavy" short "boxd_dlrh"] + tcl::dict::set charinfo 9486 [list desc "Box Drawings Down Heavy and Right Light" short "boxd_dhrl"] + tcl::dict::set charinfo 9487 [list desc "Box Drawings Heavy Down and Right" short "boxd_hdr"] + tcl::dict::set charinfo 9488 [list desc "Box Drawings Light Down and Left" short "boxd_ldl"] + tcl::dict::set charinfo 9489 [list desc "Box Drawings Down Light and Left Heavy" short "boxd_dllh"] + tcl::dict::set charinfo 9490 [list desc "Box Drawings Down Heavy and Left Light" short "boxd_dhll"] + tcl::dict::set charinfo 9491 [list desc "Box Drawings Heavy Down and Left" short "boxd_hdl"] + tcl::dict::set charinfo 9492 [list desc "Box Drawings Light Up and Right" short "boxd_lur"] + tcl::dict::set charinfo 9493 [list desc "Box Drawings Up Light and Right Heavy" short "boxd_ulrh"] + tcl::dict::set charinfo 9494 [list desc "Box Drawings Up Heavy and Right Light" short "boxd_uhrl"] + tcl::dict::set charinfo 9495 [list desc "Box Drawings Heavy Up and Right" short "boxd_hur"] + tcl::dict::set charinfo 9496 [list desc "Box Drawings Light Up and Left" short "boxd_lul"] + tcl::dict::set charinfo 9497 [list desc "Box Drawings Up Light and Left Heavy" short "boxd_ullh"] + tcl::dict::set charinfo 9498 [list desc "Box Drawings Up Heavy and Left Light" short "boxd_uhll"] + tcl::dict::set charinfo 9499 [list desc "Box Drawings Heavy Up and Left" short "boxd_hul"] + tcl::dict::set charinfo 9500 [list desc "Box Drawings Light Vertical and Right" short "boxd_lvr"] + tcl::dict::set charinfo 9501 [list desc "Box Drawings Vertical Light and Right Heavy" short "boxd_vlrh"] + tcl::dict::set charinfo 9502 [list desc "Box Drawings Up Heavy and Right Down Light" short "boxd_uhrdl"] + tcl::dict::set charinfo 9503 [list desc "Box Drawings Down Heavy and Right Up Light" short "boxd_dhrul"] + tcl::dict::set charinfo 9504 [list desc "Box Drawings Vertical Heavy and Right Light" short "boxd_vhrl"] + tcl::dict::set charinfo 9505 [list desc "Box Drawings Down Light and Right Up Heavy" short "boxd_dlruh"] + tcl::dict::set charinfo 9506 [list desc "Box Drawings Up Light and Right Down Heavy" short "boxd_ulrdh"] + tcl::dict::set charinfo 9507 [list desc "Box Drawings Heavy Vertical and Right" short "boxd_hvr"] + tcl::dict::set charinfo 9508 [list desc "Box Drawings Light Vertical and Left" short "boxd_lvl"] + tcl::dict::set charinfo 9509 [list desc "Box Drawings Vertical Light and Left Heavy" short "boxd_vllh"] + tcl::dict::set charinfo 9510 [list desc "Box Drawings Up Heavy and Let Down Light" short "boxd_uhldl"] + tcl::dict::set charinfo 9511 [list desc "Box Drawings Down Heavy and Left Up Light" short "boxd_dhlul"] + tcl::dict::set charinfo 9512 [list desc "Box Drawings Vertical Heavy and Left Light" short "boxd_vhll"] + tcl::dict::set charinfo 9513 [list desc "Box Drawings Down Light and left Up Heavy" short "boxd_dlluh"] + tcl::dict::set charinfo 9514 [list desc "Box Drawings Up Light and Left Down Heavy" short "boxd_ulldh"] + tcl::dict::set charinfo 9515 [list desc "Box Drawings Heavy Vertical and Left" short "boxd_hvl"] + tcl::dict::set charinfo 9516 [list desc "Box Drawings Light Down and Horizontal" short "boxd_ldhz"] + tcl::dict::set charinfo 9517 [list desc "Box Drawings Left Heavy and Right Down Light" short "boxd_lhrdl"] + tcl::dict::set charinfo 9518 [list desc "Box Drawings Right Heavy and Left Down Light" short "boxd_rhldl"] + tcl::dict::set charinfo 9519 [list desc "Box Drawings Down Light and Horizontal Heavy" short "boxd_dlhzh"] + tcl::dict::set charinfo 9520 [list desc "Box Drawings Down Heavy and Horizontal Light" short "boxd_dhhzl"] + tcl::dict::set charinfo 9521 [list desc "Box Drawings Right Light and Left Down Heavy" short "boxd_rlldh"] + tcl::dict::set charinfo 9522 [list desc "Box Drawings Left Light and Right Down Heavy" short "boxd_llrdh"] + tcl::dict::set charinfo 9523 [list desc "Box Drawings Heavy Down and Horizontal" short "boxd_hdhz"] + tcl::dict::set charinfo 9524 [list desc "Box Drawings Light Up and Horizontal" short "boxd_luhz"] + tcl::dict::set charinfo 9525 [list desc "Box Drawings Left Heavy and Right Up Light" short "boxd_lhrul"] + tcl::dict::set charinfo 9526 [list desc "Box Drawings Right Heavy and Left Up Light" short "boxd_rhlul"] + tcl::dict::set charinfo 9527 [list desc "Box Drawings Up Light and Horizontal Heavy" short "boxd_ulhzh"] + tcl::dict::set charinfo 9528 [list desc "Box Drawings Up Heavy and Horizontal Light" short "boxd_uhhzl"] + tcl::dict::set charinfo 9529 [list desc "Box Drawings Right Light and Left Up Heavy" short "boxd_rlluh"] + tcl::dict::set charinfo 9530 [list desc "Box Drawings Left Light and Right Up Heavy" short "boxd_llruh"] + tcl::dict::set charinfo 9531 [list desc "Box Drawings Heavy Up and Horizontal" short "boxd_huhz"] + tcl::dict::set charinfo 9532 [list desc "Box Drawings Light Vertical and Horizontal" short "boxd_lvhz"] + tcl::dict::set charinfo 9533 [list desc "Box Drawings Left Heavy and Right Vertical Light" short "boxd_lhrvl"] + tcl::dict::set charinfo 9534 [list desc "Box Drawings Right Heavy and Left Vertical Light" short "boxd_rhlvl"] + tcl::dict::set charinfo 9535 [list desc "Box Drawings Vertical Light and Horizontal Heavy" short "boxd_vlhzh"] + tcl::dict::set charinfo 9536 [list desc "Box Drawings Up Heavy and Down Horizontal Light" short "boxd_uhdhzl"] + tcl::dict::set charinfo 9537 [list desc "Box Drawings Down Heavy and Up Horizontal Light" short "boxd_dhuhzl"] + tcl::dict::set charinfo 9538 [list desc "Box Drawings Vertical Heavy and Horizontal Light" short "boxd_vhhzl"] + tcl::dict::set charinfo 9539 [list desc "Box Drawings Left Up Heavy and Right Down Light" short "boxd_luhrdl"] + tcl::dict::set charinfo 9540 [list desc "Box Drawings Right Up Heavy and Left Down Light" short "boxd_ruhldl"] + tcl::dict::set charinfo 9541 [list desc "Box Drawings Left Down Heavy and Right Up Light" short "boxd_ldhrul"] + tcl::dict::set charinfo 9542 [list desc "Box Drawings Right Down Heavy and Left Up Light" short "boxd_rdhlul"] + tcl::dict::set charinfo 9543 [list desc "Box Drawings Down Light and Up Horizontal Heavy" short "boxd_dluhzh"] + tcl::dict::set charinfo 9544 [list desc "Box Drawings Up Light and Down Horizontal Heavy" short "boxd_dldhzh"] + tcl::dict::set charinfo 9545 [list desc "Box Drawings Right Light and Left Vertical Heavy" short "boxd_rllvh"] + tcl::dict::set charinfo 9546 [list desc "Box Drawings Left Light and Right Vertical Heavy" short "boxd_llrvh"] + tcl::dict::set charinfo 9547 [list desc "Box Drawings Heavy Vertical and Horizontal" short "boxd_hvhz"] + tcl::dict::set charinfo 9548 [list desc "Box Drawings Light Double Dash Horizontal" short "boxd_lddshhz"] + tcl::dict::set charinfo 9549 [list desc "Box Drawings Heavy Double Dash Horizontal" short "boxd_hddshhz"] + tcl::dict::set charinfo 9550 [list desc "Box Drawings Light Double Dash Vertical" short "boxd_lddshv"] + tcl::dict::set charinfo 9551 [list desc "Box Drawings Heavy Double Dash Vertical" short "boxd_hddshv"] + tcl::dict::set charinfo 9552 [list desc "Box Drawings Double Horizontal" short "boxd_dhz"] + tcl::dict::set charinfo 9553 [list desc "Box Drawings Double Vertical" short "boxd_dv"] + tcl::dict::set charinfo 9554 [list desc "Box Drawings Down Single and Right Double" short "boxd_dsrd"] + tcl::dict::set charinfo 9555 [list desc "Box Drawings Down Double and Right Single" short "boxd_ddrs"] + tcl::dict::set charinfo 9556 [list desc "Box Drawings Double Down and Right" short "boxd_ddr"] + tcl::dict::set charinfo 9557 [list desc "Box Drawings Down Single and Left Double" short "boxd_dsld"] + tcl::dict::set charinfo 9558 [list desc "Box Drawings Down Double and Left Single" short "boxd_ddls"] + tcl::dict::set charinfo 9559 [list desc "Box Drawings Double Down and Left" short "boxd_ddl"] + tcl::dict::set charinfo 9560 [list desc "Box Drawings Up Single and Right Double" short "boxd_usrd"] + tcl::dict::set charinfo 9561 [list desc "Box Drawings Up Double and Right Single" short "boxd_udrs"] + tcl::dict::set charinfo 9562 [list desc "Box Drawings Double Up and Right" short "boxd_dur"] + tcl::dict::set charinfo 9563 [list desc "Box Drawings Up Single and Left Double" short "boxd_usld"] + tcl::dict::set charinfo 9564 [list desc "Box Drawings Up Double and Left Single" short "boxd_udls"] + tcl::dict::set charinfo 9565 [list desc "Box Drawings Double Up and Left" short "boxd_dul"] + tcl::dict::set charinfo 9566 [list desc "Box Drawings Vertical Single and Right Double" short "boxd_vsrd"] + tcl::dict::set charinfo 9567 [list desc "Box Drawings Vertical Double and Right Single" short "boxd_vdrs"] + tcl::dict::set charinfo 9568 [list desc "Box Drawings Double Vertical and Right" short "boxd_dvr"] + tcl::dict::set charinfo 9569 [list desc "Box Drawings Vertical Single and Left Double" short "boxd_vsld"] + tcl::dict::set charinfo 9570 [list desc "Box Drawings Vertical Double and Left Single" short "boxd_vdls"] + tcl::dict::set charinfo 9571 [list desc "Box Drawings Double Vertical and Left" short "boxd_dvl"] + tcl::dict::set charinfo 9572 [list desc "Box Drawings Down Single and Horizontal Double" short "boxd_dshzd"] + tcl::dict::set charinfo 9573 [list desc "Box Drawings Down Double and Horizontal Single" short "boxd_ddhzs"] + tcl::dict::set charinfo 9574 [list desc "Box Drawings Double Down and Horizontal" short "boxd_ddhz"] + tcl::dict::set charinfo 9575 [list desc "Box Drawings Up Single and Horizontal Double" short "boxd_ushzd"] + tcl::dict::set charinfo 9576 [list desc "Box Drawings Up Double and Horizontal Single" short "boxd_udhzs"] + tcl::dict::set charinfo 9577 [list desc "Box Drawings Double Up and Horizontal" short "boxd_duhz"] + tcl::dict::set charinfo 9578 [list desc "Box Drawings Vertical Single and Horizontal Double" short "boxd_vshzd"] + tcl::dict::set charinfo 9579 [list desc "Box Drawings Vertical Double and Horizontal Single" short "boxd_vdhzs"] + tcl::dict::set charinfo 9580 [list desc "Box Drawings Double Vertical and Horizontal" short "boxd_dvhz"] + tcl::dict::set charinfo 9581 [list desc "Box Drawings Light Arc Down and Right" short "boxd_ladr"] + tcl::dict::set charinfo 9582 [list desc "Box Drawings Light Arc Down and Left" short "boxd_ladl"] + tcl::dict::set charinfo 9583 [list desc "Box Drawings Light Arc Up and Left" short "boxd_laul"] + tcl::dict::set charinfo 9584 [list desc "Box Drawings Light Arc Up and Right" short "boxd_laur"] + tcl::dict::set charinfo 9585 [list desc "Box Drawings Light Diagonal Upper Right To Lower Left" short "boxd_ldgurll"] + tcl::dict::set charinfo 9586 [list desc "Box Drawings Light Diagonal Upper Left To Lower Right" short "boxd_ldgullr"] + tcl::dict::set charinfo 9587 [list desc "Box Drawings Light Diagonal Cross" short "boxd_ldc"] + tcl::dict::set charinfo 9588 [list desc "Box Drawings Light Left" short "boxd_ll"] + tcl::dict::set charinfo 9589 [list desc "Box Drawings Light Up" short "boxd_lu"] + tcl::dict::set charinfo 9590 [list desc "Box Drawings Light Right" short "boxd_lr"] + tcl::dict::set charinfo 9591 [list desc "Box Drawings Light Down" short "boxd_ld"] + tcl::dict::set charinfo 9592 [list desc "Box Drawings Heavy Left" short "boxd_hl"] + tcl::dict::set charinfo 9593 [list desc "Box Drawings Heavy Up" short "boxd_hu"] + tcl::dict::set charinfo 9594 [list desc "Box Drawings Heavy Right" short "boxd_hr"] + tcl::dict::set charinfo 9595 [list desc "Box Drawings Heavy Down" short "boxd_hd"] + tcl::dict::set charinfo 9596 [list desc "Box Drawings Light Left and Heavy Right" short "boxd_llhr"] + tcl::dict::set charinfo 9597 [list desc "Box Drawings Light Up and Heavy Down" short "boxd_luhd"] + tcl::dict::set charinfo 9598 [list desc "Box Drawings Heavy Left and Light Right" short "boxd_hllr"] + tcl::dict::set charinfo 9599 [list desc "Box Drawings Heavy Up and Light Down" short "boxd_huld"] + + + tcl::dict::set charsets "Halfwidth and Fullwidth Forms" [list ranges [list {start 65280 end 65519}] description "Halfwidth and Fullwidth Forms (variants)" settype "tcl_supplemental"] + tcl::dict::set charsets "ascii_fullwidth" [list ranges [list {start 65281 end 65374}] description "Ascii 21 to 7E fullwidth" parentblock "halfwidth_and_fullwidth_forms" settype "other"] + + tcl::dict::set charsets "Specials" [list ranges [list {start 65520 end 65535}] description "Specials" settype "tcl_supplemental"] + + tcl::dict::set charsets "noncharacters" [list ranges [list\ {start 64976 end 65007 note "BMP FDD0..FDEF"}\ {start 65534 end 65535 note "BMP FFFE,FFFF"}\ {start 131070 end 131071 note "plane1 1FFFE,1FFFF"}\ @@ -888,18 +888,18 @@ namespace eval punk::char { variable charshort proc _build_charshort {} { variable charshort - set charshort [dict create] + set charshort [tcl::dict::create] variable charinfo - dict for {k v} $charinfo { - if {[dict exists $v short]} { - set sh [dict get $v short] - if {[dict exists $charshort $sh]} { + tcl::dict::for {k v} $charinfo { + if {[tcl::dict::exists $v short]} { + set sh [tcl::dict::get $v short] + if {[tcl::dict::exists $charshort $sh]} { puts stderr "_build_charshort WARNING character data load duplicate shortcode '$sh'" } - dict set charshort $sh [format %c $k] + tcl::dict::set charshort $sh [format %c $k] } } - return [dict size $charshort] + return [tcl::dict::size $charshort] } _build_charshort @@ -916,35 +916,35 @@ namespace eval punk::char { variable charset_extents_startpoints variable charset_extents_endpoints variable charset_extents_rangenames - set charset_extents_startpoints [dict create] - set charset_extents_endpoints [dict create] - set charset_extents_rangenames [dict create] - dict for {setname setinfo} $charsets { - set ranges [dict get $setinfo ranges] - if {[dict get $setinfo settype] eq "block"} { + set charset_extents_startpoints [tcl::dict::create] + set charset_extents_endpoints [tcl::dict::create] + set charset_extents_rangenames [tcl::dict::create] + tcl::dict::for {setname setinfo} $charsets { + set ranges [tcl::dict::get $setinfo ranges] + if {[tcl::dict::get $setinfo settype] eq "block"} { #unicode block must have a single range #we consider a char a member of the block even if unassigned/reserved (as per unicode documentation) - set start [dict get [lindex $ranges 0] start] - set end [dict get [lindex $ranges 0] end] - if {![dict exists $charset_extents_startpoints $start] || $end ni [dict get $charset_extents_startpoints $start]} { + set start [tcl::dict::get [lindex $ranges 0] start] + set end [tcl::dict::get [lindex $ranges 0] end] + if {![tcl::dict::exists $charset_extents_startpoints $start] || $end ni [tcl::dict::get $charset_extents_startpoints $start]} { #assertion if end wasn't in startpoits list - then start won't be in endpoints list - dict lappend charset_extents_startpoints $start $end - dict lappend charset_extents_endpoints $end $start + tcl::dict::lappend charset_extents_startpoints $start $end + tcl::dict::lappend charset_extents_endpoints $end $start } - dict lappend charset_extents_rangenames ${start},${end} [list $setname 1] + tcl::dict::lappend charset_extents_rangenames ${start},${end} [list $setname 1] } else { #multirange sets/scripts. have holes. Char not a member if it's not explicitly in a defined range. #They should be in order within a set - but we don't assume so set r 1 foreach range $ranges { - set start [dict get $range start] - set end [dict get $range end] - if {![dict exists $charset_extents_startpoints $start] || $end ni [dict get $charset_extents_startpoints $start]} { + set start [tcl::dict::get $range start] + set end [tcl::dict::get $range end] + if {![tcl::dict::exists $charset_extents_startpoints $start] || $end ni [tcl::dict::get $charset_extents_startpoints $start]} { #assertion if end wasn't in startpoits list - then start won't be in endpoints list - dict lappend charset_extents_startpoints $start $end - dict lappend charset_extents_endpoints $end $start + tcl::dict::lappend charset_extents_startpoints $start $end + tcl::dict::lappend charset_extents_endpoints $end $start } - dict lappend charset_extents_rangenames ${start},${end} [list $setname $r] + tcl::dict::lappend charset_extents_rangenames ${start},${end} [list $setname $r] incr r } } @@ -954,7 +954,7 @@ namespace eval punk::char { set charset_extents_startpoints [lsort -stride 2 -integer $charset_extents_startpoints] set charset_extents_endpoints [lsort -stride 2 -integer $charset_extents_endpoints] #no need to sort charset_extents_rangenames - lookup only done using dict methods - return [dict size $charset_extents_startpoints] + return [tcl::dict::size $charset_extents_startpoints] } _build_charset_extents ;#rebuilds for all charsets @@ -982,11 +982,11 @@ namespace eval punk::char { if {[file exists $fname]} { #puts stderr "load_nerdfonts loading $fname" set data [fileutil::cat -translation binary $fname] - set short_seen [dict create] - set current_set_range [dict create] + set short_seen [tcl::dict::create] + set current_set_range [tcl::dict::create] set filesets_loading [list] foreach ln [split $data \n] { - set ln [string trim $ln] + set ln [tcl::string::trim $ln] if {$ln eq ""} {continue} set desc [lassign $ln hex rawsetname] set hexnum 0x$hex @@ -994,36 +994,36 @@ namespace eval punk::char { set setname "nf_$rawsetname" ;#Ensure nerdfont set names are prefixed. if {$setname ni $filesets_loading} { - if {![dict exists $charsets $setname]} { + if {![tcl::dict::exists $charsets $setname]} { #set exists - but not in our filesets_loading list - therefore this set has been previously loaded, so clear old data first dict unset charset $setname } set newrange [list start $dec end $dec] - dict set current_set_range $setname $newrange - dict set charsets $setname [list ranges [list $newrange] description "nerd fonts $rawsetname" settype "nerdfonts privateuse"] + tcl::dict::set current_set_range $setname $newrange + tcl::dict::set charsets $setname [list ranges [list $newrange] description "nerd fonts $rawsetname" settype "nerdfonts privateuse"] lappend filesets_loading $setname } #expects ordered glyph list - set existing_range [dict get $current_set_range $setname] - set existing_end [dict get $existing_range end] + set existing_range [tcl::dict::get $current_set_range $setname] + set existing_end [tcl::dict::get $existing_range end] if {$dec - $existing_end == 1} { #part of current range - dict set current_set_range $setname end $dec + tcl::dict::set current_set_range $setname end $dec #overwrite last ranges element - set rangelist [lrange [dict get $charsets $setname ranges] 0 end-1] - lappend rangelist [dict get $current_set_range $setname] - dict set charsets $setname ranges $rangelist + set rangelist [lrange [tcl::dict::get $charsets $setname ranges] 0 end-1] + lappend rangelist [tcl::dict::get $current_set_range $setname] + tcl::dict::set charsets $setname ranges $rangelist } else { #new range for set - dict set current_set_range $setname start $dec - dict set current_set_range $setname end $dec - set rangelist [dict get $charsets $setname ranges] - lappend rangelist [dict get $current_set_range $setname] - dict set charsets $setname ranges $rangelist + tcl::dict::set current_set_range $setname start $dec + tcl::dict::set current_set_range $setname end $dec + set rangelist [tcl::dict::get $charsets $setname ranges] + lappend rangelist [tcl::dict::get $current_set_range $setname] + tcl::dict::set charsets $setname ranges $rangelist } - if {![dict exists $charinfo $dec]} { + if {![tcl::dict::exists $charinfo $dec]} { # -- --- #review set map [list beaufort bf gibbous gb crescent cr thunderstorm tstorm thermometer thermom] @@ -1042,16 +1042,16 @@ namespace eval punk::char { } set joined_desc [join $normdesc _] #map after join so we can normalize some underscored elements e.g creativecommons & creative_commons - set mapped_desc [string map $map $joined_desc] + set mapped_desc [tcl::string::map $map $joined_desc] set s nf_${rawsetname}_$mapped_desc - if {![dict exists $short_seen $s]} { - dict set short_seen $s {} + if {![tcl::dict::exists $short_seen $s]} { + tcl::dict::set short_seen $s {} } else { #duplicate in the data file (e.g 2023 weather night alt rain mix) set s ${s}_$hex } - dict set charinfo $dec [list desc "$desc" short $s] + tcl::dict::set charinfo $dec [list desc "$desc" short $s] } } _build_charshort @@ -1070,7 +1070,7 @@ namespace eval punk::char { set pkg_base [file dirname $tmfile] return $pkg_base } - namespace eval internal { + tcl::namespace::eval internal { proc unicode_folder {} { set parent [file join [punk::char::package_base] char] set candidates [glob -nocomplain -type d -dir $parent -tail unicode*] @@ -1086,8 +1086,8 @@ namespace eval punk::char { error {wrong # args: should be "dict_getdef dictValue ?key ...? key default"} } set keys [lrange $args 0 end-1] - if {[dict exists $dictValue {*}$keys]} { - return [dict get $dictValue {*}$keys] + if {[tcl::dict::exists $dictValue {*}$keys]} { + return [tcl::dict::get $dictValue {*}$keys] } else { return [lindex $args end] } @@ -1097,7 +1097,7 @@ namespace eval punk::char { #charsets structure - #dict set charsets "halfwidth_and_fullwidth_forms" [list ranges [list {start 65280 end 65519}] description "Halfwidth and Fullwidth Forms (variants) settype block"] + #tcl::dict::set charsets "halfwidth_and_fullwidth_forms" [list ranges [list {start 65280 end 65519}] description "Halfwidth and Fullwidth Forms (variants) settype block"] #unicode Blocks.txt #load the defined blocks into 'charsets' and mark as type 'block'. Unicode blocks have only one range - and don't overlap. @@ -1119,20 +1119,20 @@ namespace eval punk::char { close $fd set block_count 0 foreach ln [split $data \n] { - set ln [string trim $ln] - if {[string match #* $ln]} { + set ln [tcl::string::trim $ln] + if {[tcl::string::match #* $ln]} { continue } - if {[set pcolon [string first ";" $ln]] > 0} { - set lhs [string trim [string range $ln 0 $pcolon-1]] - set name [string trim [string range $ln $pcolon+1 end]] + if {[set pcolon [tcl::string::first ";" $ln]] > 0} { + set lhs [tcl::string::trim [tcl::string::range $ln 0 $pcolon-1]] + set name [tcl::string::trim [tcl::string::range $ln $pcolon+1 end]] set lhsparts [split $lhs .] set start [lindex $lhsparts 0] set end [lindex $lhsparts end] #puts "$start -> $end '$name'" set decimal_start [expr {"0x$start"}] set decimal_end [expr {"0x$end"}] - dict set charsets $name [list ranges [list [list start $decimal_start end $decimal_end note "unicode block $lhs"]] description "" settype block] + tcl::dict::set charsets $name [list ranges [list [list start $decimal_start end $decimal_end note "unicode block $lhs"]] description "" settype block] incr block_count } } @@ -1169,7 +1169,7 @@ namespace eval punk::char { proc charshort {shortname} { variable charshort - return [dict get $charshort $shortname] + return [tcl::dict::get $charshort $shortname] } proc box_drawing {args} { @@ -1180,8 +1180,8 @@ namespace eval punk::char { } proc char_info_hex {hex args} { - set hex [string map [list _ ""] $hex] - if {[string is xdigit -strict $hex]} { + set hex [tcl::string::map [list _ ""] $hex] + if {[tcl::string::is xdigit -strict $hex]} { #has no leading 0x set dec [expr {"0x$hex"}] } else { @@ -1193,19 +1193,19 @@ namespace eval punk::char { #Note - on some versions of Tcl -e.g 8.6 use could supply something like \U1f600 (smiley icon) but we receive fffd (replacement special) #there is no way to detect what the user intended ie we can't distinguish if they actually typed \UFFFD #we can test if such mapping happens in general - and warn if codepoint is FFFD in the result dict - set returninfo [dict create] - if {[string equal \UFFFD $char] && [string equal \U1F600 \UFFFD]} { - dict set returninfo WARNING "this tcl maps multiple to FFFD" + set returninfo [tcl::dict::create] + if {[tcl::string::equal \UFFFD $char] && [tcl::string::equal \U1F600 \UFFFD]} { + tcl::dict::set returninfo WARNING "this tcl maps multiple to FFFD" } lassign [scan $char %c%s] dec_char remainder - if {[string length $remainder]} { + if {[tcl::string::length $remainder]} { error "char_info requires a single character" } - set result [dict merge $returninfo [char_info_dec $dec_char {*}$args]] + set result [tcl::dict::merge $returninfo [char_info_dec $dec_char {*}$args]] } proc char_info_dec {dec args} { set dec_char [expr {$dec}] - set opts [dict create\ + set opts [tcl::dict::create\ -fields {default}\ -except {}\ ] @@ -1217,16 +1217,16 @@ namespace eval punk::char { foreach {k v} $args { switch -- $k { -fields - -except { - dict set opts $k $v + tcl::dict::set opts $k $v } default { - error "char_info unrecognised option '$k'. Known options:'[dict keys $opts]' known_fields: $known_fields usage: char_info ?-fields {}? ?-except {}?" + error "char_info unrecognised option '$k'. Known options:'[tcl::dict::keys $opts]' known_fields: $known_fields usage: char_info ?-fields {}? ?-except {}?" } } } # -- --- --- --- --- --- --- --- --- --- --- --- - set opt_fields [dict get $opts -fields] - set opt_except [dict get $opts -except] + set opt_fields [tcl::dict::get $opts -fields] + set opt_except [tcl::dict::get $opts -except] # -- --- --- --- --- --- --- --- --- --- --- --- set initial_fields [list] if {"default" in $opt_fields} { @@ -1270,51 +1270,51 @@ namespace eval punk::char { variable charinfo variable charsets set hex_char [format %04x $dec_char] - set returninfo [dict create] + set returninfo [tcl::dict::create] foreach f $fields { switch -- $f { dec { - dict set returninfo dec $dec_char + tcl::dict::set returninfo dec $dec_char } hex { - dict set returninfo hex $hex_char + tcl::dict::set returninfo hex $hex_char } desc { - if {[dict exists $charinfo $dec_char desc]} { - dict set returninfo desc [dict get $charinfo $dec_char desc] + if {[tcl::dict::exists $charinfo $dec_char desc]} { + tcl::dict::set returninfo desc [tcl::dict::get $charinfo $dec_char desc] } else { - dict set returninfo desc "" + tcl::dict::set returninfo desc "" } } short { - if {[dict exists $charinfo $dec_char short]} { - dict set returninfo desc [dict get $charinfo $dec_char short] + if {[tcl::dict::exists $charinfo $dec_char short]} { + tcl::dict::set returninfo desc [tcl::dict::get $charinfo $dec_char short] } else { - dict set returninfo short "" + tcl::dict::set returninfo short "" } } testwidth { #todo - expectedwidth - lookup the printing width it is *supposed* to have from unicode tables #testwidth is one of the main ones likely to be worthwhile excluding as querying the console via ansi takes time set existing_testwidth "" - if {[dict exists $charinfo $dec_char testwidth]} { - set existing_testwidth [dict get $charinfo $dec_char testwidth] + if {[tcl::dict::exists $charinfo $dec_char testwidth]} { + set existing_testwidth [tcl::dict::get $charinfo $dec_char testwidth] } if {$existing_testwidth eq ""} { #no cached data - do expensive cursor-position test (Note this might not be 100% reliable - terminals lie e.g if ansi escape sequence has set to wide printing.) set char [format %c $dec_char] set chwidth [char_info_testwidth $char] - dict set returninfo testwidth $chwidth + tcl::dict::set returninfo testwidth $chwidth #cache it. todo - -verify flag to force recalc in case font/terminal changed in some way? - dict set charinfo $dec_char testwidth $chwidth + tcl::dict::set charinfo $dec_char testwidth $chwidth } else { - dict set returninfo testwidth $existing_testwidth + tcl::dict::set returninfo testwidth $existing_testwidth } } char { set char [format %c $dec_char] - dict set returninfo char $char + tcl::dict::set returninfo char $char } memberof { #memberof takes in the order of a few hundred microseconds if a simple scan of all ranges is taken - possibly worthwhile caching/optimising @@ -1323,17 +1323,17 @@ namespace eval punk::char { #We could potentially populate it using a side-thread - but it seems reasonable to just cache result after first use here. #some efficiency could also be gained by pre-calculating the extents for each charset which isn't a simple unicode block. (and perhaps sorting by max char) set memberof [list] - dict for {setname setinfo} $charsets { - foreach r [dict get $setinfo ranges] { - set s [dict get $r start] - set e [dict get $r end] + tcl::dict::for {setname setinfo} $charsets { + foreach r [tcl::dict::get $setinfo ranges] { + set s [tcl::dict::get $r start] + set e [tcl::dict::get $r end] if {$dec_char >= $s && $dec_char <= $e} { lappend memberof $setname break } } } - dict set returninfo memberof $memberof + tcl::dict::set returninfo memberof $memberof } } } @@ -1344,10 +1344,10 @@ namespace eval punk::char { proc _char_info_dec_memberof_scan {dec} { variable charsets set memberof [list] - dict for {setname setinfo} $charsets { - foreach r [dict get $setinfo ranges] { - set s [dict get $r start] - set e [dict get $r end] + tcl::dict::for {setname setinfo} $charsets { + foreach r [tcl::dict::get $setinfo ranges] { + set s [tcl::dict::get $r start] + set e [tcl::dict::get $r end] if {$dec >= $s && $dec <= $e} { lappend memberof $setname break @@ -1359,15 +1359,15 @@ namespace eval punk::char { proc range_split_info {dec} { variable charset_extents_startpoints variable charset_extents_endpoints - set skeys [dict keys $charset_extents_startpoints] - set ekeys [dict keys $charset_extents_endpoints] - set splen [dict size $charset_extents_startpoints] - set eplen [dict size $charset_extents_endpoints] + set skeys [tcl::dict::keys $charset_extents_startpoints] + set ekeys [tcl::dict::keys $charset_extents_endpoints] + set splen [tcl::dict::size $charset_extents_startpoints] + set eplen [tcl::dict::size $charset_extents_endpoints] set s [lsearch -bisect -integer $skeys $dec] set s_at_or_below [lrange $skeys 0 $s] set e_of_s [list] foreach sk $s_at_or_below { - lappend e_of_s {*}[dict get $charset_extents_startpoints $sk] + lappend e_of_s {*}[tcl::dict::get $charset_extents_startpoints $sk] } set e_of_s [lsort -integer $e_of_s] set splitposn [lsearch -bisect -integer $e_of_s $dec] @@ -1376,7 +1376,7 @@ namespace eval punk::char { set reduced_endpoints [lrange $e_of_s $splitposn end] set sps [list] foreach ep $reduced_endpoints { - lappend sps {*}[dict get $charset_extents_endpoints $ep] + lappend sps {*}[tcl::dict::get $charset_extents_endpoints $ep] } @@ -1386,14 +1386,14 @@ namespace eval punk::char { set e_at_or_above [lrange $ekeys $e end] set s_of_e [list] foreach ek $e_at_or_above { - lappend s_of_e {*}[dict get $charset_extents_endpoints $ek] + lappend s_of_e {*}[tcl::dict::get $charset_extents_endpoints $ek] } set startpoints_of_above [llength $s_of_e] set splitposn [lsearch -bisect -integer $s_of_e $dec] set reduced_startpoints [lrange $s_of_e 0 $splitposn] set eps [list] foreach sp $reduced_startpoints { - lappend eps {*}[dict get $charset_extents_startpoints $sp] + lappend eps {*}[tcl::dict::get $charset_extents_startpoints $sp] } } else { set s_of_e [list] @@ -1402,7 +1402,7 @@ namespace eval punk::char { } - return [dict create startpoints $splen endpoints $eplen midpoint [expr {floor($eplen/2)}] posn $e lhs_endpoints_to_check "[llength $reduced_endpoints]/[llength $e_of_s]=[llength $sps]ranges" rhs_startpoints_to_check "[llength $reduced_startpoints]/[llength $s_of_e]=[llength $eps]"] + return [tcl::dict::create startpoints $splen endpoints $eplen midpoint [expr {floor($eplen/2)}] posn $e lhs_endpoints_to_check "[llength $reduced_endpoints]/[llength $e_of_s]=[llength $sps]ranges" rhs_startpoints_to_check "[llength $reduced_startpoints]/[llength $s_of_e]=[llength $eps]"] } #for just a few extra sets such as wgl4 and unicode blocks loaded - this gives e.g 5x + better performance than the simple search above, and up to twice as slow for tcl 8.6 #performance biased towards lower numbered characters (which is not too bad in the context of unicode) @@ -1419,17 +1419,17 @@ namespace eval punk::char { #algorithm should theoretically be a little better with -stride set last_smaller_or_equal_startposn [lsearch -stride 2 -bisect -integer $charset_extents_startpoints $dec] set sets_starting_below [lrange $charset_extents_startpoints 0 $last_smaller_or_equal_startposn+1] ;#+1 to include 2nd element of stridden pair - set endpoints_of_starting_below [lsort -integer [concat {*}[dict values $sets_starting_below]]] + set endpoints_of_starting_below [lsort -integer [concat {*}[tcl::dict::values $sets_starting_below]]] } else { #no -stride available - set startkeys [dict keys $charset_extents_startpoints] + set startkeys [tcl::dict::keys $charset_extents_startpoints] set last_smaller_or_equal_startkeyposn [lsearch -bisect -integer $startkeys $dec] ;#assert will always return one of the keys if number >=0 supplied (last key if > all) #set startkey_found [lindex $startkeys $last_smaller_or_equal_startkeyposn] set start_below_keys [lrange $startkeys 0 $last_smaller_or_equal_startkeyposn] ;#These are the keys of sets which start at or below dec #puts "start_below_keys: '$start_below_keys'" set endpoints_of_starting_below [list] foreach belowkey $start_below_keys { - lappend endpoints_of_starting_below {*}[dict get $charset_extents_startpoints $belowkey] + lappend endpoints_of_starting_below {*}[tcl::dict::get $charset_extents_startpoints $belowkey] } set endpoints_of_starting_below [lsort -integer $endpoints_of_starting_below[unset endpoints_of_starting_below]] } @@ -1446,9 +1446,9 @@ namespace eval punk::char { #we have reduced our set of endpoints sufficiently (to those at or above dec) to run through and test each startpoint set ranges [list] foreach ep $reduced_opposite_limit { - foreach s [dict get $charset_extents_endpoints $ep] { + foreach s [tcl::dict::get $charset_extents_endpoints $ep] { if {$s <= $dec} { - lappend ranges [dict get $charset_extents_rangenames $s,$ep] + lappend ranges [tcl::dict::get $charset_extents_rangenames $s,$ep] } } } @@ -1459,7 +1459,7 @@ namespace eval punk::char { #with glob searching of description and short proc char_range_dict {start end args} { - if {![string is integer -strict $start] || ![string is integer -strict $end]} { + if {![tcl::string::is integer -strict $start] || ![tcl::string::is integer -strict $end]} { error "char_range_dict error start and end must be integers" } set and_globs [list] @@ -1474,32 +1474,32 @@ namespace eval punk::char { } } variable charinfo - set cdict [dict create] + set cdict [tcl::dict::create] set start [expr {$start}] ;#force string rep to decimal - otherwise first use of i as string could be hex or other rep whilst other i values will be decimal string rep due to incr for {set i $start} {$i <= $end} {incr i} { set hx [format %04x $i] set ch [format %c $i] - if {[dict exists $charinfo $i desc]} { - set d [dict get $charinfo $i desc] + if {[tcl::dict::exists $charinfo $i desc]} { + set d [tcl::dict::get $charinfo $i desc] } else { set d "" } - if {[dict exists $charinfo $i short]} { - set s [dict get $charinfo $i short] + if {[tcl::dict::exists $charinfo $i short]} { + set s [tcl::dict::get $charinfo $i short] } else { set s "" } set matchcount 0 foreach glob $and_globs { - if {[string match -nocase $glob $s] || [string match -nocase $glob $d]} { + if {[tcl::string::match -nocase $glob $s] || [tcl::string::match -nocase $glob $d]} { incr matchcount } } if {$matchcount == [llength $and_globs]} { - if {[dict exists $charinfo $i]} { - dict set cdict $hx [dict merge [dict create dec $i hex $hx char $ch] [dict get $charinfo $i]] + if {[tcl::dict::exists $charinfo $i]} { + tcl::dict::set cdict $hx [tcl::dict::merge [tcl::dict::create dec $i hex $hx char $ch] [tcl::dict::get $charinfo $i]] } else { - dict set cdict $hx [list dec $i hex $hx char $ch desc $d short $s] + tcl::dict::set cdict $hx [list dec $i hex $hx char $ch desc $d short $s] } } } @@ -1508,17 +1508,17 @@ namespace eval punk::char { #with glob searches of desc and short proc char_range {start end args} { package require overtype - if {![string is integer -strict $start] || ![string is integer -strict $end]} { + if {![tcl::string::is integer -strict $start] || ![tcl::string::is integer -strict $end]} { error "char_range error start and end must be integers" } set charset_dict [char_range_dict $start $end {*}$args] set out "" - set col3 [string repeat " " 12] - dict for {k inf} $charset_dict { + set col3 [tcl::string::repeat " " 12] + tcl::dict::for {k inf} $charset_dict { set s [internal::dict_getdef $inf short ""] set d [internal::dict_getdef $inf desc ""] set s_col [overtype::left $col3 $s] - append out "$k [dict get $inf dec] [dict get $inf char] $s_col $d" \n + append out "$k [tcl::dict::get $inf dec] [tcl::dict::get $inf char] $s_col $d" \n } return $out } @@ -1530,26 +1530,26 @@ namespace eval punk::char { #todo - more efficient datastructures? if {![regexp {[?*]} $name_or_glob]} { #no glob - just retrieve it - if {[dict exists $charsets $name_or_glob]} { - if {[dict get $charsets $name_or_glob settype] eq "block"} { - return [dict create $name_or_glob [dict get $charsets $name_or_glob]] + if {[tcl::dict::exists $charsets $name_or_glob]} { + if {[tcl::dict::get $charsets $name_or_glob settype] eq "block"} { + return [tcl::dict::create $name_or_glob [tcl::dict::get $charsets $name_or_glob]] } } #no exact match - try case insensitive.. - set name [lsearch -inline -nocase [dict keys $charsets] $name_or_glob] + set name [lsearch -inline -nocase [tcl::dict::keys $charsets] $name_or_glob] if {$name ne ""} { - if {[dict get $charsets $name settype] eq "block"} { - return [dict create $name [dict get $charsets $name]] + if {[tcl::dict::get $charsets $name settype] eq "block"} { + return [tcl::dict::create $name [tcl::dict::get $charsets $name]] } } } else { #build a subset - set charsets_block [dict create] - dict for {k v} $charsets { - if {[string match -nocase $name_or_glob $k]} { - if {[dict get $v settype] eq "block"} { - dict set charsets_block $k $v + set charsets_block [tcl::dict::create] + tcl::dict::for {k v} $charsets { + if {[tcl::string::match -nocase $name_or_glob $k]} { + if {[tcl::dict::get $v settype] eq "block"} { + tcl::dict::set charsets_block $k $v } } } @@ -1560,20 +1560,20 @@ namespace eval punk::char { variable charsets if {![regexp {[?*]} $name_or_glob]} { #no glob - just retrieve it - if {[dict exists $charsets $name_or_glob]} { + if {[tcl::dict::exists $charsets $name_or_glob]} { return [list $name_or_glob] } #no exact match - try case insensitive.. - set name [lsearch -inline -nocase [dict keys $charsets] $name_or_glob] + set name [lsearch -inline -nocase [tcl::dict::keys $charsets] $name_or_glob] if {$name ne ""} { return [list $name] } } else { if {$name_or_glob eq "*"} { - return [lsort [dict keys $charsets]] + return [lsort [tcl::dict::keys $charsets]] } - #dict keys $dict doesn't have option for case insensitive searches - return [lsort [lsearch -all -inline -nocase [dict keys $charsets] $name_or_glob]] + #tcl::dict::keys $dict doesn't have option for case insensitive searches + return [lsort [lsearch -all -inline -nocase [tcl::dict::keys $charsets] $name_or_glob]] } } @@ -1583,8 +1583,8 @@ namespace eval punk::char { proc charset_names2 {{namesearch *}} { variable charsets #dictionary sorting of the keys is slow! - we should obviously store it in sorted order instead of sorting entire list on retrieval - or just sort results - #set sortedkeys [lsort -increasing -dictionary [dict keys $charsets]] ;#NOTE must use -dictionary to use -sorted flag below - set sortedkeys [lsort -increasing [dict keys $charsets]] + #set sortedkeys [lsort -increasing -dictionary [tcl::dict::keys $charsets]] ;#NOTE must use -dictionary to use -sorted flag below + set sortedkeys [lsort -increasing [tcl::dict::keys $charsets]] if {$namesearch eq "*"} { return $sortedkeys } @@ -1602,36 +1602,36 @@ namespace eval punk::char { set charset_names [charset_names $namesearch] set settype_list [list] foreach setname $charset_names { - lappend settype_list [dict get $charsets $setname settype] + lappend settype_list [tcl::dict::get $charsets $setname settype] } set charset_names [linsert $charset_names 0 "Set Name"] set settype_list [linsert $settype_list 0 "Set Type"] - return [textblock::join [list_as_lines -- $charset_names] " " [list_as_lines $settype_list]] + return [textblock::join -- [list_as_lines -- $charset_names] " " [list_as_lines $settype_list]] } proc charset_defget {exactname} { variable charsets - return [dict get $charsets $exactname] + return [tcl::dict::get $charsets $exactname] } proc charset_defs {charsetname} { variable charsets set matches [charset_names $charsetname] set def_list [list] foreach setname $matches { - lappend def_list [dict create $setname [dict get $charsets $setname]] + lappend def_list [tcl::dict::create $setname [tcl::dict::get $charsets $setname]] } return [join $def_list \n] } proc charset_dictget {exactname} { variable charsets - set setinfo [dict get $charsets $exactname] - set ranges [dict get $setinfo ranges] - set charset_dict [dict create] + set setinfo [tcl::dict::get $charsets $exactname] + set ranges [tcl::dict::get $setinfo ranges] + set charset_dict [tcl::dict::create] foreach r $ranges { - set start [dict get $r start] - set end [dict get $r end] - set charset_dict [dict merge $charset_dict [char_range_dict $start $end]] + set start [tcl::dict::get $r start] + set end [tcl::dict::get $r end] + set charset_dict [tcl::dict::merge $charset_dict [char_range_dict $start $end]] } return $charset_dict } @@ -1643,7 +1643,7 @@ namespace eval punk::char { } set dict_list [list] foreach m $matches { - lappend dict_list [dict create $m [charset_dictget $m]] + lappend dict_list [tcl::dict::create $m [charset_dictget $m]] } #return $dict_list return [join $dict_list \n] @@ -1658,14 +1658,14 @@ namespace eval punk::char { if {![llength $matched_names]} { error "charset_page no charset matched pattern '$namesearch' - use 'charset_names' to get list" } - set defaults [dict create\ + set defaults [tcl::dict::create\ -ansi 0\ -lined 1\ ] - set opts [dict merge $defaults $args] + set opts [tcl::dict::merge $defaults $args] # -- --- --- --- - set opt_ansi [dict get $opts -ansi] - set opt_lined [dict get $opts -lined] + set opt_ansi [tcl::dict::get $opts -ansi] + set opt_lined [tcl::dict::get $opts -lined] # -- --- --- --- set re_diacritics {[\u0300-\u036f]+|[\u1ab0-\u1aff]+|[\u1dc0-\u1dff]+|[\u20d0-\u20ff]+|[\ufe20-\ufe2f]+} @@ -1681,18 +1681,18 @@ namespace eval punk::char { append out $prefix foreach charsetname $matched_names { if {[llength $search_this_and_that]} { - set setinfo [dict get $charsets $charsetname] - set ranges [dict get $setinfo ranges] - set charset_dict [dict create] + set setinfo [tcl::dict::get $charsets $charsetname] + set ranges [tcl::dict::get $setinfo ranges] + set charset_dict [tcl::dict::create] foreach r $ranges { - set start [dict get $r start] - set end [dict get $r end] - set charset_dict [dict merge $charset_dict [char_range_dict $start $end {*}$search_this_and_that]] + set start [tcl::dict::get $r start] + set end [tcl::dict::get $r end] + set charset_dict [tcl::dict::merge $charset_dict [char_range_dict $start $end {*}$search_this_and_that]] } } else { set charset_dict [charset_dictget $charsetname] } - if {![dict size $charset_dict]} { + if {![tcl::dict::size $charset_dict]} { continue } set i 1 @@ -1701,12 +1701,12 @@ namespace eval punk::char { set marker_line $prefix set line $prefix - dict for {hex inf} $charset_dict { - set ch [dict get $inf char] + tcl::dict::for {hex inf} $charset_dict { + set ch [tcl::dict::get $inf char] set twidth "" set dec [expr {"0x$hex"}] - if {[dict exists $charinfo $dec testwidth]} { - set twidth [dict get $charinfo $dec testwidth] + if {[tcl::dict::exists $charinfo $dec testwidth]} { + set twidth [tcl::dict::get $charinfo $dec testwidth] } if {$twidth eq ""} { #set width [ansifreestring_width $ch] ;#based on unicode props @@ -1731,23 +1731,23 @@ namespace eval punk::char { set marker "__ " set displayv "${a1}$ch${a2} " } - set hexlen [string length $hex] - append marker_line "[string repeat " " $hexlen] $marker" + set hexlen [tcl::string::length $hex] + append marker_line "[tcl::string::repeat " " $hexlen] $marker" append line "$hex $displayv" - if {$i == [dict size $charset_dict] || $i % $cols == 0} { + if {$i == [tcl::dict::size $charset_dict] || $i % $cols == 0} { if {$opt_lined} { append out $marker_line \n } append out $line \n set marker_line $prefix set line $prefix - #set out [string range $out 0 end-2] + #set out [tcl::string::range $out 0 end-2] #append out \n " " } incr i } } - set out [string trimright $out " "] + set out [tcl::string::trimright $out " "] return $out } @@ -1765,13 +1765,13 @@ namespace eval punk::char { foreach charsetname $matched_names { if {[llength $search_this_and_that]} { - set setinfo [dict get $charsets $charsetname] - set ranges [dict get $setinfo ranges] - set charset_dict [dict create] + set setinfo [tcl::dict::get $charsets $charsetname] + set ranges [tcl::dict::get $setinfo ranges] + set charset_dict [tcl::dict::create] foreach r $ranges { - set start [dict get $r start] - set end [dict get $r end] - set charset_dict [dict merge $charset_dict [char_range_dict $start $end {*}$search_this_and_that]] + set start [tcl::dict::get $r start] + set end [tcl::dict::get $r end] + set charset_dict [tcl::dict::merge $charset_dict [char_range_dict $start $end {*}$search_this_and_that]] } } else { set charset_dict [charset_dictget $charsetname] @@ -1779,22 +1779,22 @@ namespace eval punk::char { set col_items_short [list] set col_items_desc [list] - dict for {k inf} $charset_dict { + tcl::dict::for {k inf} $charset_dict { lappend col_items_desc [internal::dict_getdef $inf desc ""] lappend col_items_short [internal::dict_getdef $inf short ""] } if {[llength $col_items_desc]} { - set widest3 [tcl::mathfunc::max {*}[lmap v $col_items_short {string length $v}]] + set widest3 [tcl::mathfunc::max {*}[lmap v $col_items_short {tcl::string::length $v}]] if {$widest3 == 0} { set col3 " " } else { - set col3 [string repeat " " $widest3] + set col3 [tcl::string::repeat " " $widest3] } - dict for {k inf} $charset_dict { + tcl::dict::for {k inf} $charset_dict { set s [internal::dict_getdef $inf short ""] set d [internal::dict_getdef $inf desc ""] set s_col [overtype::left $col3 $s] - append out "$k [dict get $inf char] $s_col $d" \n + append out "$k [tcl::dict::get $inf char] $s_col $d" \n } } } @@ -1812,44 +1812,44 @@ namespace eval punk::char { } set search_this_and_that $args set charcount 0 - set width_results [dict create] + set width_results [tcl::dict::create] puts stdout "calibrating using terminal cursor movements.." foreach charsetname $matched_names { if {[llength $search_this_and_that]} { - set setinfo [dict get $charsets $charsetname] - set ranges [dict get $setinfo ranges] - set charset_dict [dict create] + set setinfo [tcl::dict::get $charsets $charsetname] + set ranges [tcl::dict::get $setinfo ranges] + set charset_dict [tcl::dict::create] foreach r $ranges { - set start [dict get $r start] - set end [dict get $r end] - set charset_dict [dict merge $charset_dict [char_range_dict $start $end {*}$search_this_and_that]] + set start [tcl::dict::get $r start] + set end [tcl::dict::get $r end] + set charset_dict [tcl::dict::merge $charset_dict [char_range_dict $start $end {*}$search_this_and_that]] } } else { set charset_dict [charset_dictget $charsetname] } - if {![dict size $charset_dict]} { + if {![tcl::dict::size $charset_dict]} { continue } - dict for {hex inf} $charset_dict { + tcl::dict::for {hex inf} $charset_dict { set ch [format %c 0x$hex] set twidth "" set dec [expr {"0x$hex"}] - if {[dict exists $charinfo $dec testwidth]} { - set twidth [dict get $charinfo $dec testwidth] + if {[tcl::dict::exists $charinfo $dec testwidth]} { + set twidth [tcl::dict::get $charinfo $dec testwidth] } if {$twidth eq ""} { #puts -nonewline stdout "." ;#this set width [char_info_testwidth $ch] ;#based on console test rather than unicode props - dict set charinfo $dec testwidth $width + tcl::dict::set charinfo $dec testwidth $width } else { set width $twidth } - dict incr width_results $width + tcl::dict::incr width_results $width incr charcount } } puts stdout "\ncalibration done - results cached in charinfo dictionary" - return [dict create charcount $charcount widths $width_results] + return [tcl::dict::create charcount $charcount widths $width_results] } #maint warning - also in overtype! @@ -1861,19 +1861,19 @@ namespace eval punk::char { proc grapheme_width_cached {ch {key ""}} { variable grapheme_widths #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] + if {[tcl::dict::exists $grapheme_widths $key $ch]} { + return [tcl::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 $key $ch $width + tcl::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] + set grapheme_widths [tcl::dict::create] } else { - dict unset grapheme_widths $key + tcl::dict::unset grapheme_widths $key } return } @@ -1893,7 +1893,7 @@ namespace eval punk::char { if {[punk::ansi::ta::detect $text]} { puts stderr "string_width detected ANSI!" } - if {[string last \n $text] >= 0} { + if {[tcl::string::last \n $text] >= 0} { error "string_width accepts only a single line" } tailcall ansifreestring_width $text @@ -1901,7 +1901,25 @@ namespace eval punk::char { #faster than textutil::wcswidth (at least for string up to a few K in length) proc wcswidth {string} { - set codes [scan $string [string repeat %c [string length $string]]] + set codes [scan $string [tcl::string::repeat %c [tcl::string::length $string]]] + set width 0 + foreach c $codes { + if {$c <= 255} { + incr width + } else { + set w [textutil::wcswidth_char $c] + if {$w < 0} { + return -1 + } else { + incr width $w + } + } + } + return $width + } + #faster than textutil::wcswidth (at least for string up to a few K in length) + proc wcswidth1 {string} { + set codes [scan $string [tcl::string::repeat %c [tcl::string::length $string]]] set width 0 foreach c $codes { set w [textutil::wcswidth_char $c] @@ -1914,7 +1932,7 @@ namespace eval punk::char { return $width } proc wcswidth2 {string} { - set codes [scan $string [string repeat %c [string length $string]]] + set codes [scan $string [tcl::string::repeat %c [tcl::string::length $string]]] set widths [lmap c $codes {textutil::wcswidth_char $c}] if {-1 in $widths} { return -1 @@ -1931,7 +1949,7 @@ namespace eval punk::char { #we can c0 control characters after or while processing ansi escapes. #we need to map remaining control characters to zero-width (under assumption we're not using a font/codepage that displays them - review!) #anyway - we must allow things like raw ESC,DEL, NUL etc to pass through without error - #if {[string first \033 $text] >= 0} { + #if {[tcl::string::first \033 $text] >= 0} { # error "string_width doesn't accept ansi escape sequences. Use punk::ansi::stripansi first" #} @@ -1972,7 +1990,10 @@ namespace eval punk::char { #\uFFEFBOM/ ZWNBSP and others that should be zero width #todo - work out proper way to mark/group zero width. - set text [string map [list \u200b "" \u200c "" \u200d "" \uFFEF ""] $text] + #set text [tcl::string::map [list \u200b "" \u200c "" \u200d "" \uFFEF ""] $text] + set text [tcl::string::map [list \u200b "" \u200c "" \u200d ""] $text] + #\uFFEF tends to print as 1 length replacement char - REVIEW + #\uFFFF varies between terminals - some print replacement char (width 1) some print nothing (width 0) # -- --- --- --- --- --- --- #we should only map control sequences to nothing after processing ones with length effects, such as \b (\x07f) or DEL \x1f @@ -1992,16 +2013,16 @@ namespace eval punk::char { #short-circuit basic cases #support tcl pre 2023-11 - see regexp bug below #if {![regexp {[\uFF-\U10FFFF]} $text]} { - # return [string length $text] + # return [tcl::string::length $text] #} if {![regexp "\[\uFF-\U10FFFF\]" $text]} { - return [string length $text] + return [tcl::string::length $text] } #split just to get the standalone character widths - and then scan for other combiners (?) - or scan for clusters first? #review - #set can_regex_high_unicode [string match [regexp -all -inline {[\U80-\U0010FFFF]} \U0001F525] \U0001F525] + #set can_regex_high_unicode [tcl::string::match [regexp -all -inline {[\U80-\U0010FFFF]} \U0001F525] \U0001F525] #tcl pre 2023-11 - braced high unicode regexes don't work #fixed in bug-4ed788c618 2023-11 #set uc_chars [regexp -all -inline "\[\u0100-\U10FFFF\]" $text] ;#e.g return list of chars in range only @@ -2013,7 +2034,7 @@ namespace eval punk::char { foreach {uc ascii} $uc_sequences { #puts "-ascii $ascii" #puts "-uc $uc" - incr len [string length $ascii] + incr len [tcl::string::length $ascii] #textutil::wcswidth uses unicode data #fall back to textutil::wcswidth (which doesn't for example handle diactricts/combiners so we can't use until these and other things such as \u200b and diacritics are already stripped/accounted for) #todo - find something that understands grapheme clusters - needed also for grapheme_split @@ -2035,7 +2056,7 @@ namespace eval punk::char { #we can c0 control characters after or while processing ansi escapes. #we need to map remaining control characters to zero-width (under assumption we're not using a font/codepage that displays them - review!) #anyway - we must allow things like raw ESC,DEL, NUL etc to pass through without error - #if {[string first \033 $text] >= 0} { + #if {[tcl::string::first \033 $text] >= 0} { # error "string_width doesn't accept ansi escape sequences. Use punk::ansi::stripansi first" #} @@ -2078,10 +2099,10 @@ namespace eval punk::char { #short-circuit basic cases #support tcl pre 2023-11 - see regexp bug below #if {![regexp {[\uFF-\U10FFFF]} $text]} { - # return [string length $text] + # return [tcl::string::length $text] #} if {![regexp "\[\uFF-\U10FFFF\]" $text]} { - return [string length $text] + return [tcl::string::length $text] } #review - wcswidth should detect these @@ -2091,7 +2112,7 @@ namespace eval punk::char { set zerowidth_char_count 0 #split just to get the standalone character widths - and then scan for other combiners (?) #review - #set can_regex_high_unicode [string match [regexp -all -inline {[\U80-\U0010FFFF]} \U0001F525] \U0001F525] + #set can_regex_high_unicode [tcl::string::match [regexp -all -inline {[\U80-\U0010FFFF]} \U0001F525] \U0001F525] #tcl pre 2023-11 - braced high unicode regexes don't work #fixed in bug-4ed788c618 2023-11 #set uc_sequences [regexp -all -inline -indices {[\u0100-\U10FFFF]} $text] @@ -2127,7 +2148,7 @@ namespace eval punk::char { } } #todo - work out what to do with anomalies like grave combiner which print at different lengths on different terminals (fonts?) and for which cursor-query escape sequence lies. - return [expr {[string length $text] + $doublewidth_char_count - $zerowidth_char_count}] + return [expr {[tcl::string::length $text] + $doublewidth_char_count - $zerowidth_char_count}] } #slow - textutil::wcswidth is slow with mixed ascii uc @@ -2139,7 +2160,7 @@ namespace eval punk::char { #we can c0 control characters after or while processing ansi escapes. #we need to map remaining control characters to zero-width (under assumption we're not using a font/codepage that displays them - review!) #anyway - we must allow things like raw ESC,DEL, NUL etc to pass through without error - #if {[string first \033 $text] >= 0} { + #if {[tcl::string::first \033 $text] >= 0} { # error "string_width doesn't accept ansi escape sequences. Use punk::ansi::stripansi first" #} @@ -2173,14 +2194,14 @@ namespace eval punk::char { #short-circuit basic cases #support tcl pre 2023-11 - see regexp bug below #if {![regexp {[\uFF-\U10FFFF]} $text]} { - # return [string length $text] + # return [tcl::string::length $text] #} if {![regexp "\[\uFF-\U10FFFF\]" $text]} { - return [string length $text] + return [tcl::string::length $text] } #slow when ascii mixed with unicode (but why?) - return [punk::wcswidth $text] + return [punk::char::wcswidth $text] } #This shouldn't be called on text containing ansi codes! proc strip_nonprinting_ascii {str} { @@ -2192,7 +2213,7 @@ namespace eval punk::char { \x07 ""\ \x7f ""\ ] - return [string map $map $str] + return [tcl::string::map $map $str] } @@ -2203,25 +2224,25 @@ namespace eval punk::char { # #set re_diacritics {[\u0300-\u036f]+|[\u1ab0-\u1aff]+|[\u1dc0-\u1dff]+|[\u20d0-\u20ff]+|[\ufe20-\ufe2f]+} set graphemes [list] - if {[string length $text] == 0} { + if {[tcl::string::length $text] == 0} { return {} } set list [list] set start 0 - set strlen [string length $text] + set strlen [tcl::string::length $text] #make sure our regexes aren't non-greedy - or we may not have exit condition for loop #review while {$start < $strlen && [regexp -start $start -indices -- {[\u0300-\u036f]+|[\u1ab0-\u1aff]+|[\u1dc0-\u1dff]+|[\u20d0-\u20ff]+|[\ufe20-\ufe2f]+} $text match]} { lassign $match matchStart matchEnd #puts "->start $start ->match $matchStart $matchEnd" - lappend list [string range $text $start $matchStart-1] [string range $text $matchStart $matchEnd] + lappend list [tcl::string::range $text $start $matchStart-1] [tcl::string::range $text $matchStart $matchEnd] set start [expr {$matchEnd+1}] - #if {$start >= [string length $text]} { + #if {$start >= [tcl::string::length $text]} { # break #} } - lappend list [string range $text $start end] + lappend list [tcl::string::range $text $start end] } #ZWJ ZWNJ ? @@ -2241,7 +2262,7 @@ namespace eval punk::char { foreach {pt combiners} [lrange $csplits 0 end-1] { set clist [split $pt ""] lappend graphemes {*}[lrange $clist 0 end-1] - lappend graphemes [string cat [lindex $clist end] $combiners] + lappend graphemes [tcl::string::cat [lindex $clist end] $combiners] } #last csplit never has a combiner (_perlish_split style) - and may be empty - in which case we don't append it as a grapheme if {[lindex $csplits end] ne ""} { @@ -2253,14 +2274,14 @@ namespace eval punk::char { set graphemes [list] set csplits [combiner_split $text] foreach {pt combiners} [lrange $csplits 0 end-1] { - set pt_decs [scan $pt [string repeat %c [string length $pt]]] - set combiner_decs [scan $combiners [string repeat %c [string length $combiners]]] + set pt_decs [scan $pt [tcl::string::repeat %c [tcl::string::length $pt]]] + set combiner_decs [scan $combiners [tcl::string::repeat %c [tcl::string::length $combiners]]] lset pt_decs end [concat [lindex $pt_decs end] $combiner_decs] lappend graphemes {*}$pt_decs } #last csplit never has a combiner (_perlish_split style) - and may be empty - in which case we don't append it as a grapheme if {[lindex $csplits end] ne ""} { - lappend graphemes {*}[scan [lindex $csplits end] [string repeat %c [string length [lindex $csplits end]]]] + lappend graphemes {*}[scan [lindex $csplits end] [tcl::string::repeat %c [tcl::string::length [lindex $csplits end]]]] } return $graphemes } @@ -2268,9 +2289,9 @@ namespace eval punk::char { set graphemes [list] set csplits [combiner_split $text] foreach {pt combiners} $csplits { - set pt_decs [scan $pt [string repeat %c [string length $pt]]] + set pt_decs [scan $pt [tcl::string::repeat %c [tcl::string::length $pt]]] if {$combiners ne ""} { - set combiner_decs [scan $combiners [string repeat %c [string length $combiners]]] + set combiner_decs [scan $combiners [tcl::string::repeat %c [tcl::string::length $combiners]]] lset pt_decs end [concat [lindex $pt_decs end] $combiner_decs] } lappend graphemes {*}$pt_decs @@ -2282,7 +2303,7 @@ namespace eval punk::char { set csplits [combiner_split $text] foreach {pt combiners} [lrange $csplits 0 end-1] { set clist [split $pt ""] - lappend graphemes {*}[lrange $clist 0 end-1] [string cat [lindex $clist end] $combiners] + lappend graphemes {*}[lrange $clist 0 end-1] [tcl::string::cat [lindex $clist end] $combiners] } #last csplit never has a combiner (_perlish_split style) - and may be empty - in which case we don't append it as a grapheme if {[lindex $csplits end] ne ""} { @@ -2303,12 +2324,12 @@ namespace eval punk::char { variable charinfo set dec [scan $char %c] set twidth "" - if {[dict exists $charinfo $dec testwidth]} { - set twidth [dict get $charinfo $dec testwidth] + if {[tcl::dict::exists $charinfo $dec testwidth]} { + set twidth [tcl::dict::get $charinfo $dec testwidth] } if {$twidth eq ""} { set width [char_info_testwidth $char] - dict set charinfo $dec testwidth $width + tcl::dict::set charinfo $dec testwidth $width return $width } else { return $twidth @@ -2316,7 +2337,7 @@ namespace eval punk::char { } proc char_info_is_testwidth_cached {char} { variable charinfo - return [dict exists $charinfo [scan $char %c] testwidth] + return [tcl::dict::exists $charinfo [scan $char %c] testwidth] } # -- --- --- --- --- @@ -2328,7 +2349,7 @@ namespace eval punk::char { # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ ## Ready -package provide punk::char [namespace eval punk::char { +package provide punk::char [tcl::namespace::eval punk::char { variable version set version 0.1.0 }] diff --git a/src/bootsupport/modules/punk/console-0.1.1.tm b/src/bootsupport/modules/punk/console-0.1.1.tm index 7697288..832232b 100644 --- a/src/bootsupport/modules/punk/console-0.1.1.tm +++ b/src/bootsupport/modules/punk/console-0.1.1.tm @@ -20,10 +20,10 @@ package require punk::ansi -if {"windows" eq $::tcl_platform(platform)} { - #package require zzzload - #zzzload::pkg_require twapi -} +#if {"windows" eq $::tcl_platform(platform)} { +# #package require zzzload +# #zzzload::pkg_require twapi +#} #see https://learn.microsoft.com/en-us/windows/console/classic-vs-vt #https://learn.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session @@ -765,6 +765,7 @@ namespace eval punk::console { } ;#end namespace eval internal variable colour_disabled 0 + #todo - move to punk::config # https://no-color.org if {[info exists ::env(NO_COLOR)]} { if {$::env(NO_COLOR) ne ""} { @@ -779,7 +780,7 @@ namespace eval punk::console { #stdout variable ansi_wanted if {$ansi_wanted <= 0} { - puts -nonewline [punk::ansi::stripansi [::punk::ansi::a?]] + puts -nonewline [punk::ansi::stripansiraw [::punk::ansi::a?]] } else { tailcall ansi::a? {*}$args } diff --git a/src/bootsupport/modules/punk/du-0.1.0.tm b/src/bootsupport/modules/punk/du-0.1.0.tm index 121f1fb..538dc86 100644 --- a/src/bootsupport/modules/punk/du-0.1.0.tm +++ b/src/bootsupport/modules/punk/du-0.1.0.tm @@ -901,7 +901,7 @@ namespace eval punk::du { set hlinks [glob -nocomplain -dir $folderpath -types {hidden l} * .*] #set hlinks {} - set links [glob -nocomplain -dir $folderpath -types l * .*] ;#links may have dupes - we don't care. struct::set difference will remove + set links [glob -nocomplain -dir $folderpath -types l * .*] ;#links may have dupes - we don't care. struct::set difference will remove (?) #set links [lsort -unique [concat $hlinks $links[unset links]]] set hfiles [glob -nocomplain -dir $folderpath -types {hidden f} * .*] @@ -913,18 +913,20 @@ namespace eval punk::du { set dirs [glob -nocomplain -dir $folderpath -types d $opt_glob] set hlinks [glob -nocomplain -dir $folderpath -types {hidden l} $opt_glob] - set links [glob -nocomplain -dir $folderpath -types l $opt_glob] ;#links may have dupes - we don't care. struct::set difference will remove + set links [glob -nocomplain -dir $folderpath -types l $opt_glob] ;#links may have dupes - we don't care. struct::set difference will remove (?) set hfiles [glob -nocomplain -dir $folderpath -types {hidden f} $opt_glob] set files [glob -nocomplain -dir $folderpath -types f $opt_glob] } #note struct::set difference produces unordered result - #struct::set difference removes duplicates + #struct::set difference removes duplicates (but not always.. e.g if using tcl impl and 2nd element empty!) + #relying on struct::set to remove dupes is somewhat risky. It is not well documented - and behaviour of dupes in inputs is underspecified as it appears to be intended for mathematical 'sets' #remove links and . .. from directories, remove links from files + #struct::set will affect order: tcl vs critcl give different ordering! set files [struct::set difference [concat $hfiles $files[unset files]] $links] set dirs [struct::set difference [concat $hdirs $dirs[unset dirs]] [concat $links [list [file join $folderpath .] [file join $folderpath ..] ]]] - set links [lsort -unique [concat $links $hlinks]] + #set links [lsort -unique [concat $links $hlinks]] #---- @@ -1022,7 +1024,7 @@ namespace eval punk::du { dict set effective_opts -with_times $timed_types dict set effective_opts -with_sizes $sized_types - return [list dirs $dirs vfsmounts $vfsmounts links $links files $files filesizes [dict get $mdata_lists fsizes] sizes [dict get $mdata_lists allsizes] times $alltimes flaggedhidden {} flaggedsystem {} flaggedreadonly {} altname {} opts $effective_opts errors $errors] + return [list dirs $dirs vfsmounts $vfsmounts links $links files $files filesizes [dict get $mdata_lists fsizes] sizes [dict get $mdata_lists allsizes] times [dict get $mdata_lists alltimes] flaggedhidden {} flaggedsystem {} flaggedreadonly {} altname {} opts $effective_opts errors $errors] } #we can halve the number of round trips on unix-like systems, where 'hidden' always corresponds to dotted files diff --git a/src/bootsupport/modules/punk/fileline-0.1.0.tm b/src/bootsupport/modules/punk/fileline-0.1.0.tm index 54c693d..837b982 100644 --- a/src/bootsupport/modules/punk/fileline-0.1.0.tm +++ b/src/bootsupport/modules/punk/fileline-0.1.0.tm @@ -1555,10 +1555,10 @@ namespace eval punk::fileline::lib { } proc range_boundaries {start end chunksizes args} { - lassign [punk::get_leading_opts_and_values {\ - -offset 0\ - } $args] _opts opts _vals remainingargs - + set argd [punk::args::get_dict { + -offset -default 0 + } $args] + lassign [dict values $argd] opts remainingargs } @@ -1650,16 +1650,19 @@ namespace eval punk::fileline::system { #gets very slow (comparitively) with large resultsets proc _range_spans_chunk_boundaries_tcl {start end chunksize args} { if {$chunksize < 1} {error "chunksize must be > 0"} ;#sanitycheck in case called directly - set defaults [dict create\ + set opts [dict create\ -offset 0\ ] - set known_opts [dict keys $defaults] foreach {k v} $args { - if {$k ni $known_opts} { - error "unknown option '$k'. Known options: $known_opts" + switch -- $k { + -offset { + dict set opts $k $v + } + default { + error "unknown option '$k'. Known options: [dict keys $opts]" + } } } - set opts [dict merge $defaults $args] # -- --- --- --- set opt_offset [dict get $opts -offset] # -- --- --- --- diff --git a/src/bootsupport/modules/punk/lib-0.1.1.tm b/src/bootsupport/modules/punk/lib-0.1.1.tm index c257178..3d0332b 100644 --- a/src/bootsupport/modules/punk/lib-0.1.1.tm +++ b/src/bootsupport/modules/punk/lib-0.1.1.tm @@ -66,11 +66,11 @@ package require Tcl 8.6- # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # oo::class namespace # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -namespace eval punk::lib::class { +tcl::namespace::eval punk::lib::class { #*** !doctools #[subsection {Namespace punk::lib::class}] #[para] class definitions - if {[info commands [namespace current]::interface_sample1] eq ""} { + if {[info commands [tcl::namespace::current]::interface_sample1] eq ""} { #*** !doctools #[list_begin enumerated] @@ -96,46 +96,46 @@ namespace eval punk::lib::class { } # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -namespace eval punk::lib::ensemble { +tcl::namespace::eval punk::lib::ensemble { #wiki.tcl-lang.org/page/ensemble+extend # extend an ensemble-like routine with the routines in some namespace proc extend {routine extension} { if {![string match ::* $routine]} { - set resolved [uplevel 1 [list ::namespace which $routine]] + set resolved [uplevel 1 [list ::tcl::namespace::which $routine]] if {$resolved eq {}} { error [list {no such routine} $routine] } set routine $resolved } - set routinens [namespace qualifiers $routine] + set routinens [tcl::namespace::qualifiers $routine] if {$routinens eq {::}} { set routinens {} } - set routinetail [namespace tail $routine] + set routinetail [tcl::namespace::tail $routine] if {![string match ::* $extension]} { set extension [uplevel 1 [ - list [namespace which namespace] current]]::$extension + list [tcl::namespace::which namespace] current]]::$extension } - if {![namespace exists $extension]} { + if {![tcl::namespace::exists $extension]} { error [list {no such namespace} $extension] } - set extension [namespace eval $extension [ - list [namespace which namespace] current]] + set extension [tcl::namespace::eval $extension [ + list [tcl::namespace::which namespace] current]] - namespace eval $extension [ - list [namespace which namespace] export *] + tcl::namespace::eval $extension [ + list [tcl::namespace::which namespace] export *] while 1 { set renamed ${routinens}::${routinetail}_[info cmdcount] - if {[namespace which $renamed] eq {}} break + if {[tcl::namespace::which $renamed] eq {}} break } rename $routine $renamed - namespace eval $extension [ + tcl::namespace::eval $extension [ list namespace ensemble create -command $routine -unknown [ list apply {{renamed ensemble routine args} { list $renamed $routine @@ -147,7 +147,7 @@ namespace eval punk::lib::ensemble { } } -namespace eval punk::lib::compat { +tcl::namespace::eval punk::lib::compat { #*** !doctools #[subsection {Namespace punk::lib::compat}] #[para] compatibility functions for features that may not be available in earlier Tcl versions @@ -315,8 +315,8 @@ namespace eval punk::lib::compat { } # Bind [string insert] to [::tcl::string::insert]. - namespace ensemble configure string -map [dict replace\ - [namespace ensemble configure string -map]\ + tcl::namespace::ensemble configure string -map [tcl::dict::replace\ + [tcl::namespace::ensemble configure string -map]\ insert ::tcl::string::insert] } #*** !doctools @@ -327,7 +327,7 @@ namespace eval punk::lib::compat { # Base namespace # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ namespace eval punk::lib { - namespace export * + tcl::namespace::export * #variable xyz #*** !doctools @@ -335,15 +335,192 @@ namespace eval punk::lib { #[para] Core API functions for punk::lib #[list_begin definitions] - proc range {from to args} { - if {[info commands lseq] ne ""} { - #tcl 8.7+ lseq significantly faster for larger ranges - return [lseq $from $to] + if {[info commands lseq] ne ""} { + #tcl 8.7+ lseq significantly faster, especially for larger ranges + #support minimal set from to + proc range {from to} { + lseq $from $to + } + } else { + #lseq accepts basic expressions e.g 4-2 for both arguments + #e.g we can do lseq 0 [llength $list]-1 + #if range is to be consistent with the lseq version above - it should support that, even though we don't support most lseq functionality in either wrapper. + proc range {from to} { + set to [offset_expr $to] + set from [offset_expr $from] + if {$to > $from} { + set count [expr {($to -$from) + 1}] + if {$from == 0} { + return [lsearch -all [lrepeat $count 0] *] + } else { + incr from -1 + return [lmap v [lrepeat $count 0] {incr from}] + } + #slower methods. + #2) + #set i -1 + #set L [lrepeat $count 0] + #lmap v $L {lset L [incr i] [incr from];lindex {}} + #return $L + #3) + #set L {} + #for {set i 0} {$i < $count} {incr i} { + # lappend L [incr from] + #} + #return $L + } elseif {$from > $to} { + set count [expr {$from - $to} + 1] + #1) + if {$to == 0} { + return [lreverse [lsearch -all [lrepeat $count 0] *]] + } else { + incr from + return [lmap v [lrepeat $count 0] {incr from -1}] + } + + #2) + #set i -1 + #set L [lrepeat $count 0] + #lmap v $L {lset L [incr i] [incr from -1];lindex {}} + #return $L + #3) + #set L {} + #for {set i 0} {$i < $count} {incr i} { + # lappend L [incr from -1] + #} + #return $L + } else { + return [list $from] + } } - set count [expr {($to -$from) + 1}] - incr from -1 - return [lmap v [lrepeat $count 0] {incr from}] } + + proc pdict {args} { + set argd [punk::args::get_dict { + *proc -name pdict -help {Print dict keys,values to channel + (see also showdict)} + *opts -any 1 + #default separator to provide similarity to tcl's parray function + -separator -default " = " + -channel -default stdout -help "existing channel - or 'none' to return as string" + *values -min 1 -max -1 + dictvar -type string -help "name of dict variable" + patterns -type string -default * -multiple 1 + } $args] + set opts [dict get $argd opts] + set dvar [dict get $argd values dictvar] + set patterns [dict get $argd values patterns] + set dvalue [uplevel 1 [list set $dvar]] + showdict {*}$opts $dvalue {*}$patterns + } + proc showdict {args} { ;# analogous to parray (except that it takes the dict as a value) + set argd [punk::args::get_dict { + *id punk::lib::pdict + *proc -name punk::lib::pdict -help "display dictionary keys and values" + #todo - table tableobject + -return -default "tailtohead" -choices {tailtohead sidebyside} + -channel -default none + -trimright -default 1 -type boolean -help "Trim whitespace off rhs of each line. + This can help prevent a single long line that wraps in terminal from making every line wrap due to long rhs padding + " + -separator -default " " -help "Separator column between keys and values" + -ansibase_keys -default "" + -ansibase_values -default "" + -keysorttype -default "none" -choices {none dictionary ascii integer real} + -keysortdirection -default ascending -choices {ascending descending} + *values -min 1 -max -1 + dictvalue -type dict -help "dict value" + patterns -default * -type string -multiple 1 -help "key or key glob pattern" + } $args] + set opt_sep [dict get $argd opts -separator] + set opt_keysorttype [dict get $argd opts -keysorttype] + set opt_keysortdirection [dict get $argd opts -keysortdirection] + set opt_trimright [dict get $argd opts -trimright] + set opt_ansibase_key [dict get $argd opts -ansibase_keys] + set opt_ansibase_value [dict get $argd opts -ansibase_values] + set opt_return [dict get $argd opts -return] + + set dval [dict get $argd values dictvalue] + set patterns [dict get $argd values patterns] + + set result "" + + set filtered_keys [list] + foreach p $patterns { + lappend filtered_keys {*}[dict keys $dval $p] + } + if {$opt_keysorttype eq "none"} { + #we can only get duplicate keys if there are multiple patterns supplied + #ignore keysortdirection - doesn't apply + if {[llength $patterns] > 1} { + #order-maintaining (order of keys as they appear in dict) + set filtered_keys [punk::lib::lunique $filtered_keys] + } + } else { + set filtered_keys [lsort -unique -$opt_keysorttype $opt_keysortdirection $filtered_keys] + } + + if {[llength $filtered_keys]} { + #both keys and values could have newline characters. + #simple use of 'format' won't cut it for more complex dict keys/values + #use block::width or our columns won't align in some cases + set maxl [::tcl::mathfunc::max {*}[lmap v $filtered_keys {textblock::width $v}]] + set RST [a] + switch -- $opt_return { + "tailtohead" { + #last line of key is side by side (possibly with separator) with first line of value + #This is more intelligible when terminal wrapping occurs - and is closer to what happens with parray multiline keys and values + #we still pad the key to max width so that the separator appears in the same column - which in the case of wide keys could cause that to wrap for all entries + foreach key $filtered_keys { + lassign [textblock::size $key] _kw kwidth _kh kheight + lassign [textblock::size [dict get $dval $key]] _vw vwidth _vh vheight + set totalheight [expr {$kheight + $vheight -1}] + set blanks_above [string repeat \n [expr {$kheight -1}]] + set blanks_below [string repeat \n [expr {$vheight -1}]] + set sepwidth [textblock::width $opt_sep] + #append result [textblock::pad $opt_ansibase_key$key$RST -width $maxl] $opt_sep $opt_ansibase_value[dict get $dval $key]$RST \n + set kblock [textblock::pad $opt_ansibase_key$key$RST$blanks_below -width $maxl] + set sblock [textblock::pad $blanks_above$opt_sep$blanks_below -width $sepwidth] + set vblock $blanks_above$opt_ansibase_value[dict get $dval $key]$RST + #only vblock is ragged - we can do a basic join because we don't care about rhs whitespace + append result [textblock::join_basic $kblock $sblock $vblock] \n + } + } + "sidebyside" { + #This is nice for multiline keys and values of reasonable length, will produce unintuitive results when line-wrapping occurs. + #use ansibase_key etc to make the output more comprehensible in that situation. + #This is why it is not the default. (review - terminal width detection and wrapping?) + foreach key $filtered_keys { + #append result [format "%-*s = %s" $maxl $key [dict get $dval $key]] \n + #differing height blocks (ie ragged) so we need a full textblock::join rather than join_basic + append result [textblock::join -- [textblock::pad $opt_ansibase_key$key$RST -width $maxl] $opt_sep "$opt_ansibase_value[dict get $dval $key]$RST"] \n + } + } + } + } + if {$opt_trimright} { + set result [::join [lines_as_list -line trimright $result] \n] + } + if {[string last \n $result] == [string length $result]-1} { + set result [string range $result 0 end-1] + } + #stdout/stderr can exist but not be in 'chan names' (e.g when transforms in place) + set chan [dict get $argd opts -channel] + switch -- $chan { + stderr - stdout { + puts $chan $result + } + none { + return $result + } + default { + #review - check member of chan names? + #just try outputting to the supplied channel for now + puts $chan $result + } + } + } + proc is_list_all_in_list {small large} { package require struct::list package require struct::set @@ -356,7 +533,87 @@ namespace eval punk::lib { return [expr {[llength $i] == 0}] } + #somewhat like struct::set difference - but order preserving, and doesn't treat as a 'set' so preserves dupes in fromlist + #struct::set difference may happen to preserve ordering when items are integers, but order can't be relied on, + # especially as struct::set has 2 differing implementations (tcl vs critcl) which return results with different ordering to each other and different deduping behaviour in some cases (e.g empty 2nd arg) + proc ldiff {fromlist removeitems} { + if {[llength $removeitems] == 0} {return $fromlist} + set result {} + foreach item $fromlist { + if {$item ni $removeitems} { + lappend result $item + } + } + return $result + } + proc ldiff2 {fromlist removeitems} { + set doomed [list] + foreach item $removeitems { + lappend doomed {*}[lsearch -all -exact $fromlist $item] + } + lremove $fromlist {*}$doomed + } + #non-dupe preserving - for consistency in dedupe behaviour we don't shortcircuit empty B + #consistent dedupe and order-maintenance of remaining items in A differentiate this from struct::set difference + #also struct::set difference with critcl is faster + proc setdiff {A B} { + if {[llength $A] == 0} {return {}} + set d [dict create] + foreach x $A {dict set d $x {}} + foreach x $B {dict unset d $x} + return [dict keys $d] + } + #bulk dict remove is slower than a foreach with dict unset + #proc setdiff2 {fromlist removeitems} { + # #if {[llength $fromlist] == 0} {return {}} + # set d [dict create] + # foreach x $fromlist { + # dict set d $x {} + # } + # return [dict keys [dict remove $d {*}$removeitems]] + #} + #array is about 15% faster than dict - but unordered. (which is ok for sets - but we have struct::set for that) + proc setdiff_unordered {A B} { + if {[llength $A] == 0} {return {}} + array set tmp {} + foreach x $A {::set tmp($x) .} + foreach x $B {catch {unset tmp($x)}} + return [array names tmp] + } + + package require struct::set + if {[struct::set equal [struct::set union {a a} {}] {a}]} { + proc lunique_unordered {list} { + struct::set union $list {} + } + } else { + puts stderr "WARNING: struct::set union no longer dedupes!" + #we could also test a sequence of: struct::set add + proc lunique_unordered {list} { + tailcall lunique $list + } + } + #order-preserving + proc lunique {list} { + set new {} + foreach item $list { + if {$item ni $new} { + lappend new $item + } + } + return $new + } + proc lunique2 {list} { + set doomed [list] + #expr 'in' probably faster than using a dict - for lists approx < 20,000 items. (wiki wisdom - url?) + for {set i 0} {$i < [llength $list]} {} { + set item [lindex $list $i] + lappend doomed {*}[lrange [lsearch -all -exact -start $i $list $item] 1 end] + while {[incr i] in $doomed} {} + } + lremove $list {*}$doomed + } #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] @@ -368,29 +625,29 @@ namespace eval punk::lib { #capture - use uplevel 1 or namespace eval depending on context set capture [uplevel 1 { apply { varnames { - set capturevars [dict create] - set capturearrs [dict create] + set capturevars [tcl::dict::create] + set capturearrs [tcl::dict::create] foreach fullv $varnames { - set v [namespace tail $fullv] + set v [tcl::namespace::tail $fullv] upvar 1 $v var if {[info exists var]} { if {(![array exists var])} { - dict set capturevars $v $var + tcl::dict::set capturevars $v $var } else { - dict set capturearrs capturedarray_$v [array get var] + tcl::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] + return [tcl::dict::create vars $capturevars arrs $capturearrs] } } [info vars] } ] # -- --- --- - set cvars [dict get $capture vars] - set carrs [dict get $capture arrs] + set cvars [tcl::dict::get $capture vars] + set carrs [tcl::dict::get $capture arrs] set apply_script "" - foreach arrayalias [dict keys $carrs] { + foreach arrayalias [tcl::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%] @@ -409,9 +666,9 @@ namespace eval punk::lib { foreach $varnames $list { lappend result {*}[apply\ [list\ - [concat $varnames [dict keys $cvars] [dict keys $carrs] ]\ + [concat $varnames [tcl::dict::keys $cvars] [tcl::dict::keys $carrs] ]\ $apply_script\ - ] {*}[subst $values] {*}[dict values $cvars] {*}[dict values $carrs] ] + ] {*}[subst $values] {*}[tcl::dict::values $cvars] {*}[tcl::dict::values $carrs] ] } return $result } @@ -447,8 +704,15 @@ namespace eval punk::lib { return $result } - proc lmapflat {varnames list script} { - concat {*}[uplevel 1 [list lmap $varnames $list $script]] + #proc lmapflat {varnames list script} { + # concat {*}[uplevel 1 [list lmap $varnames $list $script]] + #} + #lmap can accept multiple var list pairs + proc lmapflat {args} { + concat {*}[uplevel 1 [list lmap {*}$args]] + } + proc lmapflat2 {args} { + concat {*}[uplevel 1 lmap {*}$args] } proc dict_getdef {dictValue args} { @@ -456,8 +720,8 @@ namespace eval punk::lib { error {wrong # args: should be "dict_getdef dictValue ?key ...? key default"} } set keys [lrange $args -1 end-1] - if {[dict exists $dictValue {*}$keys]} { - return [dict get $dictValue {*}$keys] + if {[tcl::dict::exists $dictValue {*}$keys]} { + return [tcl::dict::get $dictValue {*}$keys] } else { return [lindex $args end] } @@ -475,6 +739,23 @@ namespace eval punk::lib { # return "ok" #} + #supports *safe* ultra basic offset expressions as used by lindex etc, but without the 'end' features + #safe in that we don't evaluate the expression as a string. + proc offset_expr {expression} { + set expression [tcl::string::map {_ {}} $expression] + if {[tcl::string::is integer -strict $expression]} { + return [expr {$expression}] + } + if {[regexp {(.*)([+-])(.*)} $expression _match a op b] && [tcl::string::is integer -strict $a] && [tcl::string::is integer -strict $b]} { + if {$op eq "-"} { + return [expr {$a - $b}] + } else { + return [expr {$a + $b}] + } + } else { + error "bad expression '$expression': must be integer?\[+-\]integer?" + } + } proc lindex_resolve {list index} { #*** !doctools @@ -492,7 +773,7 @@ namespace eval punk::lib { if {![llength $list]} { return -1 } - set index [string map [list _ ""] $index] ;#forward compatibility with integers such as 1_000 + set index [tcl::string::map {_ {}} $index] ;#forward compatibility with integers such as 1_000 if {[string is integer -strict $index]} { #can match +i -i if {$index < 0} { @@ -566,7 +847,7 @@ namespace eval punk::lib { } else { #we still don't know the actual integer index for an index such as end-x or int-int without parsing and evaluating ourself. #we can return the value - but only in a way that won't collide with our -1 out-of-range indicator - return [dict create value [lindex $resultlist 0]] + return [tcl::dict::create value [lindex $resultlist 0]] } } @@ -661,17 +942,17 @@ namespace eval punk::lib { if {[llength $argopts]%2 !=0} { error "[namespace current]::hex2dec arguments prior to list_largeHex must be option/value pairs - received '$argopts'" } - set opts [dict create\ + set opts [tcl::dict::create\ -validate 1\ -empty_as_hex "INVALID set -empty_as_hex to a hex string e.g FF if empty values should be replaced"\ ] - set known_opts [dict keys $opts] + set known_opts [tcl::dict::keys $opts] foreach {k v} $argopts { - dict set opts [tcl::prefix match -message "options for hex2dec. Unexpected option" $known_opts $k] $v + tcl::dict::set opts [tcl::prefix match -message "options for hex2dec. Unexpected option" $known_opts $k] $v } # -- --- --- --- - set opt_validate [dict get $opts -validate] - set opt_empty [dict get $opts -empty_as_hex] + set opt_validate [tcl::dict::get $opts -validate] + set opt_empty [tcl::dict::get $opts -empty_as_hex] # -- --- --- --- set list_largeHex [lmap h $list_largeHex[unset list_largeHex] {string map [list _ ""] [string trim $h]}] @@ -710,21 +991,21 @@ namespace eval punk::lib { if {[llength $argopts]%2 !=0} { error "[namespace current]::dec2hex arguments prior to list_decimals must be option/value pairs - received '$argopts'" } - set defaults [dict create\ + set defaults [tcl::dict::create\ -width 1\ -case upper\ -empty_as_decimal "INVALID set -empty_as_decimal to a number if empty values should be replaced"\ ] - set known_opts [dict keys $defaults] - set fullopts [dict create] + set known_opts [tcl::dict::keys $defaults] + set fullopts [tcl::dict::create] foreach {k v} $argopts { - dict set fullopts [tcl::prefix match -message "options for [namespace current]::dec2hex. Unexpected option" $known_opts $k] $v + tcl::dict::set fullopts [tcl::prefix match -message "options for [tcl::namespace::current]::dec2hex. Unexpected option" $known_opts $k] $v } - set opts [dict merge $defaults $fullopts] + set opts [tcl::dict::merge $defaults $fullopts] # -- --- --- --- - set opt_width [dict get $opts -width] - set opt_case [dict get $opts -case] - set opt_empty [dict get $opts -empty_as_decimal] + set opt_width [tcl::dict::get $opts -width] + set opt_case [tcl::dict::get $opts -case] + set opt_empty [tcl::dict::get $opts -empty_as_decimal] # -- --- --- --- @@ -933,35 +1214,35 @@ namespace eval punk::lib { proc sieve n { set primes [list] if {$n < 2} {return $primes} - set nums [dict create] + set nums [tcl::dict::create] for {set i 2} {$i <= $n} {incr i} { - dict set nums $i "" + tcl::dict::set nums $i "" } set next 2 set limit [expr {sqrt($n)}] while {$next <= $limit} { - for {set i $next} {$i <= $n} {incr i $next} {dict unset nums $i} + for {set i $next} {$i <= $n} {incr i $next} {tcl::dict::unset nums $i} lappend primes $next - dict for {next -} $nums break + tcl::dict::for {next -} $nums break } - return [concat $primes [dict keys $nums]] + return [concat $primes [tcl::dict::keys $nums]] } proc sieve2 n { set primes [list] if {$n < 2} {return $primes} - set nums [dict create] + set nums [tcl::dict::create] for {set i 2} {$i <= $n} {incr i} { - dict set nums $i "" + tcl::dict::set nums $i "" } set next 2 set limit [expr {sqrt($n)}] while {$next <= $limit} { - for {set i $next} {$i <= $n} {incr i $next} {dict unset nums $i} + for {set i $next} {$i <= $n} {incr i $next} {tcl::dict::unset nums $i} lappend primes $next #dict for {next -} $nums break set next [lindex $nums 0] } - return [concat $primes [dict keys $nums]] + return [concat $primes [tcl::dict::keys $nums]] } proc hasglobs {str} { @@ -1002,7 +1283,7 @@ namespace eval punk::lib { #[para]This function merges the two dicts whilst maintaining the key order of main followed by defaults. #1st merge (inner merge) with wrong values taking precedence - but right key-order - then (outer merge) restore values - return [dict merge [dict merge $main $defaults] $main] + return [tcl::dict::merge [tcl::dict::merge $main $defaults] $main] } proc askuser {question} { @@ -1044,7 +1325,7 @@ namespace eval punk::lib { set answer [gets stdin] } } finally { - fconfigure stdin -blocking [dict get $stdin_state -blocking] + fconfigure stdin -blocking [tcl::dict::get $stdin_state -blocking] } return $answer } @@ -1162,13 +1443,13 @@ namespace eval punk::lib { } proc list_as_lines2 {args} { #eat or own dogfood version - shows the implementation is simpler - but unfortunately not suitable for a simple function like this which should be as fast as possible? - lassign [dict values [punk::args::get_dict { + lassign [tcl::dict::values [punk::args::get_dict { -joinchar -default \n *values -min 1 -max 1 } $args]] opts values puts "opts:$opts" puts "values:$values" - return [join [dict get $values 0] [dict get $opts -joinchar]] + return [join [tcl::dict::get $values 0] [tcl::dict::get $opts -joinchar]] } proc lines_as_list {args} { @@ -1189,7 +1470,7 @@ namespace eval punk::lib { } else { set opts [lrange $args 0 end-1] } - #set opts [dict merge {-block {}} $opts] + #set opts [tcl::dict::merge {-block {}} $opts] set bposn [lsearch $opts -block] if {$bposn < 0} { lappend opts -block {} @@ -1203,11 +1484,11 @@ namespace eval punk::lib { #-anyopts 1 avoids having to know what to say if odd numbers of options passed etc #we don't have to decide what is an opt vs a value #even if the caller provides the argument -block without a value the next function's validation will report a reasonable error because there is now nothing in $values (consumed by -block) - lassign [dict values [punk::args::get_dict { + lassign [tcl::dict::values [punk::args::get_dict { *opts -any 1 -block -default {} } $args]] opts valuedict - tailcall linelist {*}$opts {*}[dict values $valuedict] + tailcall linelist {*}$opts {*}[tcl::dict::values $valuedict] } # important for pipeline & match_assign @@ -1222,7 +1503,7 @@ namespace eval punk::lib { set text [string map [list \r\n \n] $text] ;#review - option? set arglist [lrange $args 0 end-1] - set opts [dict create\ + set opts [tcl::dict::create\ -block {trimhead1 trimtail1}\ -line {}\ -commandprefix ""\ @@ -1232,7 +1513,7 @@ namespace eval punk::lib { foreach {o v} $arglist { switch -- $o { -block - -line - -commandprefix - -ansiresets - -ansireplays { - dict set opts $o $v + tcl::dict::set opts $o $v } default { error "linelist: Unrecognized option '$o' usage:$usage" @@ -1240,7 +1521,7 @@ namespace eval punk::lib { } } # -- --- --- --- --- --- - set opt_block [dict get $opts -block] + set opt_block [tcl::dict::get $opts -block] if {[llength $opt_block]} { foreach bo $opt_block { switch -- $bo { @@ -1272,7 +1553,7 @@ namespace eval punk::lib { # -- --- --- --- --- --- - set opt_line [dict get $opts -line] + set opt_line [tcl::dict::get $opts -line] set tl_left 0 set tl_right 0 set tl_both 0 @@ -1299,11 +1580,11 @@ namespace eval punk::lib { set tl_both 1 } # -- --- --- --- --- --- - set opt_commandprefix [dict get $opts -commandprefix] + set opt_commandprefix [tcl::dict::get $opts -commandprefix] # -- --- --- --- --- --- - set opt_ansiresets [dict get $opts -ansiresets] + set opt_ansiresets [tcl::dict::get $opts -ansiresets] # -- --- --- --- --- --- - set opt_ansireplays [dict get $opts -ansireplays] + set opt_ansireplays [tcl::dict::get $opts -ansireplays] if {$opt_ansireplays} { if {$opt_ansiresets eq "auto"} { set opt_ansiresets 1 @@ -1414,7 +1695,11 @@ namespace eval punk::lib { set replaycodes $RST ;#todo - default? set transformed [list] #shortcircuit common case of no ansi - if {![punk::ansi::ta::detect $linelist]} { + #NOTE: running ta::detect on a list (or dict) as a whole can be problematic if items in the have backslash escapes due to Tcl list quoting and escaping behaviour. + #This commonly happens if there is an unbalanced brace (which is a normal occurrence and needs to be handled) + #ta::detect on a list of ansi-containing string may appear to work for some simple inputs but is not reliable + #detect_in_list will check at first level. (not intended for detecting ansi in deeper structures) + if {![punk::ansi::ta::detect_in_list $linelist]} { if {$opt_ansiresets} { foreach ln $linelist { lappend transformed $RST$ln$RST @@ -1604,8 +1889,29 @@ namespace eval punk::lib { } #we are interested in seeing jumpTable line and following lines up until next line starting with "Command" or bracketed number e.g (164) - proc show_jump_tables {procname} { - set data [tcl::unsupported::disassemble proc $procname] + proc show_jump_tables {args} { + #avoiding use of 'info cmdtype' as unavaliable in safe interps as at 2024-06. + if {[llength $args] == 1} { + set data [tcl::unsupported::disassemble proc [lindex $args 0]] + } elseif {[llength $args] == 2} { + #review - this looks for direct methods on the supplied object/class, and then tries to disassemble method on the supplied class or class of supplied object if it isn't a class itself. + #not sure if this handles more complex hierarchies or mixins etc. + lassign $args obj method + if {![info object isa object $obj]} { + error "show_jump_tables unable to examine '$args'. $obj is not an oo object" + } + #classes are objects too and can have direct methods + if {$method in [info object methods $obj]} { + set data [tcl::unsupported::disassemble objmethod $obj $method] + } else { + if {![info object isa class $obj]} { + set obj [info object class $obj] + } + set data [tcl::unsupported::disassemble method $obj $method] + } + } else { + error "show_jump_tables expected a procname or a class/object and method" + } set result "" set in_jt 0 foreach ln [split $data \n] { @@ -1626,6 +1932,12 @@ namespace eval punk::lib { return $result } + proc temperature_f_to_c {deg_fahrenheit} { + return [expr {($deg_fahrenheit -32) * (5/9.0)}] + } + proc temperature_c_to_f {deg_celsius} { + return [expr {($deg_celsius * (9/5.0)) + 32}] + } #*** !doctools #[list_end] [comment {--- end definitions namespace punk::lib ---}] } @@ -1639,7 +1951,7 @@ namespace eval punk::lib { #todo - way to generate 'internal' docs separately? #*** !doctools #[section Internal] -namespace eval punk::lib::system { +tcl::namespace::eval punk::lib::system { #*** !doctools #[subsection {Namespace punk::lib::system}] #[para] Internal functions that are not part of the API @@ -1664,6 +1976,51 @@ namespace eval punk::lib::system { return false } } + proc has_safeinterp_compile_bug {{show 0}} { + #ensemble calls within safe interp not compiled + namespace eval [namespace current]::testcompile { + proc ensembletest {} {string index a 0} + } + + set has_bug 0 + + set bytecode_outer [tcl::unsupported::disassemble proc [namespace current]::testcompile::ensembletest] + if {$show} { + puts outer: + puts $bytecode_outer + } + if {![interp issafe]} { + #test of safe subinterp only needed if we aren't already in a safe interp + if {![catch { + interp create x -safe + } errMsg]} { + x eval {proc ensembletest {} {string index a 0}} + set bytecode_safe [x eval {tcl::unsupported::disassemble proc ::ensembletest}] + if {$show} { + puts safe: + puts $bytecode_safe + } + interp delete x + #mainly we expect the safe interp might contain invokeStk - indicating not byte compiled (or we would see strindex instead) + #It's possible the interp we're running in is also not compiling ensembles. + #we could then get a result of 2 - which still indicates a problem + if {[string last "invokeStk" $bytecode_safe] >= 1} { + incr has_bug + } + } else { + #our failure to create a safe interp here doesn't necessarily mean the Tcl version doesn't have the problem - but we could end up returning zero if somehow safe interp can't be created from unsafe interp? + #unlikely - but we should warn + puts stderr "Unable to create a safe sub-interp to test - result only indicates status of current interpreter" + } + } + + namespace delete [namespace current]::testcompile + + if {[string last "invokeStk" $bytecode_outer] >= 1} { + incr has_bug + } + return $has_bug + } proc mostFactorsBelow {n} { ##*** !doctools @@ -1888,7 +2245,7 @@ namespace eval punk::lib::system { } # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ ## Ready -package provide punk::lib [namespace eval punk::lib { +package provide punk::lib [tcl::namespace::eval punk::lib { variable pkg punk::lib variable version set version 0.1.1 diff --git a/src/bootsupport/modules/punk/mix-0.2.tm b/src/bootsupport/modules/punk/mix-0.2.tm index f24deb6..24ef156 100644 --- a/src/bootsupport/modules/punk/mix-0.2.tm +++ b/src/bootsupport/modules/punk/mix-0.2.tm @@ -1,25 +1,31 @@ package require punk::cap -package require punk::cap::handlers::templates ;#handler for templates cap -punk::cap::register_capabilityname punk.templates ::punk::cap::handlers::templates - -package require punk::mix::templates ;#registers as provider pkg for 'punk.templates' capability with punk::cap -if {[catch {punk::mix::templates::provider register *} errM]} { - puts stderr "punk::mix failure during punk::mix::templates::provider register *" - puts stderr $errM - puts stderr "-----" - puts stderr $::errorInfo + +tcl::namespace::eval punk::mix { + proc init {} { + package require punk::cap::handlers::templates ;#handler for templates cap + punk::cap::register_capabilityname punk.templates ::punk::cap::handlers::templates ;#time taken should generally be sub 200us + + package require punk::mix::templates ;#registers as provider pkg for 'punk.templates' capability with punk::cap + set t [time { + if {[catch {punk::mix::templates::provider register *} errM]} { + puts stderr "punk::mix failure during punk::mix::templates::provider register *" + puts stderr $errM + puts stderr "-----" + puts stderr $::errorInfo + } + }] + puts stderr "->punk::mix::templates::provider register * t=$t" + } + init + } package require punk::mix::base package require punk::mix::cli -namespace eval punk::mix { - -} - -package provide punk::mix [namespace eval punk::mix { +package provide punk::mix [tcl::namespace::eval punk::mix { variable version set version 0.2 diff --git a/src/bootsupport/modules/punk/mix/base-0.1.tm b/src/bootsupport/modules/punk/mix/base-0.1.tm index 0a13ad3..6eec4d8 100644 --- a/src/bootsupport/modules/punk/mix/base-0.1.tm +++ b/src/bootsupport/modules/punk/mix/base-0.1.tm @@ -394,7 +394,7 @@ namespace eval punk::mix::base { proc module_subpath {modulename} { set modulename [string trim $modulename :] set nsq [namespace qualifiers $modulename] - return [string map [list :: /] $nsq] + return [string map {:: /} $nsq] } proc get_build_workdir {path} { @@ -420,8 +420,11 @@ namespace eval punk::mix::base { } #not just used by cksum_path. used by caller (e.g fill_relativecksums_from_base_and_relativepathdict via cksum_filter_opts) to determine what opt names passed through + variable cksum_default_opts + set cksum_default_opts [dict create -cksum_content 1 -cksum_meta auto -cksum_acls 0 -cksum_usetar auto -cksum_algorithm sha1] proc cksum_default_opts {} { - return [dict create -cksum_content 1 -cksum_meta auto -cksum_acls 0 -cksum_usetar auto -cksum_algorithm sha1] + variable cksum_default_opts + return $cksum_default_opts } #crc::cksum is extremely slow in tcllib as at 2023 e.g 20x slower (no c implementation?) diff --git a/src/bootsupport/modules/punk/mix/cli-0.3.tm b/src/bootsupport/modules/punk/mix/cli-0.3.tm index 3e941e4..db21a25 100644 --- a/src/bootsupport/modules/punk/mix/cli-0.3.tm +++ b/src/bootsupport/modules/punk/mix/cli-0.3.tm @@ -123,8 +123,9 @@ namespace eval punk::mix::cli { } #review - why can't we be anywhere in the project? + #also - if no make.tcl - can we use the running shell's make.tcl ? (after prompting user?) if {([file tail $sourcefolder] ne "src") || (![file exists $sourcefolder/make.tcl])} { - puts stderr "deck make must be run from src folder containing make.tcl - unable to proceed (cwd: [pwd])" + puts stderr "dev make must be run from src folder containing make.tcl - unable to proceed (cwd: [pwd])" if {[string length $project_base]} { if {[file exists $project_base/src] && [string tolower [pwd]] ne [string tolower $project_base/src]} { puts stderr "Try cd to $project_base/src" @@ -224,7 +225,7 @@ namespace eval punk::mix::cli { # -- --- --- --- --- --- --- --- --- --- --- --- --- --- validate_name_not_empty_or_spaced $modulename -errorprefix $opt_errorprefix - set testname [string map [list :: ""] $modulename] + set testname [string map {:: {}} $modulename] if {[string first : $testname] >=0} { error "$opt_errorprefix '$modulename' can only contain paired colons" } @@ -366,13 +367,13 @@ namespace eval punk::mix::cli { if {"project" in $repotypes} { #punk project if {![catch {package require textblock; package require patternpunk}]} { - set result [textblock::join [>punk . logo] " " $result] + set result [textblock::join -- [>punk . logo] " " $result] append result \n } } set timeline [exec fossil timeline -n 5 -t ci] - set timeline [string map [list \r\n \n] $timeline] + set timeline [string map {\r\n \n} $timeline] append result $timeline if {$opt_v} { set repostate [punk::repo::workingdir_state $repopath -repopaths $repopaths -repotypes fossil] diff --git a/src/bootsupport/modules/punk/mix/commandset/layout-0.1.0.tm b/src/bootsupport/modules/punk/mix/commandset/layout-0.1.0.tm index 621aa00..401ddb7 100644 --- a/src/bootsupport/modules/punk/mix/commandset/layout-0.1.0.tm +++ b/src/bootsupport/modules/punk/mix/commandset/layout-0.1.0.tm @@ -31,7 +31,12 @@ namespace eval punk::mix::commandset::layout { #per layout functions - proc files {layout} { + proc files {{layout ""}} { + set argd [punk::args::get_dict { + *values -min 1 -max 1 + layout -type string -minlen 1 + } [list $layout]] + set allfiles [lib::layout_all_files $layout] return [join $allfiles \n] } @@ -77,6 +82,13 @@ namespace eval punk::mix::commandset::layout { } proc _default {args} { + punk::args::get_dict [subst { + *proc -name ::punk::mix::commandset::layout::collection::_default + -startdir -type string + -not -type string -multiple 1 + globsearches -default * -multiple 1 + }] $args + set tdict_low_to_high [as_dict {*}$args] #convert to screen order - with higher priority at the top set tdict [dict create] diff --git a/src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm b/src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm index 39e9b09..f94bfed 100644 --- a/src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm +++ b/src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm @@ -26,33 +26,102 @@ package require punk::lib namespace eval punk::mix::commandset::loadedlib { namespace export * #search automatically wrapped in * * - can contain inner * ? globs - proc search {searchstring} { + proc search {args} { + set argspecs { + *proc -name "punk::mix::commandset::loadedlib search" -help "search all Tcl libraries available to your local interpreter" + -return -type string -default table -choices {table tableobject list lines} + -present -type integer -default 2 -choices {0 1 2} -choicelabels {absent present both} -help "(unimplemented) Display only those that are 0:absent 1:present 2:both" + -highlight -type boolean -default 1 -help "Highlight which version is present with ansi underline and colour" + -refresh -default 0 -type boolean -help "Re-scan the tm and library folders" + searchstrings -default * -multiple 1 -help "Names to search for, may contain glob chars (* ?) e.g *lib* + If no glob chars are explicitly specified, the searchstring will be wrapped with star globs. + eg name -> *name* + " + } + set argd [punk::args::get_dict $argspecs $args] + set searchstrings [dict get $argd values searchstrings] + set opts [dict get $argd opts] + set opt_return [dict get $opts -return] + set opt_highlight [dict get $opts -highlight] + catch {package require frobznodule666} ;#ensure pkg system has loaded/searched for everything if {[catch {package require natsort}]} { set has_natsort 0 } else { set has_natsort 1 } - if {[regexp {[?*]} $searchstring]} { - #caller has specified specific glob pattern - use it - #todo - respect supplied case only if uppers present? require another flag? - set matches [lsearch -all -inline -nocase [package names] $searchstring] - } else { - #make it easy to search for anything - set matches [lsearch -all -inline -nocase [package names] "*$searchstring*"] + set packages [package names] + set matches [list] + foreach search $searchstrings { + if {[regexp {[?*]} $search]} { + #caller has specified specific glob pattern - use it + #todo - respect supplied case only if uppers present? require another flag? + lappend matches {*}[lsearch -all -inline -nocase $packages $search] + } elseif {[string match =* $search]} { + lappend matches {*}[lsearch -all -inline -exact $packages [string range $search 1 end]] + } else { + #make it easy to search for anything + lappend matches {*}[lsearch -all -inline -nocase $packages "*$search*"] + } } - + set matches [lsort -unique $matches][unset matches] set matchinfo [list] + set highlight_ansi [a+ web-limegreen underline] + set RST [a] foreach m $matches { set versions [package versions $m] + if {![llength $versions]} { + #e.g builtins such as zlib - shows no versions - but will show version when package present/provide used + set versions [package provide $m] + #if {![catch {package present $m} v]} { + # set versions $v + #} + } if {$has_natsort} { set versions [natsort::sort $versions] } else { set versions [lsort $versions] } + if {$opt_highlight} { + set v [package provide $m] + if {$v ne ""} { + set posn [lsearch $versions $v] + if {$posn >= 0} { + #FIXME! (probably in textblock::pad ?) + #TODO - determine why underline is extended to padding even with double reset. (space or other char required to prevent) + set highlighted "$highlight_ansi$v$RST $RST" + set versions [lreplace $versions $posn $posn $highlighted] + } else { + #shouldn't be possible? + puts stderr "failed to find version '$v' in versions:$versions for package $m" + } + } + } lappend matchinfo [list $m $versions] } - return [join [lsort $matchinfo] \n] + switch -- $opt_return { + list { + return $matchinfo + } + lines { + return [join $matchinfo \n] + } + table - tableobject { + set t [textblock::class::table new] + $t add_column -headers "Package" + $t add_column -headers "Version" + $t configure -show_hseps 0 + foreach m $matchinfo { + $t add_row [list [lindex $m 0] [join [lindex $m 1] " "]] + } + if {$opt_return eq "tableobject"} { + return $t + } + set result [$t print] + $t destroy + return $result + } + } } proc loaded.search {searchstring} { set search_result [search $searchstring] @@ -251,7 +320,7 @@ namespace eval punk::mix::commandset::loadedlib { } set loadinfo [package ifneeded $libfound $ver] - set loadinfo [string map [list \r\n \n] $loadinfo] + set loadinfo [string map {\r\n \n} $loadinfo] set loadinfo_lines [split $loadinfo \n] if {[catch {llength $loadinfo}]} { set loadinfo_is_listshaped 0 @@ -316,7 +385,7 @@ namespace eval punk::mix::commandset::loadedlib { #we only follow one level of package require redirection - seems unlikely/imprudent to follow arbitrarily in a while loop(?) set libfound $lib_diversion_name set loadinfo [package ifneeded $libfound $ver] - set loadinfo [string map [list \r\n \n] $loadinfo] + set loadinfo [string map {\r\n \n} $loadinfo] set loadinfo_lines [split $loadinfo \n] if {[catch {llength $loadinfo}]} { set loadinfo_is_listshaped 0 diff --git a/src/bootsupport/modules/punk/mix/commandset/module-0.1.0.tm b/src/bootsupport/modules/punk/mix/commandset/module-0.1.0.tm index 794faf0..56aa815 100644 --- a/src/bootsupport/modules/punk/mix/commandset/module-0.1.0.tm +++ b/src/bootsupport/modules/punk/mix/commandset/module-0.1.0.tm @@ -111,7 +111,7 @@ namespace eval punk::mix::commandset::module { set tablewidth [expr {$widest(name) + 1 + $widest(pathtype) + 1 + $widest(name)}] set table "" append table [string repeat - $tablewidth] \n - append table "[textblock::join [overtype::left $col(name) $title(name)] " " [overtype::left $col(pathtype) $title(pathtype)] " " [overtype::left $col(path) $title(path)]]" \n + append table "[textblock::join -- [overtype::left $col(name) $title(name)] " " [overtype::left $col(pathtype) $title(pathtype)] " " [overtype::left $col(path) $title(path)]]" \n append table [string repeat - $tablewidth] \n foreach n $names pt $pathtypes p $paths { @@ -122,6 +122,14 @@ namespace eval punk::mix::commandset::module { } #return all module templates with repeated ones suffixed with .2 .3 etc proc templates_dict {args} { + set argspec { + *proc -name templates_dict -help "Templates from module and project paths" + -startdir -default "" -help "Project folder used in addition to module paths" + -not -default "" -multiple 1 + *values + globsearches -default * -multiple 1 + } + set argd [punk::args::get_dict $argspec $args] package require punk::cap if {[punk::cap::capability_has_handler punk.templates]} { set template_folder_dict [punk::cap::call_handler punk.templates get_itemdict_moduletemplates {*}$args] @@ -129,18 +137,26 @@ namespace eval punk::mix::commandset::module { put stderr "get_template_basefolders WARNING - no handler available for the 'punk.templates' capability - template providers will be unable to provide template locations" } } - proc new {module args} { + proc new {args} { set year [clock format [clock seconds] -format %Y] - set defaults [list\ - -project \uFFFF\ - -version \uFFFF\ - -license \ - -template punk.module\ - -type \uFFFF\ - -force 0\ - -quiet 0\ - ] - set opts [dict merge $defaults $args] + set moduletypes [punk::mix::cli::lib::module_types] + # use \uFFFD because unicode replacement char should consistently render as 1 wide + set argspecs [subst { + -project -default \uFFFD + -version -default \uFFFD + -license -default + -template -default punk.module + -type -default \uFFFD -choices {$moduletypes} + -force -default 0 -type boolean + -quiet -default 0 -type boolean + *values -min 1 -max 1 + module -type string + }] + set argd [punk::args::get_dict $argspecs $args] + lassign [dict values $argd] opts values + set module [dict get $values module] + + #set opts [dict merge $defaults $args] #todo - review compatibility between -template and -type #-type is the wrapping technology e.g 'plain' for none or tarjar/zipkit etc (consider also snappy/snappy-tcl) @@ -152,7 +168,7 @@ namespace eval punk::mix::commandset::module { # we need this value before looking at the named argument # -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- set opt_version_supplied [dict get $opts -version] - if {$opt_version_supplied eq "\uFFFF"} { + if {$opt_version_supplied eq "\uFFFD"} { set opt_version "0.1.0" } else { set opt_version $opt_version_supplied @@ -178,7 +194,7 @@ namespace eval punk::mix::commandset::module { } else { set vmsg "from -version option: $opt_version_supplied" } - if {$opt_version_supplied ne "\uFFFF"} { + if {$opt_version_supplied ne "\uFFFD"} { if {$vcompare_is_mversion_bigger != 0} { #is bigger or smaller puts stderr "module.new WARNING: version supplied in module argument as well as -version option. Using the higher version number $vmsg" @@ -269,7 +285,7 @@ namespace eval punk::mix::commandset::module { # -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- set opt_type [dict get $opts -type] - if {$opt_type eq "\uFFFF"} { + if {$opt_type eq "\uFFFD"} { set opt_type [lindex [punk::mix::cli::lib::module_types] 0] ;#default to plain } if {$opt_type ni [punk::mix::cli::lib::module_types]} { diff --git a/src/bootsupport/modules/punk/mix/commandset/scriptwrap-0.1.0.tm b/src/bootsupport/modules/punk/mix/commandset/scriptwrap-0.1.0.tm index 9ac7896..c61db42 100644 --- a/src/bootsupport/modules/punk/mix/commandset/scriptwrap-0.1.0.tm +++ b/src/bootsupport/modules/punk/mix/commandset/scriptwrap-0.1.0.tm @@ -73,7 +73,7 @@ namespace eval punk::mix::commandset::scriptwrap { set tablewidth [expr {$widest(name) + 1 + $widest(pathtype) + $widest(path)}] set table "" append table [string repeat - $tablewidth] \n - append table [textblock::join [overtype::left $col(name) $title(name)] " " [overtype::left $col(pathtype) $title(pathtype)] " " [overtype::left $col(path) $title(path)]] \n + append table [textblock::join -- [overtype::left $col(name) $title(name)] " " [overtype::left $col(pathtype) $title(pathtype)] " " [overtype::left $col(path) $title(path)]] \n append table [string repeat - $tablewidth] \n foreach n $names pt $pathtypes p $paths { diff --git a/src/bootsupport/modules/punk/mix/templates-0.1.0.tm b/src/bootsupport/modules/punk/mix/templates-0.1.0.tm index 5521ad8..dab5312 100644 --- a/src/bootsupport/modules/punk/mix/templates-0.1.0.tm +++ b/src/bootsupport/modules/punk/mix/templates-0.1.0.tm @@ -59,7 +59,7 @@ namespace eval punk::mix::templates { oo::objdefine provider { method register {{capabilityname_glob *}} { #puts registering punk::mix::templates $capabilityname - next + next $capabilityname_glob } method capabilities {} { next diff --git a/src/bootsupport/modules/punk/mix/templates/layouts/project/src/make.tcl b/src/bootsupport/modules/punk/mix/templates/layouts/project/src/make.tcl index f4eef65..c53315e 100644 --- a/src/bootsupport/modules/punk/mix/templates/layouts/project/src/make.tcl +++ b/src/bootsupport/modules/punk/mix/templates/layouts/project/src/make.tcl @@ -281,7 +281,7 @@ if {$::punkmake::command eq "bootsupport"} { foreach {relpath module} $bootsupport_modules { set module [string trim $module :] - set module_subpath [string map [list :: /] [namespace qualifiers $module]] + set module_subpath [string map {:: /} [namespace qualifiers $module]] set srclocation [file join $projectroot $relpath $module_subpath] #puts stdout "$relpath $module $module_subpath $srclocation" set pkgmatches [glob -nocomplain -dir $srclocation -tail [namespace tail $module]-*] @@ -617,7 +617,7 @@ if {[file exists $mapfile]} { fconfigure $fdmap -translation binary set mapdata [read $fdmap] close $fdmap - set mapdata [string map [list \r\n \n] $mapdata] + set mapdata [string map {\r\n \n} $mapdata] set missing [list] foreach ln [split $mapdata \n] { set ln [string trim $ln] diff --git a/src/bootsupport/modules/punk/ns-0.1.0.tm b/src/bootsupport/modules/punk/ns-0.1.0.tm index 5dbacbc..10a8d9a 100644 --- a/src/bootsupport/modules/punk/ns-0.1.0.tm +++ b/src/bootsupport/modules/punk/ns-0.1.0.tm @@ -20,12 +20,12 @@ package require punk::lib package require punk::args -namespace eval ::punk_dynamic::ns { +tcl::namespace::eval ::punk_dynamic::ns { } # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -namespace eval punk::ns { +tcl::namespace::eval punk::ns { variable ns_current "::" variable ns_re_cache [dict create] ;#cache regular expressions used in globmatchns namespace export nsjoin nsprefix nstail nsparts nseval nsimport_noclobber corp @@ -58,7 +58,7 @@ namespace eval punk::ns { set has_globchars [regexp {[*?]} $ns_or_glob] if {$is_absolute} { if {!$has_globchars} { - if {![namespace exists $ns_or_glob]} { + if {![tcl::namespace::exists $ns_or_glob]} { error "cannot change to namespace $ns_or_glob" } set ns_current $ns_or_glob @@ -71,7 +71,7 @@ namespace eval punk::ns { } else { if {!$has_globchars} { set nsnext [nsjoin $ns_current $ns_or_glob] - if {![namespace exists $nsnext]} { + if {![tcl::namespace::exists $nsnext]} { error "cannot change to namespace $ns_or_glob" } set ns_current $nsnext @@ -86,7 +86,7 @@ namespace eval punk::ns { set ns_display "\n$ns_queried" if {$ns_current eq $ns_queried} { if {$ns_current in [info commands $ns_current] } { - if {![catch [list namespace ensemble configure $ns_current] ensemble_info]} { + if {![catch [list tcl::namespace::ensemble configure $ns_current] ensemble_info]} { if {[llength $ensemble_info] > 0} { #this namespace happens to match ensemble command. #todo - keep cache of encountered ensembles from commands.. and examine namespace in the configure info. @@ -119,13 +119,13 @@ namespace eval punk::ns { set nspath [nsjoinall $ns_current {*}$args] } - set ns_exists [nseval [nsprefix $nspath] [list ::namespace exists [nstail $nspath] ]] + set ns_exists [nseval [nsprefix $nspath] [list ::tcl::namespace::exists [nstail $nspath] ]] if {$ns_exists} { error "Namespace $nspath already exists" } - #namespace eval [nsprefix $nspath] [list namespace eval [nstail $nspath] {}] - nseval [nsprefix $nspath] [list ::namespace eval [nstail $nspath] {}] + #tcl::namespace::eval [nsprefix $nspath] [list tcl::namespace::eval [nstail $nspath] {}] + nseval [nsprefix $nspath] [list ::tcl::namespace::eval [nstail $nspath] {}] n/ $nspath } @@ -157,7 +157,7 @@ namespace eval punk::ns { } #recursive nseval - for introspection of weird namespace trees - #approx 10x slower than normal namespace eval - but still only a few microseconds.. fine for repl introspection + #approx 10x slower than normal tcl::namespace::eval - but still only a few microseconds.. fine for repl introspection proc nseval_script {location} { set parts [nsparts $location] if {[lindex $parts 0] eq ""} { @@ -171,7 +171,7 @@ namespace eval punk::ns { set i 0 set tails [lrepeat [llength $parts] ""] foreach ns $parts { - set cmdlist [list ::namespace eval $ns] + set cmdlist [list ::tcl::namespace::eval $ns] set t "" if {$i > 0} { append body " " @@ -194,7 +194,7 @@ namespace eval punk::ns { set scr {[::list ::eval [::uplevel {::set script}]]} set up [expr {$i - 1}] - set scr [string map [list $up] $scr] + set scr [string map " $up" $scr] set body [string map [list