Browse Source

update bootsupport/vfs/layouts

master
Julian Noble 3 months ago
parent
commit
2746fc666c
  1. 746
      src/bootsupport/modules/punk-0.1.tm
  2. 177
      src/bootsupport/modules/punk/ansi-0.1.1.tm
  3. 153
      src/bootsupport/modules/punk/args-0.2.1.tm
  4. 1550
      src/bootsupport/modules/punk/args/moduledoc/tclcore-0.1.0.tm
  5. 40
      src/bootsupport/modules/punk/assertion-0.1.0.tm
  6. 4
      src/bootsupport/modules/punk/du-0.1.0.tm
  7. 12
      src/bootsupport/modules/punk/lib-0.1.3.tm
  8. 8
      src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm
  9. 2328
      src/bootsupport/modules/punk/ns-0.1.0.tm
  10. 58
      src/bootsupport/modules/punk/repl-0.1.2.tm
  11. 4
      src/bootsupport/modules/punk/repl/codethread-0.1.1.tm
  12. 18
      src/bootsupport/modules/punk/unixywindows-0.1.0.tm
  13. 12
      src/bootsupport/modules/punk/winpath-0.1.0.tm
  14. 746
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk-0.1.tm
  15. 177
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/ansi-0.1.1.tm
  16. 153
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/args-0.2.1.tm
  17. 1550
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/args/moduledoc/tclcore-0.1.0.tm
  18. 40
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/assertion-0.1.0.tm
  19. 4
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/du-0.1.0.tm
  20. 12
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/lib-0.1.3.tm
  21. 8
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm
  22. 2328
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/ns-0.1.0.tm
  23. 58
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/repl-0.1.2.tm
  24. 4
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/repl/codethread-0.1.1.tm
  25. 18
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/unixywindows-0.1.0.tm
  26. 12
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/winpath-0.1.0.tm
  27. 746
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk-0.1.tm
  28. 177
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/ansi-0.1.1.tm
  29. 153
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/args-0.2.1.tm
  30. 1550
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/args/moduledoc/tclcore-0.1.0.tm
  31. 40
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/assertion-0.1.0.tm
  32. 4
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/du-0.1.0.tm
  33. 12
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/lib-0.1.3.tm
  34. 8
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm
  35. 2328
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/ns-0.1.0.tm
  36. 58
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/repl-0.1.2.tm
  37. 4
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/repl/codethread-0.1.1.tm
  38. 18
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/unixywindows-0.1.0.tm
  39. 12
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/winpath-0.1.0.tm
  40. 1940
      src/vfs/_vfscommon.vfs/modules/patterndispatcher-1.2.4.tm
  41. 664
      src/vfs/_vfscommon.vfs/modules/patternpredator1-1.0.tm
  42. 664
      src/vfs/_vfscommon.vfs/modules/patternpredator1-1.2.4.tm
  43. 746
      src/vfs/_vfscommon.vfs/modules/punk-0.1.tm
  44. 177
      src/vfs/_vfscommon.vfs/modules/punk/ansi-0.1.1.tm
  45. 153
      src/vfs/_vfscommon.vfs/modules/punk/args-0.2.1.tm
  46. 1550
      src/vfs/_vfscommon.vfs/modules/punk/args/moduledoc/tclcore-0.1.0.tm
  47. 315
      src/vfs/_vfscommon.vfs/modules/punk/args/moduledoc/tkcore-0.1.1.tm
  48. 40
      src/vfs/_vfscommon.vfs/modules/punk/assertion-0.1.0.tm
  49. 4
      src/vfs/_vfscommon.vfs/modules/punk/du-0.1.0.tm
  50. 12
      src/vfs/_vfscommon.vfs/modules/punk/lib-0.1.3.tm
  51. 8
      src/vfs/_vfscommon.vfs/modules/punk/mix/commandset/loadedlib-0.1.0.tm
  52. 2328
      src/vfs/_vfscommon.vfs/modules/punk/ns-0.1.0.tm
  53. 58
      src/vfs/_vfscommon.vfs/modules/punk/repl-0.1.2.tm
  54. 4
      src/vfs/_vfscommon.vfs/modules/punk/repl/codethread-0.1.1.tm
  55. 18
      src/vfs/_vfscommon.vfs/modules/punk/unixywindows-0.1.0.tm
  56. 12
      src/vfs/_vfscommon.vfs/modules/punk/winpath-0.1.0.tm
  57. 16
      src/vfs/_vfscommon.vfs/modules/punk/winrun-0.1.0.tm
  58. BIN
      src/vfs/_vfscommon.vfs/modules/treeobj-1.3.1.tm

746
src/bootsupport/modules/punk-0.1.tm

@ -496,23 +496,25 @@ namespace eval punk {
#-----------------------------------------------------------------------------------
#get a copy of the item without affecting internal rep
proc objclone {obj} {
proc valcopy {obj} {
append obj2 $obj {}
}
proc set_clone {varname obj} {
#maintenance: also punk::lib::set_clone
#e.g used by repl's codeinterp. Maintains internal rep, easier to call e.g interp eval code [list punk::set_clone varnmame $val]
proc set_valcopy {varname obj} {
#maintenance: also punk::lib::set_valcopy
#e.g used by repl's codeinterp. Maintains internal rep, easier to call e.g interp eval code [list punk::set_valcopy varnmame $val]
append obj2 $obj {}
uplevel 1 [list set $varname $obj2]
}
interp alias "" strlen "" ::punk::strlen
interp alias "" str_len "" ::punk::strlen
interp alias "" objclone "" ::punk::objclone
interp alias "" valcopy "" ::punk::valcopy
#proc ::strlen {str} {
# string length [append str2 $str {}]
#}
#proc ::objclone {obj} {
#proc ::valcopy {obj} {
# append obj2 $obj {}
#}
@ -629,10 +631,24 @@ namespace eval punk {
-- -type none
@values
pattern -type string -help\
"regex pattern to match in plaintext portion of ANSI string
{regex pattern to match in plaintext portion of ANSI string
The pattern may contain bracketed capturing groups, which
will be highlighted (todo) in the result. If there is no capturing
group, the entire match will be highlighted."
will be highlighted in the result. If there is no capturing
group, the entire match will be highlighted.
Note that if we were to attempt to highlight curly braces based
on the regexp {\{|\}} then the inserted ansi would come between
the backslash and brace in cases where a curly brace is escaped
ie \{ or \}
Depending on how the output is used, this can break the syntactic
structure causing problems.
Instead a pair of regexes such as
{^\{|[^\\](\{+)}
{[^\\](\}+)}
should be used to
exclude braces that are escaped.
(note the capturing groups around each curly brace)
}
string -type string
}
proc grepstr {args} {
@ -706,9 +722,12 @@ namespace eval punk {
}
if {$lineindex in $matched_line_indices} {
set plain_ln [lindex $plainlines $lineindex]
#first test the regexp with a single match to determine number of capturing groups
set matchparts [regexp {*}$nocase -inline -- $pattern $plain_ln] ;#get fullmatch capturegroup...
set numgroups [expr {[llength $matchparts] -1}]
#first - determine the number of capturing groups (subexpressions)
#option 1: test the regexp with a single match
#set testparts [regexp {*}$nocase -inline -- $pattern $plain_ln] ;#get fullmatch capturegroup...
#set numgroups [expr {[llength $testparts] -1}]
#option 2: use the regexp -about flag
set numgroups [lindex [regexp -about $pattern] 0]
set allparts [regexp -all {*}$nocase -indices -inline -- $pattern $plain_ln]
#allparts includes each full match as well as each capturing group
@ -730,9 +749,14 @@ namespace eval punk {
# restrict ourself to just the capture groups, excluding the full match (if there are capture groups)
set highlight_ranges [list]
set i 0
#{-1 -1} returned for non-matching group when there are capture-group alternatives
#e.g {(a)|(b)}
foreach range $allparts {
if {($i % ($numgroups+1)) != 0} {
lappend highlight_ranges $range
lassign $range a b
if {$range ne {-1 -1} & $a <= $b} {
lappend highlight_ranges $range
}
}
incr i
}
@ -917,10 +941,8 @@ namespace eval punk {
return [twapi::new_uuid]
}
}
#get last command result that was run through the repl
proc ::punk::get_runchunk {args} {
set argd [punk::args::parse $args withdef {
namespace eval argdoc {
punk::args::define {
@id -id ::punk::get_runchunk
@cmd -name "punk::get_runchunk" -help\
"experimental"
@ -928,7 +950,19 @@ namespace eval punk {
-1 -optional 1 -type none
-2 -optional 1 -type none
@values -min 0 -max 0
}]
}
}
#get last command result that was run through the repl
proc ::punk::get_runchunk {args} {
#set argd [punk::args::parse $args withdef {
# @id -id ::punk::get_runchunk
# @cmd -name "punk::get_runchunk" -help\
# "experimental"
# @opts
# -1 -optional 1 -type none
# -2 -optional 1 -type none
# @values -min 0 -max 0
#}]
#todo - make this command run without truncating previous runchunks
set runchunks [tsv::array names repl runchunks-*]
@ -4148,7 +4182,7 @@ namespace eval punk {
#pipealias should capture the namespace context of the pipeline so that commands are resolved in the namespace in which the pipealias is created
proc pipealias {targetcmd args} {
set cmdcopy [punk::objclone $args]
set cmdcopy [punk::valcopy $args]
set nscaller [uplevel 1 [list namespace current]]
tailcall interp alias {} $targetcmd {} apply [list args [append cmdcopy " {*}\$args"] $nscaller]
}
@ -4159,7 +4193,7 @@ namespace eval punk {
}
#although the pipealias2 'concat' alias is cleaner in that the original pipeline can be extracted using list commands - it runs much slower
proc pipealias2 {targetcmd args} {
set cmdcopy [punk::objclone $args]
set cmdcopy [punk::valcopy $args]
set nscaller [uplevel 1 [list namespace current]]
tailcall interp alias {} $targetcmd {} apply [list args [concat "\[concat" [list $cmdcopy] "\$args]"] $nscaller]
}
@ -6654,15 +6688,16 @@ namespace eval punk {
#interp alias {} ~ {} file join $::env(HOME) ;#HOME must be capitalized to work cross platform (lowercase home works on windows - but probably not elsewhere)
#interp alias {} ~ {} apply {args {file join $::env(HOME) $args}}
proc ~ {args} {
set hdir [punk::objclone $::env(HOME)]
set hdir [punk::valcopy $::env(HOME)]
file pathtype $hdir
set d $hdir
#use the file join 2-arg optimisation to avoid losing path-rep - probably doesn't give any advantage on all Tcl versions
#review - for what versions does/did the 2-arg version of file join not just return a string?
foreach a $args {
set d [file join $d $a]
}
file pathtype $d
return [punk::objclone $d]
return [punk::valcopy $d]
}
interp alias {} ~ {} punk::~
@ -7789,47 +7824,61 @@ namespace eval punk {
namespace eval argdoc {
punk::args::define {
@id -id ::punk::help_chunks
@cmd -name "punk::help_chunks"\
-summary\
""\
-help\
""
@opts
-- -type none
@values -min 0 -max -1
arg -type any -optional 1 -multiple 1
}
}
proc help {args} {
set chunks [uplevel 1 [list ::punk::help_chunks {*}$args]]
foreach chunk $chunks {
lassign $chunk chan text
puts -nonewline $chan $text
}
}
#return list of {chan chunk} elements
proc help_chunks {args} {
set chunks [list]
set linesep [string repeat - 76]
set mascotblock ""
catch {
package require patternpunk
#lappend chunks [list stderr [>punk . rhs]]
append mascotblock [textblock::frame -checkargs 0 [>punk . banner -title "Punk Shell" -left Tcl -right [package provide Tcl]]]
set argd [punk::args::parse $args withid ::punk::help_chunks]
lassign [dict values $argd] leaders opts values received
if {[dict exists $values arg]} {
set topicparts [dict get $values arg]
} else {
set topicparts [list ""]
}
#set topic [lindex $args end]
#set argopts [lrange $args 0 end-1]
set topic [lindex $args end]
set argopts [lrange $args 0 end-1]
set chunks [list]
set linesep [string repeat - 76]
set warningblock ""
set title "[a+ brightgreen] Punk core navigation commands: "
set I [punk::ansi::a+ italic]
set NI [punk::ansi::a+ noitalic]
#todo - load from source code annotation?
# -------------------------------------------------------
set logoblock ""
if {[catch {
package require patternpunk
#lappend chunks [list stderr [>punk . rhs]]
append logoblock [textblock::frame -title "Punk Shell [package provide punk]" -width 29 -checkargs 0 [>punk . banner -title "" -left Tcl -right [package provide Tcl]]]
}]} {
append logoblock [textblock::frame -title "Punk Shell [package provide punk]" -subtitle "TCL [package provide Tcl]" -width 29 -height 10 -checkargs 0 ""]
}
set title "[a+ brightgreen] Help System: "
set cmdinfo [list]
lappend cmdinfo [list help "?topics?" "This help. To see available subitems type: help topics"]
lappend cmdinfo [list i "cmd ?subcommand...?" "Show usage for a command or ensemble subcommand"]
lappend cmdinfo [list s "cmd ?subcommand...?" "Show synopsis for a command or ensemble subcommand"]
lappend cmdinfo [list eg "cmd ?subcommand...?" "Show example from manpage"]
lappend cmdinfo [list ./ "?subdir?" "view/change directory"]
lappend cmdinfo [list ../ "" "go up one directory"]
lappend cmdinfo [list ./new "subdir" "make new directory and switch to it"]
lappend cmdinfo [list n/ "?glob...?" "view/change namespace\n (accepts ns path globs e.g **::*get* to match comands at any level )"]
lappend cmdinfo [list n// "" "view/change namespace (with command listing)"]
lappend cmdinfo [list "nn/" "" "go up one namespace"]
lappend cmdinfo [list "n/new" "<ns>" "make child namespace and switch to it"]
lappend cmdinfo [list dev "?subcommand?" "(ensemble command to make new projects/modules and to generate docs)"]
lappend cmdinfo [list a? "?subcommand...?" "view ANSI colours\n e.g a? web"]
#set cmds [lsearch -all -inline -index 0 -subindices $cmdinfo *]
#set descr [lsearch -all -inline -index 1 -subindices $cmdinfo *]
#set widest1 [tcl::mathfunc::max {*}[lmap v $cmds {string length $v}]]
#set widest2 [tcl::mathfunc::max {*}[lmap v $descr {string length $v}]]
set t [textblock::class::table new -show_seps 0]
#foreach c $cmds d $descr {
# $t add_row [list $c $d]
#}
lappend cmdinfo [list help "?${I}topic${NI}?" "This help.\nTo see available subitems type:\nhelp topics\n\n\n\n\n"]
set t [textblock::class::table new -minwidth 51 -show_seps 0]
foreach row $cmdinfo {
$t add_row $row
}
@ -7838,272 +7887,387 @@ namespace eval punk {
set width_1 [$t column_datawidth 1]
$t configure_column 1 -minwidth [expr {$width_1 + 1}]
$t configure -title $title
set text ""
append text [$t print]
set warningblock ""
set introblock $mascotblock
append introblock \n $text
#if {[catch {package require textblock} errM]} {
# append warningblock \n "WARNING: textblock package couldn't be loaded. Side-by-side display not available"
#} else {
# set introblock [textblock::join -- " " \n$mascotblock " " $text]
#}
lappend chunks [list stdout $introblock]
if {$topic in [list tcl]} {
if {[punk::lib::check::has_tclbug_script_var]} {
append warningblock \n "minor warning: punk::lib::check::has_tclbug_script_var returned true! (string rep for list variable in script generated when script changed)"
}
if {[punk::lib::check::has_tclbug_safeinterp_compile]} {
set indent " "
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_safeinterp returned true!" \n
append warningblock "${indent}(ensemble commands not compiled in safe interps - heavy performance impact in safe interps)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/1095bf7f75]"
append warningblock [a]
}
if {[catch {lsearch -stride 2 {a b} b}]} {
set indent " "
append warningblock \n "[a+ web-red]warning: lsearch does not seem to support -stride option" \n
append warningblock "${indent}(Consider upgrading to a late release of tcl 8.6 or tcl 9+ )" \n
append warningblock [a]
} else {
if {[punk::lib::check::has_tclbug_lsearch_strideallinline]} {
set text [$t print]
set introblock [textblock::join -- $logoblock $text]
lappend chunks [list stdout $introblock\n]
# -------------------------------------------------------
switch -- [lindex $topicparts 0] {
"" {
# -------------------------------------------------------
set title "[a+ brightgreen] Filesystem navigation: "
set cmdinfo [list]
lappend cmdinfo [list ./ "?${I}subdir${NI}?" "view/change directory"]
lappend cmdinfo [list ../ "" "go up one directory"]
lappend cmdinfo [list ./new "${I}subdir${NI}" "make new directory and switch to it"]
set t [textblock::class::table new -minwidth 80 -show_seps 0]
foreach row $cmdinfo {
$t add_row $row
}
set width_0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$width_0 + 2}]
set width_1 [$t column_datawidth 1]
$t configure_column 1 -minwidth [expr {$width_1 + 1}]
$t configure -title $title
set text ""
append text [$t print]
lappend chunks [list stdout $text\n]
# -------------------------------------------------------
# -------------------------------------------------------
set title "[a+ brightgreen] Namespace navigation: "
set cmdinfo [list]
lappend cmdinfo [list n/ "?${I}ns${NI}|${I}glob${NI}?" "view/change namespace\n (accepts ns path globs e.g **::*get* to match\n commands at any level )"]
lappend cmdinfo [list n// "?${I}ns${NI}|${I}glob${NI}?" "view/change namespace (with command listing)"]
lappend cmdinfo [list "nn/" "" "go up one namespace"]
lappend cmdinfo [list "n/new" "${I}ns${NI}" "make child namespace and switch to it"]
set t [textblock::class::table new -minwidth 80 -show_seps 0]
foreach row $cmdinfo {
$t add_row $row
}
set width_0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$width_0 + 2}]
set width_1 [$t column_datawidth 1]
$t configure_column 1 -minwidth [expr {$width_1 + 1}]
$t configure -title $title
set text ""
append text [$t print]
lappend chunks [list stdout $text\n]
# -------------------------------------------------------
# -------------------------------------------------------
set title "[a+ brightgreen] Command help: "
set cmdinfo [list]
lappend cmdinfo [list i "${I}cmd${NI} ?${I}subcommand${NI}...?" "Show usage for a command or ensemble subcommand"]
lappend cmdinfo [list s "${I}cmd${NI} ?${I}subcommand${NI}...?" "Show synopsis for a command or ensemble subcommand"]
lappend cmdinfo [list eg "${I}cmd${NI} ?${I}subcommand${NI}...?" "Show example from manpage"]
lappend cmdinfo [list corp "${I}proc${NI}" "View proc body and arguments"]
set t [textblock::class::table new -minwidth 80 -show_seps 0]
foreach row $cmdinfo {
$t add_row $row
}
set width_0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$width_0 + 2}]
set width_1 [$t column_datawidth 1]
$t configure_column 1 -minwidth [expr {$width_1 + 1}]
$t configure -title $title
set text ""
append text [$t print]
lappend chunks [list stdout $text\n]
# -------------------------------------------------------
set title "[a+ brightgreen] Miscellaneous: "
#todo - load from source code annotation?
set cmdinfo [list]
lappend cmdinfo [list dev "?${I}subcommand${NI}?" "(ensemble command to make new projects/modules and\n to generate docs)"]
lappend cmdinfo [list a? "?${I}subcommand${NI}...?" "view ANSI colours\n e.g a? web\n or individual code samples/diagnostics\n e.g a? red bold\n e.g a? underline web-orange Term-purple3"]
lappend cmdinfo [list a+ "?${I}colourcode${NI}...?" "Return ANSI codes\n e.g puts \"\[a+ purple\]purple\[a+ Green\]purple on green\[a\]\"\n [a+ purple]purple[a+ Green]purple on green[a] "]
lappend cmdinfo [list a "?${I}colourcode${NI}...?" "Return ANSI codes (with leading reset)\n e.g puts \"\[a+ purple\]purple\[a Green\]normal on green\[a\]\"\n [a+ purple]purple[a Green]normal on green[a] "]
set t [textblock::class::table new -minwidth 80 -show_seps 0]
foreach row $cmdinfo {
$t add_row $row
}
set width_0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$width_0 + 2}]
set width_1 [$t column_datawidth 1]
$t configure_column 1 -minwidth [expr {$width_1 + 1}]
$t configure -title $title
set text ""
append text [$t print]
lappend chunks [list stdout $text]
# -------------------------------------------------------
}
tcl {
set text "Tcl Patchlevel: [info patchlevel]"
catch {
append text \n "Tcl build-info: [::tcl::build-info]"
}
if {[punk::lib::check::has_tclbug_script_var]} {
append warningblock \n "minor warning: punk::lib::check::has_tclbug_script_var returned true! (string rep for list variable in script generated when script changed)"
}
if {[punk::lib::check::has_tclbug_safeinterp_compile]} {
set indent " "
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_lsearch_strideallinline returned true!" \n
append warningblock "${indent}(lsearch using -stride -all -inline -subindices does not return values corresponding to subindex when a single -index value is used)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/5a1aaa201d]"
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_safeinterp returned true!" \n
append warningblock "${indent}(ensemble commands not compiled in safe interps - heavy performance impact in safe interps)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/1095bf7f75]"
append warningblock [a]
}
}
if {[punk::lib::check::has_tclbug_list_quoting_emptyjoin]} {
set indent " "
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_list_quoting returned true!" \n
append warningblock "${indent}lists elements not properly quoted in some cases. e.g 'list {*}[lindex {etc #foo} 1] {*}[list]' (#foo not braced)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/e38dce74e2]"
}
}
set text ""
if {$topic in [list env environment]} {
#todo - move to punk::config?
upvar ::punk::config::punk_env_vars_config punkenv_config
upvar ::punk::config::other_env_vars_config otherenv_config
set known_punk [dict keys $punkenv_config]
set known_other [dict keys $otherenv_config]
append text \n
set usetable 1
if {$usetable} {
set t [textblock::class::table new -show_hseps 0 -show_header 1 -ansiborder_header [a+ web-green]]
if {"windows" eq $::tcl_platform(platform)} {
#If any env vars have been set to empty string - this is considered a deletion of the variable on windows.
#The Tcl ::env array is linked to the underlying process view of the environment
#- but info exists ::env(var) can misreport as true if it has been deleted by setting to empty string rather than using unset.
#an 'array get' will resynchronise.
#Even if an env variable didn't exist before - setting it to empty string can get it to this inconsistent state.
array get ::env
}
#do an array read on ::env
foreach {v vinfo} $punkenv_config {
if {[info exists ::env($v)]} {
set c2 [set ::env($v)]
} else {
set c2 "(NOT SET)"
}
set help ""
if {[dict exists $vinfo help]} {
set help [dict get $vinfo help]
if {[catch {lsearch -stride 2 {a b} b}]} {
set indent " "
append warningblock \n "[a+ web-red]warning: lsearch does not seem to support -stride option" \n
append warningblock "${indent}(Consider upgrading to a late release of tcl 8.6 or tcl 9+ )" \n
append warningblock [a]
} else {
if {[punk::lib::check::has_tclbug_lsearch_strideallinline]} {
set indent " "
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_lsearch_strideallinline returned true!" \n
append warningblock "${indent}(lsearch using -stride -all -inline -subindices does not return values corresponding to subindex when a single -index value is used)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/5a1aaa201d]"
append warningblock [a]
}
$t add_row [list $v $c2 $help]
}
$t configure_column 0 -headers [list "Punk environment vars"]
$t configure_column 0 -minwidth [expr {[$t column_datawidth 0]+4}] -blockalign left -textalign left -header_colspans {any}
if {[punk::lib::check::has_tclbug_list_quoting_emptyjoin]} {
set indent " "
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_list_quoting returned true!" \n
append warningblock "${indent}lists elements not properly quoted in some cases. e.g 'list {*}[lindex {etc #foo} 1] {*}[list]' (#foo not braced)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/e38dce74e2]"
}
lappend chunks [list stdout $text]
}
env - environment {
set text ""
#todo - move to punk::config?
upvar ::punk::config::punk_env_vars_config punkenv_config
upvar ::punk::config::other_env_vars_config otherenv_config
set known_punk [dict keys $punkenv_config]
set known_other [dict keys $otherenv_config]
append text \n
set usetable 1
if {$usetable} {
set t [textblock::class::table new -show_hseps 0 -show_header 1 -ansiborder_header [a+ web-green]]
if {"windows" eq $::tcl_platform(platform)} {
#If any env vars have been set to empty string - this is considered a deletion of the variable on windows.
#The Tcl ::env array is linked to the underlying process view of the environment
#- but info exists ::env(var) can misreport as true if it has been deleted by setting to empty string rather than using unset.
#an 'array get' will resynchronise.
#Even if an env variable didn't exist before - setting it to empty string can get it to this inconsistent state.
array get ::env
}
#do an array read on ::env
foreach {v vinfo} $punkenv_config {
if {[info exists ::env($v)]} {
set c2 [set ::env($v)]
} else {
set c2 "(NOT SET)"
}
set help ""
if {[dict exists $vinfo help]} {
set help [dict get $vinfo help]
}
$t add_row [list $v $c2 $help]
}
$t configure_column 0 -headers [list "Punk environment vars"]
$t configure_column 0 -minwidth [expr {[$t column_datawidth 0]+4}] -blockalign left -textalign left -header_colspans {any}
set punktable [$t print]
$t destroy
set punktable [$t print]
$t destroy
set t [textblock::class::table new -show_hseps 0 -show_header 1 -ansiborder_header [a+ web-green]]
foreach {v vinfo} $otherenv_config {
if {[info exists ::env($v)]} {
set env_val [set ::env($v)]
if {[string match "*_TM_PATH" $v]} {
set entries [split $env_val $::tcl_platform(pathSeparator)]
set c2 [join $entries \n]
set t [textblock::class::table new -show_hseps 0 -show_header 1 -ansiborder_header [a+ web-green]]
foreach {v vinfo} $otherenv_config {
if {[info exists ::env($v)]} {
set env_val [set ::env($v)]
if {[string match "*_TM_PATH" $v]} {
set entries [split $env_val $::tcl_platform(pathSeparator)]
set c2 [join $entries \n]
} else {
set c2 $::env($v)
}
} else {
set c2 $::env($v)
set c2 "(NOT SET)"
}
} else {
set c2 "(NOT SET)"
$t add_row [list $v $c2]
}
$t add_row [list $v $c2]
}
$t configure_column 0 -headers [list "Other environment vars"]
$t configure_column 0 -minwidth [expr {[$t column_datawidth 0]+4}] -blockalign left -textalign left -header_colspans {any}
$t configure_column 0 -headers [list "Other environment vars"]
$t configure_column 0 -minwidth [expr {[$t column_datawidth 0]+4}] -blockalign left -textalign left -header_colspans {any}
set othertable [$t print]
$t destroy
#append text [textblock::join -- $punktable " " $othertable]\n
append text $punktable\n$othertable\n
} else {
set othertable [$t print]
$t destroy
#append text [textblock::join -- $punktable " " $othertable]\n
append text $punktable\n$othertable\n
} else {
append text $linesep\n
append text "punk environment vars:\n"
append text $linesep\n
set col1 [string repeat " " 25]
set col2 [string repeat " " 50]
foreach v $known_punk {
set c1 [overtype::left $col1 $v]
if {[info exists ::env($v)]} {
set c2 [overtype::left $col2 [set ::env($v)]]
} else {
set c2 [overtype::right $col2 "(NOT SET)"]
}
append text "$c1 $c2\n"
}
append text $linesep\n
}
lappend chunks [list stdout $text]
}
if {$topic in [list console terminal]} {
set indent [string repeat " " [string length "WARNING: "]]
lappend cstring_tests [dict create\
type "PM "\
msg "UN"\
f7 punk::ansi::controlstring_PM\
f7prefix "7bit ESC ^ secret "\
f7suffix "safe"\
f8 punk::ansi::controlstring_PM8\
f8prefix "8bit \\x9e secret "\
f8suffix "safe"\
]
lappend cstring_tests [dict create\
type SOS\
msg "NOT"\
f7 punk::ansi::controlstring_SOS\
f7prefix "7bit ESC X string "\
f7suffix " hidden"\
f8 punk::ansi::controlstring_SOS8\
f8prefix "8bit \\x98 string "\
f8suffix " hidden"\
]
lappend cstring_tests [dict create\
type APC\
msg "NOT"\
f7 punk::ansi::controlstring_APC\
f7prefix "7bit ESC _ APPLICATION PROGRAM COMMAND "\
f7suffix " hidden"\
f8 punk::ansi::controlstring_APC8\
f8prefix "8bit \\x9f APPLICATION PROGRAM COMMAND "\
f8suffix " hidden"\
]
foreach test $cstring_tests {
set m [[dict get $test f7] [dict get $test msg]]
set hidden_width_m [punk::console::test_char_width $m]
set m8 [[dict get $test f8] [dict get $test msg]]
set hidden_width_m8 [punk::console::test_char_width $m8]
if {$hidden_width_m != 0 || $hidden_width_m8 != 0} {
if {$hidden_width_m == 0} {
set d "[a+ green bold][dict get $test f7prefix][a red]${m}[a][a+ green bold][dict get $test f7suffix][a]"
} else {
set d "[a+ yellow bold][dict get $test f7prefix][a red]$m[a][a+ yellow bold][dict get $test f7suffix][a]"
append text $linesep\n
append text "punk environment vars:\n"
append text $linesep\n
set col1 [string repeat " " 25]
set col2 [string repeat " " 50]
foreach v $known_punk {
set c1 [overtype::left $col1 $v]
if {[info exists ::env($v)]} {
set c2 [overtype::left $col2 [set ::env($v)]]
} else {
set c2 [overtype::right $col2 "(NOT SET)"]
}
append text "$c1 $c2\n"
}
append text $linesep\n
}
lappend chunks [list stdout $text]
}
console - term - terminal {
set indent [string repeat " " [string length "WARNING: "]]
lappend cstring_tests [dict create\
type "PM "\
msg "UN"\
f7 punk::ansi::controlstring_PM\
f7prefix "7bit ESC ^ secret "\
f7suffix "safe"\
f8 punk::ansi::controlstring_PM8\
f8prefix "8bit \\x9e secret "\
f8suffix "safe"\
]
lappend cstring_tests [dict create\
type SOS\
msg "NOT"\
f7 punk::ansi::controlstring_SOS\
f7prefix "7bit ESC X string "\
f7suffix " hidden"\
f8 punk::ansi::controlstring_SOS8\
f8prefix "8bit \\x98 string "\
f8suffix " hidden"\
]
lappend cstring_tests [dict create\
type APC\
msg "NOT"\
f7 punk::ansi::controlstring_APC\
f7prefix "7bit ESC _ APPLICATION PROGRAM COMMAND "\
f7suffix " hidden"\
f8 punk::ansi::controlstring_APC8\
f8prefix "8bit \\x9f APPLICATION PROGRAM COMMAND "\
f8suffix " hidden"\
]
foreach test $cstring_tests {
set m [[dict get $test f7] [dict get $test msg]]
set hidden_width_m [punk::console::test_char_width $m]
set m8 [[dict get $test f8] [dict get $test msg]]
set hidden_width_m8 [punk::console::test_char_width $m8]
if {$hidden_width_m != 0 || $hidden_width_m8 != 0} {
if {$hidden_width_m == 0} {
set d "[a+ green bold][dict get $test f7prefix][a red]${m}[a][a+ green bold][dict get $test f7suffix][a]"
} else {
set d "[a+ yellow bold][dict get $test f7prefix][a red]$m[a][a+ yellow bold][dict get $test f7suffix][a]"
}
if {$hidden_width_m8 == 0} {
set d8 "[a+ green ][dict get $test f8prefix][a red]$m8[a][a+ green][dict get $test f8suffix][a]"
} else {
set d8 "[a+ yellow bold][dict get $test f8prefix][a red]$m8[a][a+ yellow bold][dict get $test f8suffix][a]"
}
append warningblock \n "WARNING: terminal doesn't hide all [dict get $test type] control strings: $d $d8"
}
if {$hidden_width_m8 == 0} {
set d8 "[a+ green ][dict get $test f8prefix][a red]$m8[a][a+ green][dict get $test f8suffix][a]"
} else {
set d8 "[a+ yellow bold][dict get $test f8prefix][a red]$m8[a][a+ yellow bold][dict get $test f8suffix][a]"
}
if {![catch {punk::console::check::has_bug_legacysymbolwidth} result]} {
if {$result} {
append warningblock \n "WARNING: terminal has legacysymbolwidth bug - screen position for symbol reports 2 wide but displays 1 wide."
append warningblock \n $indent "Layout using 'legacy symbols for computing' affected."
append warningblock \n $indent "(e.g textblock frametype block2 unsupported)"
append warningblock \n $indent "This can cause extreme layout deformation when ANSI is present"
append warningblock \n $indent "In some cases unwanted spacing effects occur at a distance from the characters causing it"
}
append warningblock \n "WARNING: terminal doesn't hide all [dict get $test type] control strings: $d $d8"
} else {
append warningblock \n "WARNING: terminal unable to check for legacysymbolwidth bug. err:$result"
}
}
if {![catch {punk::console::check::has_bug_legacysymbolwidth} result]} {
if {$result} {
append warningblock \n "WARNING: terminal has legacysymbolwidth bug - screen position for symbol reports 2 wide but displays 1 wide."
append warningblock \n $indent "Layout using 'legacy symbols for computing' affected."
append warningblock \n $indent "(e.g textblock frametype block2 unsupported)"
append warningblock \n $indent "This can cause extreme layout deformation when ANSI is present"
append warningblock \n $indent "In some cases unwanted spacing effects occur at a distance from the characters causing it"
if {![catch {punk::console::check::has_bug_zwsp} result]} {
if {$result} {
append warningblock \n "WARNING: terminal has zero width space (\\u200b) bug - cursor position incremented when it shouldn't be."
append warningblock \n $indent "The zwsp may or may not be displayed. zwsp contributes to line length and wrapping point"
}
} else {
append warningblock \n "WARNING: terminal unable to check for zwsp bug. err:$result"
}
} else {
append warningblock \n "WARNING: terminal unable to check for legacysymbolwidth bug. err:$result"
}
if {![catch {punk::console::check::has_bug_zwsp} result]} {
if {$result} {
append warningblock \n "WARNING: terminal has zero width space (\\u200b) bug - cursor position incremented when it shouldn't be."
append warningblock \n $indent "The zwsp may or may not be displayed. zwsp contributes to line length and wrapping point"
set grapheme_support [punk::console::grapheme_cluster_support]
#mode, 1 = set, 2 = unset. (0 = mode not recognised, 3 = permanently set, 4 = permanently unset)
if {![dict size $grapheme_support] || [dict get $grapheme_support mode] eq "unsupported" } {
append warningblock \n "WARNING: terminal either doesn't support grapheme clusters, or doesn't report so via decmode 2027 query."
if {[dict size $grapheme_support] && [dict get $grapheme_support available]} {
append warningblock \n $indent "(but punk::console::grapheme_cluster_support has determined it is probably available)"
}
} else {
if {![dict get $grapheme_support available]} {
switch -- [dict get $grapheme_support mode] {
"unset" {
append warningblock \n "WARNING: terminal reports via decmode 2027 that grapheme cluster support is off."
}
"permanently_unset" {
append warningblock \n "WARNING: terminal reports via decmode 2027 that grapheme cluster support is permanently off."
}
"BAD_RESPONSE" {
append warningblock \n "WARNING: terminal doesn't seem to recognize decmode 2027 query. No grapheme cluster support."
}
}
}
}
if {![string length $warningblock]} {
set text "No terminal warnings\n"
lappend chunks [list stdout $text]
}
} else {
append warningblock \n "WARNING: terminal unable to check for zwsp bug. err:$result"
}
topics - help {
set text ""
set topics [dict create\
"topics|help" "List help topics"\
"tcl" "Tcl version warnings"\
"env|environment" "punkshell environment vars"\
"console|terminal" "Some console behaviour tests and warnings"\
]
set grapheme_support [punk::console::grapheme_cluster_support]
#mode, 1 = set, 2 = unset. (0 = mode not recognised, 3 = permanently set, 4 = permanently unset)
if {![dict size $grapheme_support] || [dict get $grapheme_support mode] eq "unsupported" } {
append warningblock \n "WARNING: terminal either doesn't support grapheme clusters, or doesn't report so via decmode 2027 query."
if {[dict size $grapheme_support] && [dict get $grapheme_support available]} {
append warningblock \n $indent "(but punk::console::grapheme_cluster_support has determined it is probably available)"
set t [textblock::class::table new -show_seps 0]
$t add_column -headers [list "Topic"]
$t add_column
foreach {k v} $topics {
$t add_row [list $k $v]
}
} else {
if {![dict get $grapheme_support available]} {
switch -- [dict get $grapheme_support mode] {
"unset" {
append warningblock \n "WARNING: terminal reports via decmode 2027 that grapheme cluster support is off."
}
"permanently_unset" {
append warningblock \n "WARNING: terminal reports via decmode 2027 that grapheme cluster support is permanently off."
}
"BAD_RESPONSE" {
append warningblock \n "WARNING: terminal doesn't seem to recognize decmode 2027 query. No grapheme cluster support."
set widest0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$widest0 + 4}]
append text \n [$t print]
lappend chunks [list stdout $text]
}
default {
set text ""
set cinfo [uplevel 1 [list punk::ns::cmdwhich [lindex $topicparts 0]]]
set wtype [dict get $cinfo whichtype]
if {$wtype eq "notfound"} {
set externalinfo [auto_execok [lindex $topicparts 0]]
if {[string length $externalinfo]} {
set text "$topicparts"
append text \n "Base type: External command"
append text \n "$externalinfo [lrange $topicparts 1 end]"
} else {
set text "$topicparts\n"
append text "No matching internal or external command found"
}
} else {
set text "[dict get $cinfo which] [lrange $topicparts 1 end]"
append text \n "Base type: $wtype"
set synopsis [uplevel 1 [list punk::ns::synopsis {*}$topicparts]]
set synshow ""
foreach sline [split $synopsis \n] {
if {[regexp {\s*#.*} $sline]} {
append synshow [punk::ansi::a+ bold term-darkgreen Term-white]$sline[punk::ansi::a] \n
} else {
append synshow $sline \n
}
}
if {[string index $synshow end] eq "\n"} {
set synshow [string range $synshow 0 end-1]
}
append text \n $synshow
}
lappend chunks [list stdout $text]
}
}
lappend chunks [list stderr $warningblock]
if {$topic in [list topics help]} {
set text ""
set topics [dict create\
"topics|help" "List help topics"\
"tcl" "Tcl version warnings"\
"env|environment" "punkshell environment vars"\
"console|terminal" "Some console behaviour tests and warnings"\
]
set t [textblock::class::table new -show_seps 0]
$t add_column -headers [list "Topic"]
$t add_column
foreach {k v} $topics {
$t add_row [list $k $v]
}
set widest0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$widest0 + 4}]
append text \n[$t print]
lappend chunks [list stdout $text]
}
lappend chunks [list stderr $warningblock]
return $chunks
}
proc help {args} {
set chunks [help_chunks {*}$args]
foreach chunk $chunks {
lassign $chunk chan text
puts -nonewline $chan $text
}
}
proc mode {{raw_or_line query}} {
package require punk::console
tailcall ::punk::console::mode $raw_or_line

177
src/bootsupport/modules/punk/ansi-0.1.1.tm

@ -340,6 +340,28 @@ tcl::namespace::eval punk::ansi::class {
}
}
tcl::namespace::eval punk::ansi {
namespace eval argdoc {
variable PUNKARGS
# -- --- --- --- ---
#non colour SGR codes
# we can use these directly via ${$I} etc without marking a definition with @dynamic
#This is because they don't need to change when colour switched on and off.
#chicken/egg - need to use literals here
set I "\x1b\[3m" ;# [a+ italic]
set NI "\x1b\[23m" ;# [a+ noitalic]
set B "\x1b\[1m" ;# [a+ bold]
set N "\x1b\[22m" ;# [a+ normal]
set T "\x1b\[1\;4m" ;# [a+ bold underline]
set NT "\x1b\[22\;24m\x1b\[4:0m" ;# [a+ normal nounderline]
set LC \u007b ;#left curly brace
set RC \u007d ;#right curly brace
# -- --- --- --- ---
#namespace import ::punk::args::helpers::*
}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
tcl::namespace::eval punk::ansi {
@ -2262,59 +2284,74 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
return $displaytable
}
lappend PUNKARGS [list {
@id -id ::punk::ansi::a?
@cmd -name "punk::ansi::a?"\
-summary\
"ANSI colour information"\
-help\
""
@form -form "sgr_overview"
@values -form "sgr_overview" -min 0 -max 0
@form -form "term"
@leaders -form "term" -min 1 -max 1
term -type literal(term) -help\
"256 term colours"
@opts -min 0 -max 0
@values -form "term" -min 0 -max -1
panel -type string -optional 1 -multiple 1 -default {16 main greyscale note}\
-choices {16 main greyscale pastel rainbow note}
@form -form "tk"
@leaders -form "tk" -min 1 -max 1
tk -type literal(tk)|literal(TK) -help\
"Tk colours"
@opts -form "tk"
-merged -type none -help\
"If this flag is supplied - show colour names with the same RGB
values together."
@values -form "tk" -min 0 -max -1
glob -type string -optional 1 -multiple 1 -help\
"A glob string such as *green*.
Multiple glob entries can be provided. The search is case insensitive"
@form -form "web"
@values -form "web" -min 1 -max -1
web -type literal(web) -help\
"Web colours"
panel -type string -optional 1 -multiple 1 -choices {basic pink red orange yellow brown purple blue cyan green white gray}
@form -form "x11"
@values -form "x11" -min 1 -max 1
x11 -type literal(x11) -help\
"x11 colours"
@form -form "sample"
@values -form "sample" -min 1 -max -1
colourcode -type sgr|Sgr|literalprefix(term-)|literalprefix(Term-)|literalprefix(web-)|literalprefix(Web-)|literalprefix(rgb)|literalprefix(Rgb)\
-optional 0\
-multiple 1
}]
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::ansi::a?
@cmd -name "punk::ansi::a?"\
-summary\
"ANSI colour information"\
-help\
"With no arguments - display an overview information panel.
With the first argument one of:
${$B}term tk TK web x11${$N}
Display a more specific panel of colour information.
With arguments of individual colourcodes from any of the above
sets, display a small diagnostic table showing a sample of
the individual and combined effect(s), along with indications
of the raw ANSI codes."
@form -form "sgr_overview"
@values -form "sgr_overview" -min 0 -max 0
@form -form "term"
@leaders -form "term" -min 1 -max 1
term -type literal(term) -help\
"256 term colours"
@opts -min 0 -max 0
@values -form "term" -min 0 -max -1
panel -type string -optional 1 -multiple 1 -default {16 main greyscale note}\
-choices {16 main greyscale pastel rainbow note}
@form -form "tk"
@leaders -form "tk" -min 1 -max 1
tk -type literal(tk)|literal(TK) -help\
"Tk colours"
@opts -form "tk"
-merged -type none -help\
"If this flag is supplied - show colour names with the same RGB
values together."
@values -form "tk" -min 0 -max -1
glob -type string -optional 1 -multiple 1 -help\
"A glob string such as *green*.
Multiple glob entries can be provided. The search is case insensitive"
@form -form "web"
@values -form "web" -min 1 -max -1
web -type literal(web) -help\
"Web colours"
panel -type string -optional 1 -multiple 1 -choices {basic pink red orange yellow brown purple blue cyan green white gray}
@form -form "x11"
@values -form "x11" -min 1 -max 1
x11 -type literal(x11) -help\
"x11 colours"
@form -form "sample"
@values -form "sample" -min 1 -max -1
#review literalprefix is not the right thing here! (literalprefix means a tcl::prefix::match on the value)
#colourcode -type sgr|Sgr|stringstartswith(term-)|stringstartswith(Term-)|stringstartswith(web-)|stringstartswith(Web-)|stringstartswith(rgb)|stringstartswith(Rgb)
colourcode -type string|stringstartswith(term-)|stringstartswith(Term-)|stringstartswith(web-)|stringstartswith(Web-)|stringstartswith(rgb)|stringstartswith(Rgb)\
-typesynopsis {${$I}sgr${$NI}|${$I}Sgr${$NI}|${$I}colourcode${$NI}}\
-optional 0\
-multiple 1
}]
}
proc a? {args} {
#*** !doctools
#[call [fun a?] [opt {ansicode...}]]
@ -2414,8 +2451,8 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
append out \n
append out "[a+ {*}$fc web-white]Combination testing[a]" \n
append out [textblock::join -- $indent "Example: a? red brightgreen underline Tk-slategrey italic"] \n
append out [textblock::join -- $indent "This will show a small table of each applied code and a RESULT row. The 'red' in this case is redundant,"] \n
append out [textblock::join -- $indent "so a final MERGED row displays with an alert 'REDUNDANCIES FOUND'."] \n
append out [textblock::join -- $indent "This will show a small table of each applied code and a RESULT row. The 'red' in this case is redundant, because there are"] \n
append out [textblock::join -- $indent "two foreground colours (lowercase first letter) so a final MERGED row displays with an alert 'REDUNDANCIES FOUND'."] \n
append out [textblock::join -- $indent "The final columns of RESULT and MERGED (showing raw ANSI sequence) will differ if the arguments aren't in canonical order."] \n
append out [textblock::join -- $indent "The MERGED line will only display if there are redundancies or different ordering."] \n
@ -2674,14 +2711,26 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
} else {
set tkcolours [list]
}
foreach c $webcolours {
append info \n web-$c
}
foreach c $x11colours {
append info \n x11-$c
}
foreach c $tkcolours {
append info \n tk-$c
if {[string is upper -strict [string index $pfx 0]]} {
foreach c $webcolours {
append info \n Web-$c
}
foreach c $x11colours {
append info \n X11-$c
}
foreach c $tkcolours {
append info \n Tk-$c
}
} else {
foreach c $webcolours {
append info \n web-$c
}
foreach c $x11colours {
append info \n x11-$c
}
foreach c $tkcolours {
append info \n tk-$c
}
}
$t add_row [list $i "$info" $s [ansistring VIEW $s]]
}
@ -8723,7 +8772,7 @@ interp alias {} ansistring {} ::punk::ansi::ansistring
namespace eval ::punk::args::register {
#use fully qualified so 8.6 doesn't find existing var in global namespace
lappend ::punk::args::register::NAMESPACES ::punk::ansi ::punk::ansi::class ::punk::ansi::ta
lappend ::punk::args::register::NAMESPACES ::punk::ansi ::punk::ansi::argdoc ::punk::ansi::class ::punk::ansi::ta
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++

153
src/bootsupport/modules/punk/args-0.2.1.tm

@ -359,16 +359,22 @@ tcl::namespace::eval ::punk::args::helpers {
#puts $str
#puts stderr -------------------
#rudimentary colourising (not full tcl syntax parsing)
#highlight comments first - so that we can also highlight braces within comments to help with detecting unbalanced braces/square brackets in comments
switch -- $opt_syntax {
tcl {
#rudimentary colourising (not full tcl syntax parsing)
#Note that this can highlight ;# in some places as a comment where it's not appropriate
# e.g inside a regexp
#highlight comments first - so that we can also highlight braces within comments to help with detecting unbalanced braces/square brackets in comments
#result lines often indicated in examples by \u2192 →
#however - it's not present on each line of output, instead indents are used - so we can't so easily highlight all appropriate rows(?)
set str [punk::grepstr -return all -highlight {Term-grey term-darkgreen} {^\s*#.*} $str] ;#Note, will not highlight comments at end of line - like this one
set str [punk::grepstr -return all -highlight {Term-grey term-darkgreen} {;\s*(#.*)} $str]
#TODO - fix grepstr highlighting (bg issues - why?)
set str [punk::grepstr -return all -highlight {Term-grey term-darkblue} {\{|\}} $str]
#Note that if we were to highlight based on the regexp {\{|\}} then the inserted ansi would come between
# the backslash and brace in \{ or \} - this breaks the syntactic structure causing problems.
set str [punk::grepstr -return all -highlight {Term-grey tk-darkblue} {^\{|[^\\](\{+)} $str]
set str [punk::grepstr -return all -highlight {Term-grey tk-darkblue} {[^\\](\}+)} $str]
set str [punk::grepstr -return all -highlight {Term-grey term-orange1} {\[|\]} $str]
#puts stderr -------------------
#puts $str
@ -460,7 +466,7 @@ tcl::namespace::eval ::punk::args::helpers {
tcl::namespace::eval punk::args {
package require punk::assertion
#if 'package forget' was called on this package (e.g when loading test::punk::args) then assert may already exist in the namespace
#procs can be overridden silently, but not imports
#namespace import will fail if target exists
catch {
namespace import ::punk::assertion::assert
}
@ -469,9 +475,9 @@ tcl::namespace::eval punk::args {
variable PUNKARGS ;#list of our own punk::args function argument definitions - loaded with punk::args::define at the end.
tcl::namespace::export {[a-z]*}
variable rawdef_cache
if {![info exists rawdef_cache]} {
set rawdef_cache [tcl::dict::create] ;#key on rawdef list - return dict of {-id <id> -dynamic 0|1}
variable rawdef_cache_about
if {![info exists rawdef_cache_about]} {
set rawdef_cache_about [tcl::dict::create] ;#key on rawdef list - return dict of {-id <id> -dynamic 0|1}
}
variable id_cache_rawdef
if {![info exists id_cache_rawdef]} {
@ -487,9 +493,9 @@ tcl::namespace::eval punk::args {
set argdefcache_unresolved [tcl::dict::create] ;#key = original template list supplied to 'define', value = 2-element list: (tstr-parsed split of template) & (unresolved params)
}
variable argdata_cache
if {![info exists argdata_cache]} {
set argdata_cache [tcl::dict::create]
variable rawdef_cache_argdata
if {![info exists rawdef_cache_argdata]} {
set rawdef_cache_argdata [tcl::dict::create]
}
variable id_counter
@ -979,11 +985,11 @@ tcl::namespace::eval punk::args {
error todo
}
proc define {args} {
variable rawdef_cache
variable rawdef_cache_about
variable id_cache_rawdef
variable argdata_cache
if {[dict exists $rawdef_cache $args]} {
return [dict get [dict get $rawdef_cache $args] -id]
#variable rawdef_cache_argdata
if {[dict exists $rawdef_cache_about $args]} {
return [dict get [dict get $rawdef_cache_about $args] -id]
} else {
set lvl 2
set id [rawdef_id $args $lvl]
@ -991,46 +997,40 @@ tcl::namespace::eval punk::args {
#we seem to be re-creating a previously defined id...
#clear any existing caches for this id
undefine $id 0
##dict unset argdata_cache $prevraw ;#silently does nothing if key not present
#dict for {k v} $argdata_cache {
# if {[dict get $v id] eq $id} {
# dict unset argdata_cache $k
# }
#}
#dict for {k v} $rawdef_cache {
# if {[dict get $v -id] eq $id} {
# dict unset rawdef_cache $k
# }
#}
#dict unset id_cache_rawdef $id
}
set is_dynamic [rawdef_is_dynamic $args]
set defspace [uplevel 1 {::namespace current}]
dict set rawdef_cache $args [dict create -id $id -dynamic $is_dynamic -defspace $defspace]
dict set rawdef_cache_about $args [dict create -id $id -dynamic $is_dynamic -defspace $defspace]
dict set id_cache_rawdef $id $args
return $id
}
}
proc undefine {id {quiet 0}} {
variable rawdef_cache
#review - alias?
variable rawdef_cache_about
variable id_cache_rawdef
variable argdata_cache
variable rawdef_cache_argdata
if {[id_exists $id]} {
if {!$quiet} {
puts stderr "punk::args::undefine clearing existing data for id:$id"
}
dict for {k v} $argdata_cache {
if {[dict get $v id] eq $id} {
dict unset argdata_cache $k
if {[dict exists $id_cache_rawdef $id]} {
set deflist [dict get $id_cache_rawdef $id]
dict unset rawdef_cache_about $deflist
dict unset rawdef_cache_argdata $deflist
dict unset id_cache_rawdef $id
} else {
dict for {k v} $rawdef_cache_argdata {
if {[dict get $v id] eq $id} {
dict unset rawdef_cache_argdata $k
}
}
}
dict for {k v} $rawdef_cache {
if {[dict get $v -id] eq $id} {
dict unset rawdef_cache $k
dict for {k v} $rawdef_cache_about {
if {[dict get $v -id] eq $id} {
dict unset rawdef_cache_about $k
}
}
}
dict unset id_cache_rawdef $id
} else {
if {!$quiet} {
puts stderr "punk::args::undefine unable to find id: '$id'"
@ -1039,17 +1039,26 @@ tcl::namespace::eval punk::args {
}
#'punk::args::parse $args withdef $deflist' can raise parsing error after an autoid was generated
# In this case we don't see the autoid in order to delete it
#proc undefine_deflist {deflist} {
#}
proc undefine_deflist {deflist} {
variable rawdef_cache_about
variable id_cache_rawdef
variable rawdef_cache_argdata
if {[dict exists $rawdef_cache_about $deflist -id]} {
set id [dict get $rawdef_cache_about $deflist -id]
dict unset rawdef_cache_about $deflist
dict unset rawdef_cache_argdata $deflist
dict unset id_cache_rawdef $id
}
}
proc idquery_info {id} {
variable id_cache_rawdef
variable rawdef_cache
variable rawdef_cache_about
if {[dict exists $id_cache_rawdef $id]} {
set sep [string repeat - 40]
set rawdef [dict get $id_cache_rawdef $id]
if {[dict exists $rawdef_cache $rawdef]} {
set idinfo [dict get $rawdef_cache $rawdef]
if {[dict exists $rawdef_cache_about $rawdef]} {
set idinfo [dict get $rawdef_cache_about $rawdef]
} else {
set idinfo ""
}
@ -1060,11 +1069,11 @@ tcl::namespace::eval punk::args {
append result \n "id info:"
append result \n $idinfo
append result \n $sep
variable argdata_cache
variable rawdef_cache_argdata
#lsearch -stride not avail (or buggy) in some 8.6 interps - search manually for now (2025). todo - modernize some time after Tcl 9.0/9.1 more widespread.(2027?)
#check for and report if id is present multiple times
set argdata_records [list]
dict for {k v} $argdata_cache {
dict for {k v} $rawdef_cache_argdata {
if {[dict get $v id] eq $id} {
if {$k eq $rawdef} {
lappend argdata_records [list 1 $k $v]
@ -1100,12 +1109,20 @@ tcl::namespace::eval punk::args {
dict get [resolve {*}$args] id
}
lappend PUNKARGS [list {
@id -id ::punk::args::resolve
@cmd -name punk::args::resolve -help\
""
@values -min 0 -max -1
arg -type any -multiple 1 -help\
"rawdef line block"
}]
proc resolve {args} {
variable rawdef_cache
variable rawdef_cache_about
variable id_cache_rawdef
set defspace ""
if {[dict exists $rawdef_cache $args]} {
set cinfo [dict get $rawdef_cache $args]
if {[dict exists $rawdef_cache_about $args]} {
set cinfo [dict get $rawdef_cache_about $args]
set id [dict get $cinfo -id]
set is_dynamic [dict get $cinfo -dynamic]
if {[dict exists $cinfo -defspace]} {
@ -1116,12 +1133,13 @@ tcl::namespace::eval punk::args {
set id [rawdef_id $args]
puts stderr "Warning: punk::args::resolve called with undefined id:$id"
set is_dynamic [rawdef_is_dynamic $args]
dict set rawdef_cache $args [dict create -id $id -dynamic $is_dynamic]
#-defspace ???
dict set rawdef_cache_about $args [dict create -id $id -dynamic $is_dynamic]
dict set id_cache_rawdef $id $args
}
variable argdata_cache
variable rawdef_cache_argdata
variable argdefcache_unresolved
@ -1155,8 +1173,8 @@ tcl::namespace::eval punk::args {
if {!$is_dynamic} {
#todo - don't use cached version if 'colour off' vs 'colour on' different to when last resolved!
#(e.g example blocks will still have colour if previously resolved)
if {[tcl::dict::exists $argdata_cache $cache_key]} {
return [tcl::dict::get $argdata_cache $cache_key]
if {[tcl::dict::exists $rawdef_cache_argdata $cache_key]} {
return [tcl::dict::get $rawdef_cache_argdata $cache_key]
}
set normargs [list]
foreach a $textargs {
@ -1235,10 +1253,10 @@ tcl::namespace::eval punk::args {
tcl::dict::set argdefcache_unresolved $cache_key $pt_params
}
}
#argdata_cache should be limited in some fashion or will be a big memory leak???
if {[tcl::dict::exists $argdata_cache [list $optionspecs]]} {
#rawdef_cache_argdata should be limited in some fashion or will be a big memory leak???
if {[tcl::dict::exists $rawdef_cache_argdata [list $optionspecs]]} {
#resolved cache version exists
return [tcl::dict::get $argdata_cache [list $optionspecs]]
return [tcl::dict::get $rawdef_cache_argdata [list $optionspecs]]
}
}
@ -2669,10 +2687,12 @@ tcl::namespace::eval punk::args {
form_info $form_info\
]
tcl::dict::set argdata_cache $cache_key $argdata_dict
#REVIEW
tcl::dict::set rawdef_cache_argdata $cache_key $argdata_dict
if {$is_dynamic} {
#also cache resolved version
tcl::dict::set argdata_cache [list $optionspecs] $argdata_dict
tcl::dict::set rawdef_cache_argdata [list $optionspecs] $argdata_dict
}
#tcl::dict::set id_cache_rawdef $DEF_definition_id $args
@ -3094,10 +3114,10 @@ tcl::namespace::eval punk::args {
}
proc is_dynamic {id} {
variable id_cache_rawdef
variable rawdef_cache
variable rawdef_cache_about
set deflist [raw_def $id]
if {[dict exists $rawdef_cache $deflist -dynamic]} {
return [dict get $rawdef_cache $deflist -dynamic]
if {[dict exists $rawdef_cache_about $deflist -dynamic]} {
return [dict get $rawdef_cache_about $deflist -dynamic]
}
return [rawdef_is_dynamic $deflist]
#@dynamic only has meaning as 1st element of a def in the deflist
@ -4008,7 +4028,16 @@ tcl::namespace::eval punk::args {
}
if {$synopsis ne ""} {
if {$use_table} {
$t configure_header $h -colspans $arg_colspans -values [list Synopsis: [punk::ansi::ansiwrap brightwhite $synopsis]]
set form_names [dict get $spec_dict form_names]
set synhelp "Synopsis:"
if {[llength $form_names] > 1} {
set fn 0
foreach fname $form_names {
append synhelp \n " i -form $fn \U2026"
incr fn
}
}
$t configure_header $h -colspans $arg_colspans -values [list $synhelp [punk::ansi::ansiwrap brightwhite $synopsis]]
} else {
#todo
lappend errlines "Synopsis:\n$synopsis"

1550
src/bootsupport/modules/punk/args/moduledoc/tclcore-0.1.0.tm

File diff suppressed because it is too large Load Diff

40
src/bootsupport/modules/punk/assertion-0.1.0.tm

@ -66,38 +66,6 @@ package require Tcl 8.6-
#*** !doctools
#[section API]
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# oo::class namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
tcl::namespace::eval punk::assertion::class {
#*** !doctools
#[subsection {Namespace punk::assertion::class}]
#[para] class definitions
if {[tcl::info::commands [tcl::namespace::current]::interface_sample1] eq ""} {
#*** !doctools
#[list_begin enumerated]
# oo::class create interface_sample1 {
# #*** !doctools
# #[enum] CLASS [class interface_sample1]
# #[list_begin definitions]
# method test {arg1} {
# #*** !doctools
# #[call class::interface_sample1 [method test] [arg arg1]]
# #[para] test method
# puts "test: $arg1"
# }
# #*** !doctools
# #[list_end] [comment {-- end definitions interface_sample1}]
# }
#*** !doctools
#[list_end] [comment {--- end class enumeration ---}]
}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#keep 2 namespaces for assertActive and assertInactive so there is introspection available via namespace origin
tcl::namespace::eval punk::assertion::primary {
@ -149,9 +117,13 @@ tcl::namespace::eval punk::assertion {
proc do_ns_import {} {
uplevel 1 [list tcl::namespace::import ::punk::assertion::primary::assertActive]
}
do_ns_import
#puts --------BBB
rename assertActive assert
if {[catch {
do_ns_import
rename assertActive assert
} errM]} {
puts stderr "punk::assertion error during load - assert/assertActive functions already present?\n$errM"
}
}

4
src/bootsupport/modules/punk/du-0.1.0.tm

@ -961,9 +961,9 @@ namespace eval punk::du {
proc get_vfsmounts_in_folder {folderpath} {
set vfsmounts [list]
if {![llength [package provide vfs]]} {
return [list]
return [list]
}
set fpath [punk::objclone $folderpath]
set fpath [punk::valcopy $folderpath]
set is_rel 0
if {[file pathtype $fpath] ne "absolute"} {
set fpath [file normalize $fpath]

12
src/bootsupport/modules/punk/lib-0.1.3.tm

@ -4071,11 +4071,11 @@ namespace eval punk::lib {
interp eval $interp [list tcl::tm::add {*}[lreverse [tcl::tm::list]]]
}
proc objclone {obj} {
proc valcopy {obj} {
append obj2 $obj {}
}
proc set_clone {varname obj} {
#used by repl's codeinterp. Maintains internal rep, easier to call e.g interp eval code [list punk::set_clone varnmame $val]
proc set_valcopy {varname obj} {
#used by repl's codeinterp. Maintains internal rep, easier to call e.g interp eval code [list punk::set_valcopy varnmame $val]
append obj2 $obj {}
uplevel 1 [list set $varname $obj2]
}
@ -4095,9 +4095,9 @@ namespace eval punk::lib {
set default_groupsize 3
set results [list]
set nums [objclone $numbers_or_commaformattednumbers] ;#stops single num from getting internal rep of list
set nums [valcopy $numbers_or_commaformattednumbers] ;#stops single num from getting internal rep of list
foreach inputnum $nums {
set number [objclone $inputnum]
set number [valcopy $inputnum]
#also handle tcl 8.7+ underscores in numbers
set number [string map [list _ "" , ""] $number]
#normalize e.g 2e4 -> 20000.0
@ -4145,7 +4145,7 @@ namespace eval punk::lib {
# and possibly containing a decimal point, i.e.: [:space:]*[:digit:]*\.[:digit:]*
proc delimit_number {unformattednumber {delim ","} {GroupSize 3}} {
set number [objclone $unformattednumber]
set number [valcopy $unformattednumber]
set number [string map {_ ""} $number]
#normalize using expr - e.g 2e4 -> 20000.0
set number [expr {$number}]

8
src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm

@ -34,7 +34,7 @@ namespace eval punk::mix::commandset::loadedlib {
"(unimplemented) Display only those that are 0:absent 1:present 2:either"
-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"
-refresh -type none -help "Re-scan the tm and library folders"
searchstring -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.
@ -45,11 +45,11 @@ namespace eval punk::mix::commandset::loadedlib {
}
proc search {args} {
set argd [punk::args::parse $args withid ::punk::mix::commandset::loadedlib::search]
set searchstrings [dict get $argd values searchstring]
set opts [dict get $argd opts]
lassign [dict values $argd] leaders opts values received
set searchstrings [dict get $values searchstring]
set opt_return [dict get $opts -return]
set opt_highlight [dict get $opts -highlight]
set opt_refresh [dict get $opts -refresh]
set opt_refresh [dict exists $received -refresh]
if {$opt_refresh} {
catch {package require frobznodule666} ;#ensure pkg system has loaded/searched for everything REVIEW - this doesn't result in full scans

2328
src/bootsupport/modules/punk/ns-0.1.0.tm

File diff suppressed because it is too large Load Diff

58
src/bootsupport/modules/punk/repl-0.1.2.tm

@ -3177,7 +3177,7 @@ namespace eval repl {
variable errstack {}
variable outstack {}
variable run_command_cache
proc set_clone {varname obj} {
proc set_valcopy {varname obj} {
append obj2 $obj {}
uplevel 1 [list set $varname $obj2]
}
@ -3241,6 +3241,7 @@ namespace eval repl {
#}
set pkgs [list\
punk::ansi::colourmap\
punk::assertion\
punk::args\
punk::pipe\
cmdline\
@ -3256,7 +3257,6 @@ namespace eval repl {
textutil\
punk::encmime\
punk::char\
punk::assertion\
punk::ansi\
punk::lib\
overtype\
@ -3290,37 +3290,41 @@ namespace eval repl {
set prior_infoscript [code eval {info script}] ;#probably empty that's ok
foreach pkg $pkgs {
#puts stderr "---> init_script safe pkg: $pkg"
if {[catch {
set nsquals [namespace qualifiers $pkg]
if {$nsquals ne ""} {
if {![dict exists $ns_scanned $nsquals]} {
catch {package require $pkg 1-0} ;#force scan with nonsatisfiable version
dict set ns_scanned $nsquals 1
#only load from source if not already loaded (perhaps already present from another package loading it)
set vloaded [code eval [list package provide $pkg]]
if {$vloaded eq ""} {
if {[catch {
set nsquals [namespace qualifiers $pkg]
if {$nsquals ne ""} {
if {![dict exists $ns_scanned $nsquals]} {
catch {package require $pkg 1-0} ;#force scan with nonsatisfiable version
dict set ns_scanned $nsquals 1
}
}
}
set versions [lsort -command {package vcompare} [package versions $pkg]]
if {[llength $versions]} {
set v [lindex $versions end]
set path [lindex [package ifneeded $pkg $v] end]
if {[file extension $path] in {.tcl .tm}} {
if {![catch {readFile $path} data]} {
code eval [list info script $path]
code eval $data
code eval [list info script $prior_infoscript]
set versions [lsort -command {package vcompare} [package versions $pkg]]
if {[llength $versions]} {
set v [lindex $versions end]
set path [lindex [package ifneeded $pkg $v] end]
if {[file extension $path] in {.tcl .tm}} {
if {![catch {readFile $path} data]} {
code eval [list info script $path]
code eval $data
code eval [list info script $prior_infoscript]
} else {
error "safe - failed to read $path"
}
} else {
error "safe - failed to read $path"
error "safe - refusing to attempt load of $pkg from $path - (allowed extensions .tcl .tm)"
}
} else {
error "safe - refusing to attempt load of $pkg from $path - (allowed extensions .tcl .tm)"
error "safe - no versions of $pkg found"
}
} errMsg]} {
puts stderr "safe - failed to load package $pkg\n$errMsg\n$::errorInfo"
} else {
error "safe - no versions of $pkg found"
#puts stdout "---> init_script safe - loaded $pkg from $path"
#puts stdout "---> v [code eval [list package provide $pkg]]"
}
} errMsg]} {
puts stderr "safe - failed to load package $pkg\n$errMsg\n$::errorInfo"
} else {
#puts stdout "---> init_script safe - loaded $pkg from $path"
#puts stdout "---> v [code eval [list package provide $pkg]]"
}
}
@ -3337,7 +3341,7 @@ namespace eval repl {
#review
code alias ::shellfilter::stack ::shellfilter::stack
#code alias ::punk::lib::set_clone ::punk::lib::set_clone
#code alias ::punk::lib::set_valcopy ::punk::lib::set_valcopy
#code alias ::aliases ::punk::ns::aliases
code alias ::punk::ns::aliases ::punk::ns::aliases
namespace eval ::codeinterp {}

4
src/bootsupport/modules/punk/repl/codethread-0.1.1.tm

@ -196,8 +196,8 @@ tcl::namespace::eval punk::repl::codethread {
#otherwise when $script goes out of scope - internal rep of vars set in script changes.
#The shimmering may be no big deal(?) - but debug/analysis using tcl::unsupported::representation becomes impossible.
#interp eval code [list ::punk::lib::set_clone ::codeinterp::clonescript $script] ;#like objclone
interp eval code [list ::codeinterp::set_clone ::codeinterp::clonescript $script] ;#like objclone
#interp eval code [list ::punk::lib::set_valcopy ::codeinterp::clonescript $script] ;#like valcopy
interp eval code [list ::codeinterp::set_valcopy ::codeinterp::clonescript $script] ;#like valcopy
interp eval code {lappend ::codeinterp::run_command_cache $::codeinterp::clonescript}

18
src/bootsupport/modules/punk/unixywindows-0.1.0.tm

@ -35,35 +35,35 @@ namespace eval punk::unixywindows {
if {![string length $cachedunixyroot]} {
if {![catch {
set result [exec cygpath -m /] ;# -m gives result with forward-slashes - which is ok for windows paths in a Tcl context.
set cachedunixyroot [punk::objclone $result]
set cachedunixyroot [punk::valcopy $result]
file pathtype $cachedunixyroot; #this call causes the int-rep to be path
#set ::punk::last_run_display [list] ;#hack - review shouldn't really be necessary.. but because we call winpath from ./ - the repl looks for last_run_display
} errM]} {
} else {
puts stderr "Warning: Failed to determine base for unix-like paths - using default of c:/msys2"
file pathtype [set cachedunixyroot [punk::objclone "c:/msys2"]]
file pathtype [set cachedunixyroot [punk::valcopy "c:/msys2"]]
}
}
#will have been shimmered from string to 'path' internal rep by 'file pathtype' call
#let's return a different copy as it's so easy to lose path-rep
set copy [punk::objclone $cachedunixyroot]
set copy [punk::valcopy $cachedunixyroot]
return $copy
}
proc refresh_unixyroot {} {
variable cachedunixyroot
set result [exec cygpath -m /] ;# -m gives result with forward-slashes - which is ok for windows paths in a Tcl context.
set cachedunixyroot [punk::objclone $result]
set cachedunixyroot [punk::valcopy $result]
file pathtype $cachedunixyroot; #this call causes the int-rep to be path
set copy [punk::objclone $cachedunixyroot]
set copy [punk::valcopy $cachedunixyroot]
return $copy
}
proc set_unixyroot {windows_path} {
variable cachedunixyroot
file pathtype $windows_path
set cachedunixyroot [punk::objclone $windows_path]
set cachedunixyroot [punk::valcopy $windows_path]
#return the original - but probably int-rep will have shimmered to path even if started out as string
#- that's probably what we want. Caller can use as string or path without affecting cachedunixyroot
return $windows_path
@ -131,13 +131,13 @@ namespace eval punk::unixywindows {
}
#puts stderr "->$driveletters"
set path [punk::objclone $unixypath] ;#take another copy that we can deliberatley shimmer to path and know is separate to the supplied argument
set path [punk::valcopy $unixypath] ;#take another copy that we can deliberatley shimmer to path and know is separate to the supplied argument
set supplied_pathtype [file pathtype $path] ;#we don't care about the pathtype so much as the act of making this call shimmers to a path internal-rep
#copy of var that we can treat as a string without affecting path rep
#Note that some but not all read operations will lose path rep e.g 'string length $x' will lose any path-rep $x had, (but 'string equal $x something' won't)
#Todo - make int-rep tests to check stability of these behaviours across Tcl versions!
set strcopy_path [punk::objclone $path]
set strcopy_path [punk::valcopy $path]
set str_newpath ""
@ -174,7 +174,7 @@ namespace eval punk::unixywindows {
#dunno - pass through
set pathobj $path
} else {
set pathobj [punk::objclone $str_newpath]
set pathobj [punk::valcopy $str_newpath]
file pathtype $pathobj
}
}

12
src/bootsupport/modules/punk/winpath-0.1.0.tm

@ -30,7 +30,7 @@ namespace eval punk::winpath {
#\\servername\share etc or \\?\UNC\servername\share etc.
proc is_unc_path {path} {
set strcopy_path [punk::winpath::system::objclone $path]
set strcopy_path [punk::winpath::system::valcopy $path]
set strcopy_path [string map {\\ /} $strcopy_path] ;#normalize to forward slashes for testing purposes (and forward slashes seem to be auto-translated by windows anyway)
if {[string first "//" $strcopy_path] == 0} {
#check for "Dos device path" syntax
@ -77,7 +77,7 @@ namespace eval punk::winpath {
#dos device path syntax allows windows api to acces extended-length paths and filenames with illegal path characters such as trailing dots or whitespace
#(can exist on server shares and on NTFS - but standard apps can't access without dos device syntax)
proc is_dos_device_path {path} {
set strcopy_path [punk::winpath::system::objclone $path]
set strcopy_path [punk::winpath::system::valcopy $path]
set strcopy_path [string map {\\ /} $strcopy_path] ;#normalize to forward slashes for testing purposes (and forward slashes seem to be auto-translated by windows anyway)
if {[string range $strcopy_path 0 3] in {//?/ //./}} {
return 1
@ -100,13 +100,13 @@ namespace eval punk::winpath {
proc strip_unc_path_prefix {path} {
if {[is_unc_path_plain $path]} {
#plain unc //server
set strcopy_path [punk::winpath::system::objclone $path]
set strcopy_path [punk::winpath::system::valcopy $path]
set trimmedpath [string range $strcopy_path 2 end]
file pathtype $trimmedpath
return $trimmedpath
} elseif {is_unc_path $path} {
#//?/UNC/server/subpath or //./UNC/server/subpath
set strcopy_path [punk::winpath::system::objclone $path]
set strcopy_path [punk::winpath::system::valcopy $path]
set trimmedpath [string range $strcopy_path 7 end]
file pathtype $trimmedpath ;#shimmer it to path rep
return $trimmedpath
@ -153,7 +153,7 @@ namespace eval punk::winpath {
error $err
}
set strcopy_path [punk::winpath::system::objclone $path]
set strcopy_path [punk::winpath::system::valcopy $path]
#Note: path could still have leading double slash if it is a Dos device path: e.g. //?/c:/etc
@ -339,7 +339,7 @@ namespace eval punk::winpath {
namespace eval punk::winpath::system {
#get a copy of the item without affecting internal rep
proc objclone {obj} {
proc valcopy {obj} {
append obj2 $obj {}
}
}

746
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk-0.1.tm

@ -496,23 +496,25 @@ namespace eval punk {
#-----------------------------------------------------------------------------------
#get a copy of the item without affecting internal rep
proc objclone {obj} {
proc valcopy {obj} {
append obj2 $obj {}
}
proc set_clone {varname obj} {
#maintenance: also punk::lib::set_clone
#e.g used by repl's codeinterp. Maintains internal rep, easier to call e.g interp eval code [list punk::set_clone varnmame $val]
proc set_valcopy {varname obj} {
#maintenance: also punk::lib::set_valcopy
#e.g used by repl's codeinterp. Maintains internal rep, easier to call e.g interp eval code [list punk::set_valcopy varnmame $val]
append obj2 $obj {}
uplevel 1 [list set $varname $obj2]
}
interp alias "" strlen "" ::punk::strlen
interp alias "" str_len "" ::punk::strlen
interp alias "" objclone "" ::punk::objclone
interp alias "" valcopy "" ::punk::valcopy
#proc ::strlen {str} {
# string length [append str2 $str {}]
#}
#proc ::objclone {obj} {
#proc ::valcopy {obj} {
# append obj2 $obj {}
#}
@ -629,10 +631,24 @@ namespace eval punk {
-- -type none
@values
pattern -type string -help\
"regex pattern to match in plaintext portion of ANSI string
{regex pattern to match in plaintext portion of ANSI string
The pattern may contain bracketed capturing groups, which
will be highlighted (todo) in the result. If there is no capturing
group, the entire match will be highlighted."
will be highlighted in the result. If there is no capturing
group, the entire match will be highlighted.
Note that if we were to attempt to highlight curly braces based
on the regexp {\{|\}} then the inserted ansi would come between
the backslash and brace in cases where a curly brace is escaped
ie \{ or \}
Depending on how the output is used, this can break the syntactic
structure causing problems.
Instead a pair of regexes such as
{^\{|[^\\](\{+)}
{[^\\](\}+)}
should be used to
exclude braces that are escaped.
(note the capturing groups around each curly brace)
}
string -type string
}
proc grepstr {args} {
@ -706,9 +722,12 @@ namespace eval punk {
}
if {$lineindex in $matched_line_indices} {
set plain_ln [lindex $plainlines $lineindex]
#first test the regexp with a single match to determine number of capturing groups
set matchparts [regexp {*}$nocase -inline -- $pattern $plain_ln] ;#get fullmatch capturegroup...
set numgroups [expr {[llength $matchparts] -1}]
#first - determine the number of capturing groups (subexpressions)
#option 1: test the regexp with a single match
#set testparts [regexp {*}$nocase -inline -- $pattern $plain_ln] ;#get fullmatch capturegroup...
#set numgroups [expr {[llength $testparts] -1}]
#option 2: use the regexp -about flag
set numgroups [lindex [regexp -about $pattern] 0]
set allparts [regexp -all {*}$nocase -indices -inline -- $pattern $plain_ln]
#allparts includes each full match as well as each capturing group
@ -730,9 +749,14 @@ namespace eval punk {
# restrict ourself to just the capture groups, excluding the full match (if there are capture groups)
set highlight_ranges [list]
set i 0
#{-1 -1} returned for non-matching group when there are capture-group alternatives
#e.g {(a)|(b)}
foreach range $allparts {
if {($i % ($numgroups+1)) != 0} {
lappend highlight_ranges $range
lassign $range a b
if {$range ne {-1 -1} & $a <= $b} {
lappend highlight_ranges $range
}
}
incr i
}
@ -917,10 +941,8 @@ namespace eval punk {
return [twapi::new_uuid]
}
}
#get last command result that was run through the repl
proc ::punk::get_runchunk {args} {
set argd [punk::args::parse $args withdef {
namespace eval argdoc {
punk::args::define {
@id -id ::punk::get_runchunk
@cmd -name "punk::get_runchunk" -help\
"experimental"
@ -928,7 +950,19 @@ namespace eval punk {
-1 -optional 1 -type none
-2 -optional 1 -type none
@values -min 0 -max 0
}]
}
}
#get last command result that was run through the repl
proc ::punk::get_runchunk {args} {
#set argd [punk::args::parse $args withdef {
# @id -id ::punk::get_runchunk
# @cmd -name "punk::get_runchunk" -help\
# "experimental"
# @opts
# -1 -optional 1 -type none
# -2 -optional 1 -type none
# @values -min 0 -max 0
#}]
#todo - make this command run without truncating previous runchunks
set runchunks [tsv::array names repl runchunks-*]
@ -4148,7 +4182,7 @@ namespace eval punk {
#pipealias should capture the namespace context of the pipeline so that commands are resolved in the namespace in which the pipealias is created
proc pipealias {targetcmd args} {
set cmdcopy [punk::objclone $args]
set cmdcopy [punk::valcopy $args]
set nscaller [uplevel 1 [list namespace current]]
tailcall interp alias {} $targetcmd {} apply [list args [append cmdcopy " {*}\$args"] $nscaller]
}
@ -4159,7 +4193,7 @@ namespace eval punk {
}
#although the pipealias2 'concat' alias is cleaner in that the original pipeline can be extracted using list commands - it runs much slower
proc pipealias2 {targetcmd args} {
set cmdcopy [punk::objclone $args]
set cmdcopy [punk::valcopy $args]
set nscaller [uplevel 1 [list namespace current]]
tailcall interp alias {} $targetcmd {} apply [list args [concat "\[concat" [list $cmdcopy] "\$args]"] $nscaller]
}
@ -6654,15 +6688,16 @@ namespace eval punk {
#interp alias {} ~ {} file join $::env(HOME) ;#HOME must be capitalized to work cross platform (lowercase home works on windows - but probably not elsewhere)
#interp alias {} ~ {} apply {args {file join $::env(HOME) $args}}
proc ~ {args} {
set hdir [punk::objclone $::env(HOME)]
set hdir [punk::valcopy $::env(HOME)]
file pathtype $hdir
set d $hdir
#use the file join 2-arg optimisation to avoid losing path-rep - probably doesn't give any advantage on all Tcl versions
#review - for what versions does/did the 2-arg version of file join not just return a string?
foreach a $args {
set d [file join $d $a]
}
file pathtype $d
return [punk::objclone $d]
return [punk::valcopy $d]
}
interp alias {} ~ {} punk::~
@ -7789,47 +7824,61 @@ namespace eval punk {
namespace eval argdoc {
punk::args::define {
@id -id ::punk::help_chunks
@cmd -name "punk::help_chunks"\
-summary\
""\
-help\
""
@opts
-- -type none
@values -min 0 -max -1
arg -type any -optional 1 -multiple 1
}
}
proc help {args} {
set chunks [uplevel 1 [list ::punk::help_chunks {*}$args]]
foreach chunk $chunks {
lassign $chunk chan text
puts -nonewline $chan $text
}
}
#return list of {chan chunk} elements
proc help_chunks {args} {
set chunks [list]
set linesep [string repeat - 76]
set mascotblock ""
catch {
package require patternpunk
#lappend chunks [list stderr [>punk . rhs]]
append mascotblock [textblock::frame -checkargs 0 [>punk . banner -title "Punk Shell" -left Tcl -right [package provide Tcl]]]
set argd [punk::args::parse $args withid ::punk::help_chunks]
lassign [dict values $argd] leaders opts values received
if {[dict exists $values arg]} {
set topicparts [dict get $values arg]
} else {
set topicparts [list ""]
}
#set topic [lindex $args end]
#set argopts [lrange $args 0 end-1]
set topic [lindex $args end]
set argopts [lrange $args 0 end-1]
set chunks [list]
set linesep [string repeat - 76]
set warningblock ""
set title "[a+ brightgreen] Punk core navigation commands: "
set I [punk::ansi::a+ italic]
set NI [punk::ansi::a+ noitalic]
#todo - load from source code annotation?
# -------------------------------------------------------
set logoblock ""
if {[catch {
package require patternpunk
#lappend chunks [list stderr [>punk . rhs]]
append logoblock [textblock::frame -title "Punk Shell [package provide punk]" -width 29 -checkargs 0 [>punk . banner -title "" -left Tcl -right [package provide Tcl]]]
}]} {
append logoblock [textblock::frame -title "Punk Shell [package provide punk]" -subtitle "TCL [package provide Tcl]" -width 29 -height 10 -checkargs 0 ""]
}
set title "[a+ brightgreen] Help System: "
set cmdinfo [list]
lappend cmdinfo [list help "?topics?" "This help. To see available subitems type: help topics"]
lappend cmdinfo [list i "cmd ?subcommand...?" "Show usage for a command or ensemble subcommand"]
lappend cmdinfo [list s "cmd ?subcommand...?" "Show synopsis for a command or ensemble subcommand"]
lappend cmdinfo [list eg "cmd ?subcommand...?" "Show example from manpage"]
lappend cmdinfo [list ./ "?subdir?" "view/change directory"]
lappend cmdinfo [list ../ "" "go up one directory"]
lappend cmdinfo [list ./new "subdir" "make new directory and switch to it"]
lappend cmdinfo [list n/ "?glob...?" "view/change namespace\n (accepts ns path globs e.g **::*get* to match comands at any level )"]
lappend cmdinfo [list n// "" "view/change namespace (with command listing)"]
lappend cmdinfo [list "nn/" "" "go up one namespace"]
lappend cmdinfo [list "n/new" "<ns>" "make child namespace and switch to it"]
lappend cmdinfo [list dev "?subcommand?" "(ensemble command to make new projects/modules and to generate docs)"]
lappend cmdinfo [list a? "?subcommand...?" "view ANSI colours\n e.g a? web"]
#set cmds [lsearch -all -inline -index 0 -subindices $cmdinfo *]
#set descr [lsearch -all -inline -index 1 -subindices $cmdinfo *]
#set widest1 [tcl::mathfunc::max {*}[lmap v $cmds {string length $v}]]
#set widest2 [tcl::mathfunc::max {*}[lmap v $descr {string length $v}]]
set t [textblock::class::table new -show_seps 0]
#foreach c $cmds d $descr {
# $t add_row [list $c $d]
#}
lappend cmdinfo [list help "?${I}topic${NI}?" "This help.\nTo see available subitems type:\nhelp topics\n\n\n\n\n"]
set t [textblock::class::table new -minwidth 51 -show_seps 0]
foreach row $cmdinfo {
$t add_row $row
}
@ -7838,272 +7887,387 @@ namespace eval punk {
set width_1 [$t column_datawidth 1]
$t configure_column 1 -minwidth [expr {$width_1 + 1}]
$t configure -title $title
set text ""
append text [$t print]
set warningblock ""
set introblock $mascotblock
append introblock \n $text
#if {[catch {package require textblock} errM]} {
# append warningblock \n "WARNING: textblock package couldn't be loaded. Side-by-side display not available"
#} else {
# set introblock [textblock::join -- " " \n$mascotblock " " $text]
#}
lappend chunks [list stdout $introblock]
if {$topic in [list tcl]} {
if {[punk::lib::check::has_tclbug_script_var]} {
append warningblock \n "minor warning: punk::lib::check::has_tclbug_script_var returned true! (string rep for list variable in script generated when script changed)"
}
if {[punk::lib::check::has_tclbug_safeinterp_compile]} {
set indent " "
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_safeinterp returned true!" \n
append warningblock "${indent}(ensemble commands not compiled in safe interps - heavy performance impact in safe interps)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/1095bf7f75]"
append warningblock [a]
}
if {[catch {lsearch -stride 2 {a b} b}]} {
set indent " "
append warningblock \n "[a+ web-red]warning: lsearch does not seem to support -stride option" \n
append warningblock "${indent}(Consider upgrading to a late release of tcl 8.6 or tcl 9+ )" \n
append warningblock [a]
} else {
if {[punk::lib::check::has_tclbug_lsearch_strideallinline]} {
set text [$t print]
set introblock [textblock::join -- $logoblock $text]
lappend chunks [list stdout $introblock\n]
# -------------------------------------------------------
switch -- [lindex $topicparts 0] {
"" {
# -------------------------------------------------------
set title "[a+ brightgreen] Filesystem navigation: "
set cmdinfo [list]
lappend cmdinfo [list ./ "?${I}subdir${NI}?" "view/change directory"]
lappend cmdinfo [list ../ "" "go up one directory"]
lappend cmdinfo [list ./new "${I}subdir${NI}" "make new directory and switch to it"]
set t [textblock::class::table new -minwidth 80 -show_seps 0]
foreach row $cmdinfo {
$t add_row $row
}
set width_0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$width_0 + 2}]
set width_1 [$t column_datawidth 1]
$t configure_column 1 -minwidth [expr {$width_1 + 1}]
$t configure -title $title
set text ""
append text [$t print]
lappend chunks [list stdout $text\n]
# -------------------------------------------------------
# -------------------------------------------------------
set title "[a+ brightgreen] Namespace navigation: "
set cmdinfo [list]
lappend cmdinfo [list n/ "?${I}ns${NI}|${I}glob${NI}?" "view/change namespace\n (accepts ns path globs e.g **::*get* to match\n commands at any level )"]
lappend cmdinfo [list n// "?${I}ns${NI}|${I}glob${NI}?" "view/change namespace (with command listing)"]
lappend cmdinfo [list "nn/" "" "go up one namespace"]
lappend cmdinfo [list "n/new" "${I}ns${NI}" "make child namespace and switch to it"]
set t [textblock::class::table new -minwidth 80 -show_seps 0]
foreach row $cmdinfo {
$t add_row $row
}
set width_0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$width_0 + 2}]
set width_1 [$t column_datawidth 1]
$t configure_column 1 -minwidth [expr {$width_1 + 1}]
$t configure -title $title
set text ""
append text [$t print]
lappend chunks [list stdout $text\n]
# -------------------------------------------------------
# -------------------------------------------------------
set title "[a+ brightgreen] Command help: "
set cmdinfo [list]
lappend cmdinfo [list i "${I}cmd${NI} ?${I}subcommand${NI}...?" "Show usage for a command or ensemble subcommand"]
lappend cmdinfo [list s "${I}cmd${NI} ?${I}subcommand${NI}...?" "Show synopsis for a command or ensemble subcommand"]
lappend cmdinfo [list eg "${I}cmd${NI} ?${I}subcommand${NI}...?" "Show example from manpage"]
lappend cmdinfo [list corp "${I}proc${NI}" "View proc body and arguments"]
set t [textblock::class::table new -minwidth 80 -show_seps 0]
foreach row $cmdinfo {
$t add_row $row
}
set width_0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$width_0 + 2}]
set width_1 [$t column_datawidth 1]
$t configure_column 1 -minwidth [expr {$width_1 + 1}]
$t configure -title $title
set text ""
append text [$t print]
lappend chunks [list stdout $text\n]
# -------------------------------------------------------
set title "[a+ brightgreen] Miscellaneous: "
#todo - load from source code annotation?
set cmdinfo [list]
lappend cmdinfo [list dev "?${I}subcommand${NI}?" "(ensemble command to make new projects/modules and\n to generate docs)"]
lappend cmdinfo [list a? "?${I}subcommand${NI}...?" "view ANSI colours\n e.g a? web\n or individual code samples/diagnostics\n e.g a? red bold\n e.g a? underline web-orange Term-purple3"]
lappend cmdinfo [list a+ "?${I}colourcode${NI}...?" "Return ANSI codes\n e.g puts \"\[a+ purple\]purple\[a+ Green\]purple on green\[a\]\"\n [a+ purple]purple[a+ Green]purple on green[a] "]
lappend cmdinfo [list a "?${I}colourcode${NI}...?" "Return ANSI codes (with leading reset)\n e.g puts \"\[a+ purple\]purple\[a Green\]normal on green\[a\]\"\n [a+ purple]purple[a Green]normal on green[a] "]
set t [textblock::class::table new -minwidth 80 -show_seps 0]
foreach row $cmdinfo {
$t add_row $row
}
set width_0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$width_0 + 2}]
set width_1 [$t column_datawidth 1]
$t configure_column 1 -minwidth [expr {$width_1 + 1}]
$t configure -title $title
set text ""
append text [$t print]
lappend chunks [list stdout $text]
# -------------------------------------------------------
}
tcl {
set text "Tcl Patchlevel: [info patchlevel]"
catch {
append text \n "Tcl build-info: [::tcl::build-info]"
}
if {[punk::lib::check::has_tclbug_script_var]} {
append warningblock \n "minor warning: punk::lib::check::has_tclbug_script_var returned true! (string rep for list variable in script generated when script changed)"
}
if {[punk::lib::check::has_tclbug_safeinterp_compile]} {
set indent " "
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_lsearch_strideallinline returned true!" \n
append warningblock "${indent}(lsearch using -stride -all -inline -subindices does not return values corresponding to subindex when a single -index value is used)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/5a1aaa201d]"
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_safeinterp returned true!" \n
append warningblock "${indent}(ensemble commands not compiled in safe interps - heavy performance impact in safe interps)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/1095bf7f75]"
append warningblock [a]
}
}
if {[punk::lib::check::has_tclbug_list_quoting_emptyjoin]} {
set indent " "
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_list_quoting returned true!" \n
append warningblock "${indent}lists elements not properly quoted in some cases. e.g 'list {*}[lindex {etc #foo} 1] {*}[list]' (#foo not braced)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/e38dce74e2]"
}
}
set text ""
if {$topic in [list env environment]} {
#todo - move to punk::config?
upvar ::punk::config::punk_env_vars_config punkenv_config
upvar ::punk::config::other_env_vars_config otherenv_config
set known_punk [dict keys $punkenv_config]
set known_other [dict keys $otherenv_config]
append text \n
set usetable 1
if {$usetable} {
set t [textblock::class::table new -show_hseps 0 -show_header 1 -ansiborder_header [a+ web-green]]
if {"windows" eq $::tcl_platform(platform)} {
#If any env vars have been set to empty string - this is considered a deletion of the variable on windows.
#The Tcl ::env array is linked to the underlying process view of the environment
#- but info exists ::env(var) can misreport as true if it has been deleted by setting to empty string rather than using unset.
#an 'array get' will resynchronise.
#Even if an env variable didn't exist before - setting it to empty string can get it to this inconsistent state.
array get ::env
}
#do an array read on ::env
foreach {v vinfo} $punkenv_config {
if {[info exists ::env($v)]} {
set c2 [set ::env($v)]
} else {
set c2 "(NOT SET)"
}
set help ""
if {[dict exists $vinfo help]} {
set help [dict get $vinfo help]
if {[catch {lsearch -stride 2 {a b} b}]} {
set indent " "
append warningblock \n "[a+ web-red]warning: lsearch does not seem to support -stride option" \n
append warningblock "${indent}(Consider upgrading to a late release of tcl 8.6 or tcl 9+ )" \n
append warningblock [a]
} else {
if {[punk::lib::check::has_tclbug_lsearch_strideallinline]} {
set indent " "
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_lsearch_strideallinline returned true!" \n
append warningblock "${indent}(lsearch using -stride -all -inline -subindices does not return values corresponding to subindex when a single -index value is used)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/5a1aaa201d]"
append warningblock [a]
}
$t add_row [list $v $c2 $help]
}
$t configure_column 0 -headers [list "Punk environment vars"]
$t configure_column 0 -minwidth [expr {[$t column_datawidth 0]+4}] -blockalign left -textalign left -header_colspans {any}
if {[punk::lib::check::has_tclbug_list_quoting_emptyjoin]} {
set indent " "
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_list_quoting returned true!" \n
append warningblock "${indent}lists elements not properly quoted in some cases. e.g 'list {*}[lindex {etc #foo} 1] {*}[list]' (#foo not braced)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/e38dce74e2]"
}
lappend chunks [list stdout $text]
}
env - environment {
set text ""
#todo - move to punk::config?
upvar ::punk::config::punk_env_vars_config punkenv_config
upvar ::punk::config::other_env_vars_config otherenv_config
set known_punk [dict keys $punkenv_config]
set known_other [dict keys $otherenv_config]
append text \n
set usetable 1
if {$usetable} {
set t [textblock::class::table new -show_hseps 0 -show_header 1 -ansiborder_header [a+ web-green]]
if {"windows" eq $::tcl_platform(platform)} {
#If any env vars have been set to empty string - this is considered a deletion of the variable on windows.
#The Tcl ::env array is linked to the underlying process view of the environment
#- but info exists ::env(var) can misreport as true if it has been deleted by setting to empty string rather than using unset.
#an 'array get' will resynchronise.
#Even if an env variable didn't exist before - setting it to empty string can get it to this inconsistent state.
array get ::env
}
#do an array read on ::env
foreach {v vinfo} $punkenv_config {
if {[info exists ::env($v)]} {
set c2 [set ::env($v)]
} else {
set c2 "(NOT SET)"
}
set help ""
if {[dict exists $vinfo help]} {
set help [dict get $vinfo help]
}
$t add_row [list $v $c2 $help]
}
$t configure_column 0 -headers [list "Punk environment vars"]
$t configure_column 0 -minwidth [expr {[$t column_datawidth 0]+4}] -blockalign left -textalign left -header_colspans {any}
set punktable [$t print]
$t destroy
set punktable [$t print]
$t destroy
set t [textblock::class::table new -show_hseps 0 -show_header 1 -ansiborder_header [a+ web-green]]
foreach {v vinfo} $otherenv_config {
if {[info exists ::env($v)]} {
set env_val [set ::env($v)]
if {[string match "*_TM_PATH" $v]} {
set entries [split $env_val $::tcl_platform(pathSeparator)]
set c2 [join $entries \n]
set t [textblock::class::table new -show_hseps 0 -show_header 1 -ansiborder_header [a+ web-green]]
foreach {v vinfo} $otherenv_config {
if {[info exists ::env($v)]} {
set env_val [set ::env($v)]
if {[string match "*_TM_PATH" $v]} {
set entries [split $env_val $::tcl_platform(pathSeparator)]
set c2 [join $entries \n]
} else {
set c2 $::env($v)
}
} else {
set c2 $::env($v)
set c2 "(NOT SET)"
}
} else {
set c2 "(NOT SET)"
$t add_row [list $v $c2]
}
$t add_row [list $v $c2]
}
$t configure_column 0 -headers [list "Other environment vars"]
$t configure_column 0 -minwidth [expr {[$t column_datawidth 0]+4}] -blockalign left -textalign left -header_colspans {any}
$t configure_column 0 -headers [list "Other environment vars"]
$t configure_column 0 -minwidth [expr {[$t column_datawidth 0]+4}] -blockalign left -textalign left -header_colspans {any}
set othertable [$t print]
$t destroy
#append text [textblock::join -- $punktable " " $othertable]\n
append text $punktable\n$othertable\n
} else {
set othertable [$t print]
$t destroy
#append text [textblock::join -- $punktable " " $othertable]\n
append text $punktable\n$othertable\n
} else {
append text $linesep\n
append text "punk environment vars:\n"
append text $linesep\n
set col1 [string repeat " " 25]
set col2 [string repeat " " 50]
foreach v $known_punk {
set c1 [overtype::left $col1 $v]
if {[info exists ::env($v)]} {
set c2 [overtype::left $col2 [set ::env($v)]]
} else {
set c2 [overtype::right $col2 "(NOT SET)"]
}
append text "$c1 $c2\n"
}
append text $linesep\n
}
lappend chunks [list stdout $text]
}
if {$topic in [list console terminal]} {
set indent [string repeat " " [string length "WARNING: "]]
lappend cstring_tests [dict create\
type "PM "\
msg "UN"\
f7 punk::ansi::controlstring_PM\
f7prefix "7bit ESC ^ secret "\
f7suffix "safe"\
f8 punk::ansi::controlstring_PM8\
f8prefix "8bit \\x9e secret "\
f8suffix "safe"\
]
lappend cstring_tests [dict create\
type SOS\
msg "NOT"\
f7 punk::ansi::controlstring_SOS\
f7prefix "7bit ESC X string "\
f7suffix " hidden"\
f8 punk::ansi::controlstring_SOS8\
f8prefix "8bit \\x98 string "\
f8suffix " hidden"\
]
lappend cstring_tests [dict create\
type APC\
msg "NOT"\
f7 punk::ansi::controlstring_APC\
f7prefix "7bit ESC _ APPLICATION PROGRAM COMMAND "\
f7suffix " hidden"\
f8 punk::ansi::controlstring_APC8\
f8prefix "8bit \\x9f APPLICATION PROGRAM COMMAND "\
f8suffix " hidden"\
]
foreach test $cstring_tests {
set m [[dict get $test f7] [dict get $test msg]]
set hidden_width_m [punk::console::test_char_width $m]
set m8 [[dict get $test f8] [dict get $test msg]]
set hidden_width_m8 [punk::console::test_char_width $m8]
if {$hidden_width_m != 0 || $hidden_width_m8 != 0} {
if {$hidden_width_m == 0} {
set d "[a+ green bold][dict get $test f7prefix][a red]${m}[a][a+ green bold][dict get $test f7suffix][a]"
} else {
set d "[a+ yellow bold][dict get $test f7prefix][a red]$m[a][a+ yellow bold][dict get $test f7suffix][a]"
append text $linesep\n
append text "punk environment vars:\n"
append text $linesep\n
set col1 [string repeat " " 25]
set col2 [string repeat " " 50]
foreach v $known_punk {
set c1 [overtype::left $col1 $v]
if {[info exists ::env($v)]} {
set c2 [overtype::left $col2 [set ::env($v)]]
} else {
set c2 [overtype::right $col2 "(NOT SET)"]
}
append text "$c1 $c2\n"
}
append text $linesep\n
}
lappend chunks [list stdout $text]
}
console - term - terminal {
set indent [string repeat " " [string length "WARNING: "]]
lappend cstring_tests [dict create\
type "PM "\
msg "UN"\
f7 punk::ansi::controlstring_PM\
f7prefix "7bit ESC ^ secret "\
f7suffix "safe"\
f8 punk::ansi::controlstring_PM8\
f8prefix "8bit \\x9e secret "\
f8suffix "safe"\
]
lappend cstring_tests [dict create\
type SOS\
msg "NOT"\
f7 punk::ansi::controlstring_SOS\
f7prefix "7bit ESC X string "\
f7suffix " hidden"\
f8 punk::ansi::controlstring_SOS8\
f8prefix "8bit \\x98 string "\
f8suffix " hidden"\
]
lappend cstring_tests [dict create\
type APC\
msg "NOT"\
f7 punk::ansi::controlstring_APC\
f7prefix "7bit ESC _ APPLICATION PROGRAM COMMAND "\
f7suffix " hidden"\
f8 punk::ansi::controlstring_APC8\
f8prefix "8bit \\x9f APPLICATION PROGRAM COMMAND "\
f8suffix " hidden"\
]
foreach test $cstring_tests {
set m [[dict get $test f7] [dict get $test msg]]
set hidden_width_m [punk::console::test_char_width $m]
set m8 [[dict get $test f8] [dict get $test msg]]
set hidden_width_m8 [punk::console::test_char_width $m8]
if {$hidden_width_m != 0 || $hidden_width_m8 != 0} {
if {$hidden_width_m == 0} {
set d "[a+ green bold][dict get $test f7prefix][a red]${m}[a][a+ green bold][dict get $test f7suffix][a]"
} else {
set d "[a+ yellow bold][dict get $test f7prefix][a red]$m[a][a+ yellow bold][dict get $test f7suffix][a]"
}
if {$hidden_width_m8 == 0} {
set d8 "[a+ green ][dict get $test f8prefix][a red]$m8[a][a+ green][dict get $test f8suffix][a]"
} else {
set d8 "[a+ yellow bold][dict get $test f8prefix][a red]$m8[a][a+ yellow bold][dict get $test f8suffix][a]"
}
append warningblock \n "WARNING: terminal doesn't hide all [dict get $test type] control strings: $d $d8"
}
if {$hidden_width_m8 == 0} {
set d8 "[a+ green ][dict get $test f8prefix][a red]$m8[a][a+ green][dict get $test f8suffix][a]"
} else {
set d8 "[a+ yellow bold][dict get $test f8prefix][a red]$m8[a][a+ yellow bold][dict get $test f8suffix][a]"
}
if {![catch {punk::console::check::has_bug_legacysymbolwidth} result]} {
if {$result} {
append warningblock \n "WARNING: terminal has legacysymbolwidth bug - screen position for symbol reports 2 wide but displays 1 wide."
append warningblock \n $indent "Layout using 'legacy symbols for computing' affected."
append warningblock \n $indent "(e.g textblock frametype block2 unsupported)"
append warningblock \n $indent "This can cause extreme layout deformation when ANSI is present"
append warningblock \n $indent "In some cases unwanted spacing effects occur at a distance from the characters causing it"
}
append warningblock \n "WARNING: terminal doesn't hide all [dict get $test type] control strings: $d $d8"
} else {
append warningblock \n "WARNING: terminal unable to check for legacysymbolwidth bug. err:$result"
}
}
if {![catch {punk::console::check::has_bug_legacysymbolwidth} result]} {
if {$result} {
append warningblock \n "WARNING: terminal has legacysymbolwidth bug - screen position for symbol reports 2 wide but displays 1 wide."
append warningblock \n $indent "Layout using 'legacy symbols for computing' affected."
append warningblock \n $indent "(e.g textblock frametype block2 unsupported)"
append warningblock \n $indent "This can cause extreme layout deformation when ANSI is present"
append warningblock \n $indent "In some cases unwanted spacing effects occur at a distance from the characters causing it"
if {![catch {punk::console::check::has_bug_zwsp} result]} {
if {$result} {
append warningblock \n "WARNING: terminal has zero width space (\\u200b) bug - cursor position incremented when it shouldn't be."
append warningblock \n $indent "The zwsp may or may not be displayed. zwsp contributes to line length and wrapping point"
}
} else {
append warningblock \n "WARNING: terminal unable to check for zwsp bug. err:$result"
}
} else {
append warningblock \n "WARNING: terminal unable to check for legacysymbolwidth bug. err:$result"
}
if {![catch {punk::console::check::has_bug_zwsp} result]} {
if {$result} {
append warningblock \n "WARNING: terminal has zero width space (\\u200b) bug - cursor position incremented when it shouldn't be."
append warningblock \n $indent "The zwsp may or may not be displayed. zwsp contributes to line length and wrapping point"
set grapheme_support [punk::console::grapheme_cluster_support]
#mode, 1 = set, 2 = unset. (0 = mode not recognised, 3 = permanently set, 4 = permanently unset)
if {![dict size $grapheme_support] || [dict get $grapheme_support mode] eq "unsupported" } {
append warningblock \n "WARNING: terminal either doesn't support grapheme clusters, or doesn't report so via decmode 2027 query."
if {[dict size $grapheme_support] && [dict get $grapheme_support available]} {
append warningblock \n $indent "(but punk::console::grapheme_cluster_support has determined it is probably available)"
}
} else {
if {![dict get $grapheme_support available]} {
switch -- [dict get $grapheme_support mode] {
"unset" {
append warningblock \n "WARNING: terminal reports via decmode 2027 that grapheme cluster support is off."
}
"permanently_unset" {
append warningblock \n "WARNING: terminal reports via decmode 2027 that grapheme cluster support is permanently off."
}
"BAD_RESPONSE" {
append warningblock \n "WARNING: terminal doesn't seem to recognize decmode 2027 query. No grapheme cluster support."
}
}
}
}
if {![string length $warningblock]} {
set text "No terminal warnings\n"
lappend chunks [list stdout $text]
}
} else {
append warningblock \n "WARNING: terminal unable to check for zwsp bug. err:$result"
}
topics - help {
set text ""
set topics [dict create\
"topics|help" "List help topics"\
"tcl" "Tcl version warnings"\
"env|environment" "punkshell environment vars"\
"console|terminal" "Some console behaviour tests and warnings"\
]
set grapheme_support [punk::console::grapheme_cluster_support]
#mode, 1 = set, 2 = unset. (0 = mode not recognised, 3 = permanently set, 4 = permanently unset)
if {![dict size $grapheme_support] || [dict get $grapheme_support mode] eq "unsupported" } {
append warningblock \n "WARNING: terminal either doesn't support grapheme clusters, or doesn't report so via decmode 2027 query."
if {[dict size $grapheme_support] && [dict get $grapheme_support available]} {
append warningblock \n $indent "(but punk::console::grapheme_cluster_support has determined it is probably available)"
set t [textblock::class::table new -show_seps 0]
$t add_column -headers [list "Topic"]
$t add_column
foreach {k v} $topics {
$t add_row [list $k $v]
}
} else {
if {![dict get $grapheme_support available]} {
switch -- [dict get $grapheme_support mode] {
"unset" {
append warningblock \n "WARNING: terminal reports via decmode 2027 that grapheme cluster support is off."
}
"permanently_unset" {
append warningblock \n "WARNING: terminal reports via decmode 2027 that grapheme cluster support is permanently off."
}
"BAD_RESPONSE" {
append warningblock \n "WARNING: terminal doesn't seem to recognize decmode 2027 query. No grapheme cluster support."
set widest0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$widest0 + 4}]
append text \n [$t print]
lappend chunks [list stdout $text]
}
default {
set text ""
set cinfo [uplevel 1 [list punk::ns::cmdwhich [lindex $topicparts 0]]]
set wtype [dict get $cinfo whichtype]
if {$wtype eq "notfound"} {
set externalinfo [auto_execok [lindex $topicparts 0]]
if {[string length $externalinfo]} {
set text "$topicparts"
append text \n "Base type: External command"
append text \n "$externalinfo [lrange $topicparts 1 end]"
} else {
set text "$topicparts\n"
append text "No matching internal or external command found"
}
} else {
set text "[dict get $cinfo which] [lrange $topicparts 1 end]"
append text \n "Base type: $wtype"
set synopsis [uplevel 1 [list punk::ns::synopsis {*}$topicparts]]
set synshow ""
foreach sline [split $synopsis \n] {
if {[regexp {\s*#.*} $sline]} {
append synshow [punk::ansi::a+ bold term-darkgreen Term-white]$sline[punk::ansi::a] \n
} else {
append synshow $sline \n
}
}
if {[string index $synshow end] eq "\n"} {
set synshow [string range $synshow 0 end-1]
}
append text \n $synshow
}
lappend chunks [list stdout $text]
}
}
lappend chunks [list stderr $warningblock]
if {$topic in [list topics help]} {
set text ""
set topics [dict create\
"topics|help" "List help topics"\
"tcl" "Tcl version warnings"\
"env|environment" "punkshell environment vars"\
"console|terminal" "Some console behaviour tests and warnings"\
]
set t [textblock::class::table new -show_seps 0]
$t add_column -headers [list "Topic"]
$t add_column
foreach {k v} $topics {
$t add_row [list $k $v]
}
set widest0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$widest0 + 4}]
append text \n[$t print]
lappend chunks [list stdout $text]
}
lappend chunks [list stderr $warningblock]
return $chunks
}
proc help {args} {
set chunks [help_chunks {*}$args]
foreach chunk $chunks {
lassign $chunk chan text
puts -nonewline $chan $text
}
}
proc mode {{raw_or_line query}} {
package require punk::console
tailcall ::punk::console::mode $raw_or_line

177
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/ansi-0.1.1.tm

@ -340,6 +340,28 @@ tcl::namespace::eval punk::ansi::class {
}
}
tcl::namespace::eval punk::ansi {
namespace eval argdoc {
variable PUNKARGS
# -- --- --- --- ---
#non colour SGR codes
# we can use these directly via ${$I} etc without marking a definition with @dynamic
#This is because they don't need to change when colour switched on and off.
#chicken/egg - need to use literals here
set I "\x1b\[3m" ;# [a+ italic]
set NI "\x1b\[23m" ;# [a+ noitalic]
set B "\x1b\[1m" ;# [a+ bold]
set N "\x1b\[22m" ;# [a+ normal]
set T "\x1b\[1\;4m" ;# [a+ bold underline]
set NT "\x1b\[22\;24m\x1b\[4:0m" ;# [a+ normal nounderline]
set LC \u007b ;#left curly brace
set RC \u007d ;#right curly brace
# -- --- --- --- ---
#namespace import ::punk::args::helpers::*
}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
tcl::namespace::eval punk::ansi {
@ -2262,59 +2284,74 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
return $displaytable
}
lappend PUNKARGS [list {
@id -id ::punk::ansi::a?
@cmd -name "punk::ansi::a?"\
-summary\
"ANSI colour information"\
-help\
""
@form -form "sgr_overview"
@values -form "sgr_overview" -min 0 -max 0
@form -form "term"
@leaders -form "term" -min 1 -max 1
term -type literal(term) -help\
"256 term colours"
@opts -min 0 -max 0
@values -form "term" -min 0 -max -1
panel -type string -optional 1 -multiple 1 -default {16 main greyscale note}\
-choices {16 main greyscale pastel rainbow note}
@form -form "tk"
@leaders -form "tk" -min 1 -max 1
tk -type literal(tk)|literal(TK) -help\
"Tk colours"
@opts -form "tk"
-merged -type none -help\
"If this flag is supplied - show colour names with the same RGB
values together."
@values -form "tk" -min 0 -max -1
glob -type string -optional 1 -multiple 1 -help\
"A glob string such as *green*.
Multiple glob entries can be provided. The search is case insensitive"
@form -form "web"
@values -form "web" -min 1 -max -1
web -type literal(web) -help\
"Web colours"
panel -type string -optional 1 -multiple 1 -choices {basic pink red orange yellow brown purple blue cyan green white gray}
@form -form "x11"
@values -form "x11" -min 1 -max 1
x11 -type literal(x11) -help\
"x11 colours"
@form -form "sample"
@values -form "sample" -min 1 -max -1
colourcode -type sgr|Sgr|literalprefix(term-)|literalprefix(Term-)|literalprefix(web-)|literalprefix(Web-)|literalprefix(rgb)|literalprefix(Rgb)\
-optional 0\
-multiple 1
}]
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::ansi::a?
@cmd -name "punk::ansi::a?"\
-summary\
"ANSI colour information"\
-help\
"With no arguments - display an overview information panel.
With the first argument one of:
${$B}term tk TK web x11${$N}
Display a more specific panel of colour information.
With arguments of individual colourcodes from any of the above
sets, display a small diagnostic table showing a sample of
the individual and combined effect(s), along with indications
of the raw ANSI codes."
@form -form "sgr_overview"
@values -form "sgr_overview" -min 0 -max 0
@form -form "term"
@leaders -form "term" -min 1 -max 1
term -type literal(term) -help\
"256 term colours"
@opts -min 0 -max 0
@values -form "term" -min 0 -max -1
panel -type string -optional 1 -multiple 1 -default {16 main greyscale note}\
-choices {16 main greyscale pastel rainbow note}
@form -form "tk"
@leaders -form "tk" -min 1 -max 1
tk -type literal(tk)|literal(TK) -help\
"Tk colours"
@opts -form "tk"
-merged -type none -help\
"If this flag is supplied - show colour names with the same RGB
values together."
@values -form "tk" -min 0 -max -1
glob -type string -optional 1 -multiple 1 -help\
"A glob string such as *green*.
Multiple glob entries can be provided. The search is case insensitive"
@form -form "web"
@values -form "web" -min 1 -max -1
web -type literal(web) -help\
"Web colours"
panel -type string -optional 1 -multiple 1 -choices {basic pink red orange yellow brown purple blue cyan green white gray}
@form -form "x11"
@values -form "x11" -min 1 -max 1
x11 -type literal(x11) -help\
"x11 colours"
@form -form "sample"
@values -form "sample" -min 1 -max -1
#review literalprefix is not the right thing here! (literalprefix means a tcl::prefix::match on the value)
#colourcode -type sgr|Sgr|stringstartswith(term-)|stringstartswith(Term-)|stringstartswith(web-)|stringstartswith(Web-)|stringstartswith(rgb)|stringstartswith(Rgb)
colourcode -type string|stringstartswith(term-)|stringstartswith(Term-)|stringstartswith(web-)|stringstartswith(Web-)|stringstartswith(rgb)|stringstartswith(Rgb)\
-typesynopsis {${$I}sgr${$NI}|${$I}Sgr${$NI}|${$I}colourcode${$NI}}\
-optional 0\
-multiple 1
}]
}
proc a? {args} {
#*** !doctools
#[call [fun a?] [opt {ansicode...}]]
@ -2414,8 +2451,8 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
append out \n
append out "[a+ {*}$fc web-white]Combination testing[a]" \n
append out [textblock::join -- $indent "Example: a? red brightgreen underline Tk-slategrey italic"] \n
append out [textblock::join -- $indent "This will show a small table of each applied code and a RESULT row. The 'red' in this case is redundant,"] \n
append out [textblock::join -- $indent "so a final MERGED row displays with an alert 'REDUNDANCIES FOUND'."] \n
append out [textblock::join -- $indent "This will show a small table of each applied code and a RESULT row. The 'red' in this case is redundant, because there are"] \n
append out [textblock::join -- $indent "two foreground colours (lowercase first letter) so a final MERGED row displays with an alert 'REDUNDANCIES FOUND'."] \n
append out [textblock::join -- $indent "The final columns of RESULT and MERGED (showing raw ANSI sequence) will differ if the arguments aren't in canonical order."] \n
append out [textblock::join -- $indent "The MERGED line will only display if there are redundancies or different ordering."] \n
@ -2674,14 +2711,26 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
} else {
set tkcolours [list]
}
foreach c $webcolours {
append info \n web-$c
}
foreach c $x11colours {
append info \n x11-$c
}
foreach c $tkcolours {
append info \n tk-$c
if {[string is upper -strict [string index $pfx 0]]} {
foreach c $webcolours {
append info \n Web-$c
}
foreach c $x11colours {
append info \n X11-$c
}
foreach c $tkcolours {
append info \n Tk-$c
}
} else {
foreach c $webcolours {
append info \n web-$c
}
foreach c $x11colours {
append info \n x11-$c
}
foreach c $tkcolours {
append info \n tk-$c
}
}
$t add_row [list $i "$info" $s [ansistring VIEW $s]]
}
@ -8723,7 +8772,7 @@ interp alias {} ansistring {} ::punk::ansi::ansistring
namespace eval ::punk::args::register {
#use fully qualified so 8.6 doesn't find existing var in global namespace
lappend ::punk::args::register::NAMESPACES ::punk::ansi ::punk::ansi::class ::punk::ansi::ta
lappend ::punk::args::register::NAMESPACES ::punk::ansi ::punk::ansi::argdoc ::punk::ansi::class ::punk::ansi::ta
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++

153
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/args-0.2.1.tm

@ -359,16 +359,22 @@ tcl::namespace::eval ::punk::args::helpers {
#puts $str
#puts stderr -------------------
#rudimentary colourising (not full tcl syntax parsing)
#highlight comments first - so that we can also highlight braces within comments to help with detecting unbalanced braces/square brackets in comments
switch -- $opt_syntax {
tcl {
#rudimentary colourising (not full tcl syntax parsing)
#Note that this can highlight ;# in some places as a comment where it's not appropriate
# e.g inside a regexp
#highlight comments first - so that we can also highlight braces within comments to help with detecting unbalanced braces/square brackets in comments
#result lines often indicated in examples by \u2192 →
#however - it's not present on each line of output, instead indents are used - so we can't so easily highlight all appropriate rows(?)
set str [punk::grepstr -return all -highlight {Term-grey term-darkgreen} {^\s*#.*} $str] ;#Note, will not highlight comments at end of line - like this one
set str [punk::grepstr -return all -highlight {Term-grey term-darkgreen} {;\s*(#.*)} $str]
#TODO - fix grepstr highlighting (bg issues - why?)
set str [punk::grepstr -return all -highlight {Term-grey term-darkblue} {\{|\}} $str]
#Note that if we were to highlight based on the regexp {\{|\}} then the inserted ansi would come between
# the backslash and brace in \{ or \} - this breaks the syntactic structure causing problems.
set str [punk::grepstr -return all -highlight {Term-grey tk-darkblue} {^\{|[^\\](\{+)} $str]
set str [punk::grepstr -return all -highlight {Term-grey tk-darkblue} {[^\\](\}+)} $str]
set str [punk::grepstr -return all -highlight {Term-grey term-orange1} {\[|\]} $str]
#puts stderr -------------------
#puts $str
@ -460,7 +466,7 @@ tcl::namespace::eval ::punk::args::helpers {
tcl::namespace::eval punk::args {
package require punk::assertion
#if 'package forget' was called on this package (e.g when loading test::punk::args) then assert may already exist in the namespace
#procs can be overridden silently, but not imports
#namespace import will fail if target exists
catch {
namespace import ::punk::assertion::assert
}
@ -469,9 +475,9 @@ tcl::namespace::eval punk::args {
variable PUNKARGS ;#list of our own punk::args function argument definitions - loaded with punk::args::define at the end.
tcl::namespace::export {[a-z]*}
variable rawdef_cache
if {![info exists rawdef_cache]} {
set rawdef_cache [tcl::dict::create] ;#key on rawdef list - return dict of {-id <id> -dynamic 0|1}
variable rawdef_cache_about
if {![info exists rawdef_cache_about]} {
set rawdef_cache_about [tcl::dict::create] ;#key on rawdef list - return dict of {-id <id> -dynamic 0|1}
}
variable id_cache_rawdef
if {![info exists id_cache_rawdef]} {
@ -487,9 +493,9 @@ tcl::namespace::eval punk::args {
set argdefcache_unresolved [tcl::dict::create] ;#key = original template list supplied to 'define', value = 2-element list: (tstr-parsed split of template) & (unresolved params)
}
variable argdata_cache
if {![info exists argdata_cache]} {
set argdata_cache [tcl::dict::create]
variable rawdef_cache_argdata
if {![info exists rawdef_cache_argdata]} {
set rawdef_cache_argdata [tcl::dict::create]
}
variable id_counter
@ -979,11 +985,11 @@ tcl::namespace::eval punk::args {
error todo
}
proc define {args} {
variable rawdef_cache
variable rawdef_cache_about
variable id_cache_rawdef
variable argdata_cache
if {[dict exists $rawdef_cache $args]} {
return [dict get [dict get $rawdef_cache $args] -id]
#variable rawdef_cache_argdata
if {[dict exists $rawdef_cache_about $args]} {
return [dict get [dict get $rawdef_cache_about $args] -id]
} else {
set lvl 2
set id [rawdef_id $args $lvl]
@ -991,46 +997,40 @@ tcl::namespace::eval punk::args {
#we seem to be re-creating a previously defined id...
#clear any existing caches for this id
undefine $id 0
##dict unset argdata_cache $prevraw ;#silently does nothing if key not present
#dict for {k v} $argdata_cache {
# if {[dict get $v id] eq $id} {
# dict unset argdata_cache $k
# }
#}
#dict for {k v} $rawdef_cache {
# if {[dict get $v -id] eq $id} {
# dict unset rawdef_cache $k
# }
#}
#dict unset id_cache_rawdef $id
}
set is_dynamic [rawdef_is_dynamic $args]
set defspace [uplevel 1 {::namespace current}]
dict set rawdef_cache $args [dict create -id $id -dynamic $is_dynamic -defspace $defspace]
dict set rawdef_cache_about $args [dict create -id $id -dynamic $is_dynamic -defspace $defspace]
dict set id_cache_rawdef $id $args
return $id
}
}
proc undefine {id {quiet 0}} {
variable rawdef_cache
#review - alias?
variable rawdef_cache_about
variable id_cache_rawdef
variable argdata_cache
variable rawdef_cache_argdata
if {[id_exists $id]} {
if {!$quiet} {
puts stderr "punk::args::undefine clearing existing data for id:$id"
}
dict for {k v} $argdata_cache {
if {[dict get $v id] eq $id} {
dict unset argdata_cache $k
if {[dict exists $id_cache_rawdef $id]} {
set deflist [dict get $id_cache_rawdef $id]
dict unset rawdef_cache_about $deflist
dict unset rawdef_cache_argdata $deflist
dict unset id_cache_rawdef $id
} else {
dict for {k v} $rawdef_cache_argdata {
if {[dict get $v id] eq $id} {
dict unset rawdef_cache_argdata $k
}
}
}
dict for {k v} $rawdef_cache {
if {[dict get $v -id] eq $id} {
dict unset rawdef_cache $k
dict for {k v} $rawdef_cache_about {
if {[dict get $v -id] eq $id} {
dict unset rawdef_cache_about $k
}
}
}
dict unset id_cache_rawdef $id
} else {
if {!$quiet} {
puts stderr "punk::args::undefine unable to find id: '$id'"
@ -1039,17 +1039,26 @@ tcl::namespace::eval punk::args {
}
#'punk::args::parse $args withdef $deflist' can raise parsing error after an autoid was generated
# In this case we don't see the autoid in order to delete it
#proc undefine_deflist {deflist} {
#}
proc undefine_deflist {deflist} {
variable rawdef_cache_about
variable id_cache_rawdef
variable rawdef_cache_argdata
if {[dict exists $rawdef_cache_about $deflist -id]} {
set id [dict get $rawdef_cache_about $deflist -id]
dict unset rawdef_cache_about $deflist
dict unset rawdef_cache_argdata $deflist
dict unset id_cache_rawdef $id
}
}
proc idquery_info {id} {
variable id_cache_rawdef
variable rawdef_cache
variable rawdef_cache_about
if {[dict exists $id_cache_rawdef $id]} {
set sep [string repeat - 40]
set rawdef [dict get $id_cache_rawdef $id]
if {[dict exists $rawdef_cache $rawdef]} {
set idinfo [dict get $rawdef_cache $rawdef]
if {[dict exists $rawdef_cache_about $rawdef]} {
set idinfo [dict get $rawdef_cache_about $rawdef]
} else {
set idinfo ""
}
@ -1060,11 +1069,11 @@ tcl::namespace::eval punk::args {
append result \n "id info:"
append result \n $idinfo
append result \n $sep
variable argdata_cache
variable rawdef_cache_argdata
#lsearch -stride not avail (or buggy) in some 8.6 interps - search manually for now (2025). todo - modernize some time after Tcl 9.0/9.1 more widespread.(2027?)
#check for and report if id is present multiple times
set argdata_records [list]
dict for {k v} $argdata_cache {
dict for {k v} $rawdef_cache_argdata {
if {[dict get $v id] eq $id} {
if {$k eq $rawdef} {
lappend argdata_records [list 1 $k $v]
@ -1100,12 +1109,20 @@ tcl::namespace::eval punk::args {
dict get [resolve {*}$args] id
}
lappend PUNKARGS [list {
@id -id ::punk::args::resolve
@cmd -name punk::args::resolve -help\
""
@values -min 0 -max -1
arg -type any -multiple 1 -help\
"rawdef line block"
}]
proc resolve {args} {
variable rawdef_cache
variable rawdef_cache_about
variable id_cache_rawdef
set defspace ""
if {[dict exists $rawdef_cache $args]} {
set cinfo [dict get $rawdef_cache $args]
if {[dict exists $rawdef_cache_about $args]} {
set cinfo [dict get $rawdef_cache_about $args]
set id [dict get $cinfo -id]
set is_dynamic [dict get $cinfo -dynamic]
if {[dict exists $cinfo -defspace]} {
@ -1116,12 +1133,13 @@ tcl::namespace::eval punk::args {
set id [rawdef_id $args]
puts stderr "Warning: punk::args::resolve called with undefined id:$id"
set is_dynamic [rawdef_is_dynamic $args]
dict set rawdef_cache $args [dict create -id $id -dynamic $is_dynamic]
#-defspace ???
dict set rawdef_cache_about $args [dict create -id $id -dynamic $is_dynamic]
dict set id_cache_rawdef $id $args
}
variable argdata_cache
variable rawdef_cache_argdata
variable argdefcache_unresolved
@ -1155,8 +1173,8 @@ tcl::namespace::eval punk::args {
if {!$is_dynamic} {
#todo - don't use cached version if 'colour off' vs 'colour on' different to when last resolved!
#(e.g example blocks will still have colour if previously resolved)
if {[tcl::dict::exists $argdata_cache $cache_key]} {
return [tcl::dict::get $argdata_cache $cache_key]
if {[tcl::dict::exists $rawdef_cache_argdata $cache_key]} {
return [tcl::dict::get $rawdef_cache_argdata $cache_key]
}
set normargs [list]
foreach a $textargs {
@ -1235,10 +1253,10 @@ tcl::namespace::eval punk::args {
tcl::dict::set argdefcache_unresolved $cache_key $pt_params
}
}
#argdata_cache should be limited in some fashion or will be a big memory leak???
if {[tcl::dict::exists $argdata_cache [list $optionspecs]]} {
#rawdef_cache_argdata should be limited in some fashion or will be a big memory leak???
if {[tcl::dict::exists $rawdef_cache_argdata [list $optionspecs]]} {
#resolved cache version exists
return [tcl::dict::get $argdata_cache [list $optionspecs]]
return [tcl::dict::get $rawdef_cache_argdata [list $optionspecs]]
}
}
@ -2669,10 +2687,12 @@ tcl::namespace::eval punk::args {
form_info $form_info\
]
tcl::dict::set argdata_cache $cache_key $argdata_dict
#REVIEW
tcl::dict::set rawdef_cache_argdata $cache_key $argdata_dict
if {$is_dynamic} {
#also cache resolved version
tcl::dict::set argdata_cache [list $optionspecs] $argdata_dict
tcl::dict::set rawdef_cache_argdata [list $optionspecs] $argdata_dict
}
#tcl::dict::set id_cache_rawdef $DEF_definition_id $args
@ -3094,10 +3114,10 @@ tcl::namespace::eval punk::args {
}
proc is_dynamic {id} {
variable id_cache_rawdef
variable rawdef_cache
variable rawdef_cache_about
set deflist [raw_def $id]
if {[dict exists $rawdef_cache $deflist -dynamic]} {
return [dict get $rawdef_cache $deflist -dynamic]
if {[dict exists $rawdef_cache_about $deflist -dynamic]} {
return [dict get $rawdef_cache_about $deflist -dynamic]
}
return [rawdef_is_dynamic $deflist]
#@dynamic only has meaning as 1st element of a def in the deflist
@ -4008,7 +4028,16 @@ tcl::namespace::eval punk::args {
}
if {$synopsis ne ""} {
if {$use_table} {
$t configure_header $h -colspans $arg_colspans -values [list Synopsis: [punk::ansi::ansiwrap brightwhite $synopsis]]
set form_names [dict get $spec_dict form_names]
set synhelp "Synopsis:"
if {[llength $form_names] > 1} {
set fn 0
foreach fname $form_names {
append synhelp \n " i -form $fn \U2026"
incr fn
}
}
$t configure_header $h -colspans $arg_colspans -values [list $synhelp [punk::ansi::ansiwrap brightwhite $synopsis]]
} else {
#todo
lappend errlines "Synopsis:\n$synopsis"

1550
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/args/moduledoc/tclcore-0.1.0.tm

File diff suppressed because it is too large Load Diff

40
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/assertion-0.1.0.tm

@ -66,38 +66,6 @@ package require Tcl 8.6-
#*** !doctools
#[section API]
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# oo::class namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
tcl::namespace::eval punk::assertion::class {
#*** !doctools
#[subsection {Namespace punk::assertion::class}]
#[para] class definitions
if {[tcl::info::commands [tcl::namespace::current]::interface_sample1] eq ""} {
#*** !doctools
#[list_begin enumerated]
# oo::class create interface_sample1 {
# #*** !doctools
# #[enum] CLASS [class interface_sample1]
# #[list_begin definitions]
# method test {arg1} {
# #*** !doctools
# #[call class::interface_sample1 [method test] [arg arg1]]
# #[para] test method
# puts "test: $arg1"
# }
# #*** !doctools
# #[list_end] [comment {-- end definitions interface_sample1}]
# }
#*** !doctools
#[list_end] [comment {--- end class enumeration ---}]
}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#keep 2 namespaces for assertActive and assertInactive so there is introspection available via namespace origin
tcl::namespace::eval punk::assertion::primary {
@ -149,9 +117,13 @@ tcl::namespace::eval punk::assertion {
proc do_ns_import {} {
uplevel 1 [list tcl::namespace::import ::punk::assertion::primary::assertActive]
}
do_ns_import
#puts --------BBB
rename assertActive assert
if {[catch {
do_ns_import
rename assertActive assert
} errM]} {
puts stderr "punk::assertion error during load - assert/assertActive functions already present?\n$errM"
}
}

4
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/du-0.1.0.tm

@ -961,9 +961,9 @@ namespace eval punk::du {
proc get_vfsmounts_in_folder {folderpath} {
set vfsmounts [list]
if {![llength [package provide vfs]]} {
return [list]
return [list]
}
set fpath [punk::objclone $folderpath]
set fpath [punk::valcopy $folderpath]
set is_rel 0
if {[file pathtype $fpath] ne "absolute"} {
set fpath [file normalize $fpath]

12
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/lib-0.1.3.tm

@ -4071,11 +4071,11 @@ namespace eval punk::lib {
interp eval $interp [list tcl::tm::add {*}[lreverse [tcl::tm::list]]]
}
proc objclone {obj} {
proc valcopy {obj} {
append obj2 $obj {}
}
proc set_clone {varname obj} {
#used by repl's codeinterp. Maintains internal rep, easier to call e.g interp eval code [list punk::set_clone varnmame $val]
proc set_valcopy {varname obj} {
#used by repl's codeinterp. Maintains internal rep, easier to call e.g interp eval code [list punk::set_valcopy varnmame $val]
append obj2 $obj {}
uplevel 1 [list set $varname $obj2]
}
@ -4095,9 +4095,9 @@ namespace eval punk::lib {
set default_groupsize 3
set results [list]
set nums [objclone $numbers_or_commaformattednumbers] ;#stops single num from getting internal rep of list
set nums [valcopy $numbers_or_commaformattednumbers] ;#stops single num from getting internal rep of list
foreach inputnum $nums {
set number [objclone $inputnum]
set number [valcopy $inputnum]
#also handle tcl 8.7+ underscores in numbers
set number [string map [list _ "" , ""] $number]
#normalize e.g 2e4 -> 20000.0
@ -4145,7 +4145,7 @@ namespace eval punk::lib {
# and possibly containing a decimal point, i.e.: [:space:]*[:digit:]*\.[:digit:]*
proc delimit_number {unformattednumber {delim ","} {GroupSize 3}} {
set number [objclone $unformattednumber]
set number [valcopy $unformattednumber]
set number [string map {_ ""} $number]
#normalize using expr - e.g 2e4 -> 20000.0
set number [expr {$number}]

8
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm

@ -34,7 +34,7 @@ namespace eval punk::mix::commandset::loadedlib {
"(unimplemented) Display only those that are 0:absent 1:present 2:either"
-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"
-refresh -type none -help "Re-scan the tm and library folders"
searchstring -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.
@ -45,11 +45,11 @@ namespace eval punk::mix::commandset::loadedlib {
}
proc search {args} {
set argd [punk::args::parse $args withid ::punk::mix::commandset::loadedlib::search]
set searchstrings [dict get $argd values searchstring]
set opts [dict get $argd opts]
lassign [dict values $argd] leaders opts values received
set searchstrings [dict get $values searchstring]
set opt_return [dict get $opts -return]
set opt_highlight [dict get $opts -highlight]
set opt_refresh [dict get $opts -refresh]
set opt_refresh [dict exists $received -refresh]
if {$opt_refresh} {
catch {package require frobznodule666} ;#ensure pkg system has loaded/searched for everything REVIEW - this doesn't result in full scans

2328
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/ns-0.1.0.tm

File diff suppressed because it is too large Load Diff

58
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/repl-0.1.2.tm

@ -3177,7 +3177,7 @@ namespace eval repl {
variable errstack {}
variable outstack {}
variable run_command_cache
proc set_clone {varname obj} {
proc set_valcopy {varname obj} {
append obj2 $obj {}
uplevel 1 [list set $varname $obj2]
}
@ -3241,6 +3241,7 @@ namespace eval repl {
#}
set pkgs [list\
punk::ansi::colourmap\
punk::assertion\
punk::args\
punk::pipe\
cmdline\
@ -3256,7 +3257,6 @@ namespace eval repl {
textutil\
punk::encmime\
punk::char\
punk::assertion\
punk::ansi\
punk::lib\
overtype\
@ -3290,37 +3290,41 @@ namespace eval repl {
set prior_infoscript [code eval {info script}] ;#probably empty that's ok
foreach pkg $pkgs {
#puts stderr "---> init_script safe pkg: $pkg"
if {[catch {
set nsquals [namespace qualifiers $pkg]
if {$nsquals ne ""} {
if {![dict exists $ns_scanned $nsquals]} {
catch {package require $pkg 1-0} ;#force scan with nonsatisfiable version
dict set ns_scanned $nsquals 1
#only load from source if not already loaded (perhaps already present from another package loading it)
set vloaded [code eval [list package provide $pkg]]
if {$vloaded eq ""} {
if {[catch {
set nsquals [namespace qualifiers $pkg]
if {$nsquals ne ""} {
if {![dict exists $ns_scanned $nsquals]} {
catch {package require $pkg 1-0} ;#force scan with nonsatisfiable version
dict set ns_scanned $nsquals 1
}
}
}
set versions [lsort -command {package vcompare} [package versions $pkg]]
if {[llength $versions]} {
set v [lindex $versions end]
set path [lindex [package ifneeded $pkg $v] end]
if {[file extension $path] in {.tcl .tm}} {
if {![catch {readFile $path} data]} {
code eval [list info script $path]
code eval $data
code eval [list info script $prior_infoscript]
set versions [lsort -command {package vcompare} [package versions $pkg]]
if {[llength $versions]} {
set v [lindex $versions end]
set path [lindex [package ifneeded $pkg $v] end]
if {[file extension $path] in {.tcl .tm}} {
if {![catch {readFile $path} data]} {
code eval [list info script $path]
code eval $data
code eval [list info script $prior_infoscript]
} else {
error "safe - failed to read $path"
}
} else {
error "safe - failed to read $path"
error "safe - refusing to attempt load of $pkg from $path - (allowed extensions .tcl .tm)"
}
} else {
error "safe - refusing to attempt load of $pkg from $path - (allowed extensions .tcl .tm)"
error "safe - no versions of $pkg found"
}
} errMsg]} {
puts stderr "safe - failed to load package $pkg\n$errMsg\n$::errorInfo"
} else {
error "safe - no versions of $pkg found"
#puts stdout "---> init_script safe - loaded $pkg from $path"
#puts stdout "---> v [code eval [list package provide $pkg]]"
}
} errMsg]} {
puts stderr "safe - failed to load package $pkg\n$errMsg\n$::errorInfo"
} else {
#puts stdout "---> init_script safe - loaded $pkg from $path"
#puts stdout "---> v [code eval [list package provide $pkg]]"
}
}
@ -3337,7 +3341,7 @@ namespace eval repl {
#review
code alias ::shellfilter::stack ::shellfilter::stack
#code alias ::punk::lib::set_clone ::punk::lib::set_clone
#code alias ::punk::lib::set_valcopy ::punk::lib::set_valcopy
#code alias ::aliases ::punk::ns::aliases
code alias ::punk::ns::aliases ::punk::ns::aliases
namespace eval ::codeinterp {}

4
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/repl/codethread-0.1.1.tm

@ -196,8 +196,8 @@ tcl::namespace::eval punk::repl::codethread {
#otherwise when $script goes out of scope - internal rep of vars set in script changes.
#The shimmering may be no big deal(?) - but debug/analysis using tcl::unsupported::representation becomes impossible.
#interp eval code [list ::punk::lib::set_clone ::codeinterp::clonescript $script] ;#like objclone
interp eval code [list ::codeinterp::set_clone ::codeinterp::clonescript $script] ;#like objclone
#interp eval code [list ::punk::lib::set_valcopy ::codeinterp::clonescript $script] ;#like valcopy
interp eval code [list ::codeinterp::set_valcopy ::codeinterp::clonescript $script] ;#like valcopy
interp eval code {lappend ::codeinterp::run_command_cache $::codeinterp::clonescript}

18
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/unixywindows-0.1.0.tm

@ -35,35 +35,35 @@ namespace eval punk::unixywindows {
if {![string length $cachedunixyroot]} {
if {![catch {
set result [exec cygpath -m /] ;# -m gives result with forward-slashes - which is ok for windows paths in a Tcl context.
set cachedunixyroot [punk::objclone $result]
set cachedunixyroot [punk::valcopy $result]
file pathtype $cachedunixyroot; #this call causes the int-rep to be path
#set ::punk::last_run_display [list] ;#hack - review shouldn't really be necessary.. but because we call winpath from ./ - the repl looks for last_run_display
} errM]} {
} else {
puts stderr "Warning: Failed to determine base for unix-like paths - using default of c:/msys2"
file pathtype [set cachedunixyroot [punk::objclone "c:/msys2"]]
file pathtype [set cachedunixyroot [punk::valcopy "c:/msys2"]]
}
}
#will have been shimmered from string to 'path' internal rep by 'file pathtype' call
#let's return a different copy as it's so easy to lose path-rep
set copy [punk::objclone $cachedunixyroot]
set copy [punk::valcopy $cachedunixyroot]
return $copy
}
proc refresh_unixyroot {} {
variable cachedunixyroot
set result [exec cygpath -m /] ;# -m gives result with forward-slashes - which is ok for windows paths in a Tcl context.
set cachedunixyroot [punk::objclone $result]
set cachedunixyroot [punk::valcopy $result]
file pathtype $cachedunixyroot; #this call causes the int-rep to be path
set copy [punk::objclone $cachedunixyroot]
set copy [punk::valcopy $cachedunixyroot]
return $copy
}
proc set_unixyroot {windows_path} {
variable cachedunixyroot
file pathtype $windows_path
set cachedunixyroot [punk::objclone $windows_path]
set cachedunixyroot [punk::valcopy $windows_path]
#return the original - but probably int-rep will have shimmered to path even if started out as string
#- that's probably what we want. Caller can use as string or path without affecting cachedunixyroot
return $windows_path
@ -131,13 +131,13 @@ namespace eval punk::unixywindows {
}
#puts stderr "->$driveletters"
set path [punk::objclone $unixypath] ;#take another copy that we can deliberatley shimmer to path and know is separate to the supplied argument
set path [punk::valcopy $unixypath] ;#take another copy that we can deliberatley shimmer to path and know is separate to the supplied argument
set supplied_pathtype [file pathtype $path] ;#we don't care about the pathtype so much as the act of making this call shimmers to a path internal-rep
#copy of var that we can treat as a string without affecting path rep
#Note that some but not all read operations will lose path rep e.g 'string length $x' will lose any path-rep $x had, (but 'string equal $x something' won't)
#Todo - make int-rep tests to check stability of these behaviours across Tcl versions!
set strcopy_path [punk::objclone $path]
set strcopy_path [punk::valcopy $path]
set str_newpath ""
@ -174,7 +174,7 @@ namespace eval punk::unixywindows {
#dunno - pass through
set pathobj $path
} else {
set pathobj [punk::objclone $str_newpath]
set pathobj [punk::valcopy $str_newpath]
file pathtype $pathobj
}
}

12
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/winpath-0.1.0.tm

@ -30,7 +30,7 @@ namespace eval punk::winpath {
#\\servername\share etc or \\?\UNC\servername\share etc.
proc is_unc_path {path} {
set strcopy_path [punk::winpath::system::objclone $path]
set strcopy_path [punk::winpath::system::valcopy $path]
set strcopy_path [string map {\\ /} $strcopy_path] ;#normalize to forward slashes for testing purposes (and forward slashes seem to be auto-translated by windows anyway)
if {[string first "//" $strcopy_path] == 0} {
#check for "Dos device path" syntax
@ -77,7 +77,7 @@ namespace eval punk::winpath {
#dos device path syntax allows windows api to acces extended-length paths and filenames with illegal path characters such as trailing dots or whitespace
#(can exist on server shares and on NTFS - but standard apps can't access without dos device syntax)
proc is_dos_device_path {path} {
set strcopy_path [punk::winpath::system::objclone $path]
set strcopy_path [punk::winpath::system::valcopy $path]
set strcopy_path [string map {\\ /} $strcopy_path] ;#normalize to forward slashes for testing purposes (and forward slashes seem to be auto-translated by windows anyway)
if {[string range $strcopy_path 0 3] in {//?/ //./}} {
return 1
@ -100,13 +100,13 @@ namespace eval punk::winpath {
proc strip_unc_path_prefix {path} {
if {[is_unc_path_plain $path]} {
#plain unc //server
set strcopy_path [punk::winpath::system::objclone $path]
set strcopy_path [punk::winpath::system::valcopy $path]
set trimmedpath [string range $strcopy_path 2 end]
file pathtype $trimmedpath
return $trimmedpath
} elseif {is_unc_path $path} {
#//?/UNC/server/subpath or //./UNC/server/subpath
set strcopy_path [punk::winpath::system::objclone $path]
set strcopy_path [punk::winpath::system::valcopy $path]
set trimmedpath [string range $strcopy_path 7 end]
file pathtype $trimmedpath ;#shimmer it to path rep
return $trimmedpath
@ -153,7 +153,7 @@ namespace eval punk::winpath {
error $err
}
set strcopy_path [punk::winpath::system::objclone $path]
set strcopy_path [punk::winpath::system::valcopy $path]
#Note: path could still have leading double slash if it is a Dos device path: e.g. //?/c:/etc
@ -339,7 +339,7 @@ namespace eval punk::winpath {
namespace eval punk::winpath::system {
#get a copy of the item without affecting internal rep
proc objclone {obj} {
proc valcopy {obj} {
append obj2 $obj {}
}
}

746
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk-0.1.tm

@ -496,23 +496,25 @@ namespace eval punk {
#-----------------------------------------------------------------------------------
#get a copy of the item without affecting internal rep
proc objclone {obj} {
proc valcopy {obj} {
append obj2 $obj {}
}
proc set_clone {varname obj} {
#maintenance: also punk::lib::set_clone
#e.g used by repl's codeinterp. Maintains internal rep, easier to call e.g interp eval code [list punk::set_clone varnmame $val]
proc set_valcopy {varname obj} {
#maintenance: also punk::lib::set_valcopy
#e.g used by repl's codeinterp. Maintains internal rep, easier to call e.g interp eval code [list punk::set_valcopy varnmame $val]
append obj2 $obj {}
uplevel 1 [list set $varname $obj2]
}
interp alias "" strlen "" ::punk::strlen
interp alias "" str_len "" ::punk::strlen
interp alias "" objclone "" ::punk::objclone
interp alias "" valcopy "" ::punk::valcopy
#proc ::strlen {str} {
# string length [append str2 $str {}]
#}
#proc ::objclone {obj} {
#proc ::valcopy {obj} {
# append obj2 $obj {}
#}
@ -629,10 +631,24 @@ namespace eval punk {
-- -type none
@values
pattern -type string -help\
"regex pattern to match in plaintext portion of ANSI string
{regex pattern to match in plaintext portion of ANSI string
The pattern may contain bracketed capturing groups, which
will be highlighted (todo) in the result. If there is no capturing
group, the entire match will be highlighted."
will be highlighted in the result. If there is no capturing
group, the entire match will be highlighted.
Note that if we were to attempt to highlight curly braces based
on the regexp {\{|\}} then the inserted ansi would come between
the backslash and brace in cases where a curly brace is escaped
ie \{ or \}
Depending on how the output is used, this can break the syntactic
structure causing problems.
Instead a pair of regexes such as
{^\{|[^\\](\{+)}
{[^\\](\}+)}
should be used to
exclude braces that are escaped.
(note the capturing groups around each curly brace)
}
string -type string
}
proc grepstr {args} {
@ -706,9 +722,12 @@ namespace eval punk {
}
if {$lineindex in $matched_line_indices} {
set plain_ln [lindex $plainlines $lineindex]
#first test the regexp with a single match to determine number of capturing groups
set matchparts [regexp {*}$nocase -inline -- $pattern $plain_ln] ;#get fullmatch capturegroup...
set numgroups [expr {[llength $matchparts] -1}]
#first - determine the number of capturing groups (subexpressions)
#option 1: test the regexp with a single match
#set testparts [regexp {*}$nocase -inline -- $pattern $plain_ln] ;#get fullmatch capturegroup...
#set numgroups [expr {[llength $testparts] -1}]
#option 2: use the regexp -about flag
set numgroups [lindex [regexp -about $pattern] 0]
set allparts [regexp -all {*}$nocase -indices -inline -- $pattern $plain_ln]
#allparts includes each full match as well as each capturing group
@ -730,9 +749,14 @@ namespace eval punk {
# restrict ourself to just the capture groups, excluding the full match (if there are capture groups)
set highlight_ranges [list]
set i 0
#{-1 -1} returned for non-matching group when there are capture-group alternatives
#e.g {(a)|(b)}
foreach range $allparts {
if {($i % ($numgroups+1)) != 0} {
lappend highlight_ranges $range
lassign $range a b
if {$range ne {-1 -1} & $a <= $b} {
lappend highlight_ranges $range
}
}
incr i
}
@ -917,10 +941,8 @@ namespace eval punk {
return [twapi::new_uuid]
}
}
#get last command result that was run through the repl
proc ::punk::get_runchunk {args} {
set argd [punk::args::parse $args withdef {
namespace eval argdoc {
punk::args::define {
@id -id ::punk::get_runchunk
@cmd -name "punk::get_runchunk" -help\
"experimental"
@ -928,7 +950,19 @@ namespace eval punk {
-1 -optional 1 -type none
-2 -optional 1 -type none
@values -min 0 -max 0
}]
}
}
#get last command result that was run through the repl
proc ::punk::get_runchunk {args} {
#set argd [punk::args::parse $args withdef {
# @id -id ::punk::get_runchunk
# @cmd -name "punk::get_runchunk" -help\
# "experimental"
# @opts
# -1 -optional 1 -type none
# -2 -optional 1 -type none
# @values -min 0 -max 0
#}]
#todo - make this command run without truncating previous runchunks
set runchunks [tsv::array names repl runchunks-*]
@ -4148,7 +4182,7 @@ namespace eval punk {
#pipealias should capture the namespace context of the pipeline so that commands are resolved in the namespace in which the pipealias is created
proc pipealias {targetcmd args} {
set cmdcopy [punk::objclone $args]
set cmdcopy [punk::valcopy $args]
set nscaller [uplevel 1 [list namespace current]]
tailcall interp alias {} $targetcmd {} apply [list args [append cmdcopy " {*}\$args"] $nscaller]
}
@ -4159,7 +4193,7 @@ namespace eval punk {
}
#although the pipealias2 'concat' alias is cleaner in that the original pipeline can be extracted using list commands - it runs much slower
proc pipealias2 {targetcmd args} {
set cmdcopy [punk::objclone $args]
set cmdcopy [punk::valcopy $args]
set nscaller [uplevel 1 [list namespace current]]
tailcall interp alias {} $targetcmd {} apply [list args [concat "\[concat" [list $cmdcopy] "\$args]"] $nscaller]
}
@ -6654,15 +6688,16 @@ namespace eval punk {
#interp alias {} ~ {} file join $::env(HOME) ;#HOME must be capitalized to work cross platform (lowercase home works on windows - but probably not elsewhere)
#interp alias {} ~ {} apply {args {file join $::env(HOME) $args}}
proc ~ {args} {
set hdir [punk::objclone $::env(HOME)]
set hdir [punk::valcopy $::env(HOME)]
file pathtype $hdir
set d $hdir
#use the file join 2-arg optimisation to avoid losing path-rep - probably doesn't give any advantage on all Tcl versions
#review - for what versions does/did the 2-arg version of file join not just return a string?
foreach a $args {
set d [file join $d $a]
}
file pathtype $d
return [punk::objclone $d]
return [punk::valcopy $d]
}
interp alias {} ~ {} punk::~
@ -7789,47 +7824,61 @@ namespace eval punk {
namespace eval argdoc {
punk::args::define {
@id -id ::punk::help_chunks
@cmd -name "punk::help_chunks"\
-summary\
""\
-help\
""
@opts
-- -type none
@values -min 0 -max -1
arg -type any -optional 1 -multiple 1
}
}
proc help {args} {
set chunks [uplevel 1 [list ::punk::help_chunks {*}$args]]
foreach chunk $chunks {
lassign $chunk chan text
puts -nonewline $chan $text
}
}
#return list of {chan chunk} elements
proc help_chunks {args} {
set chunks [list]
set linesep [string repeat - 76]
set mascotblock ""
catch {
package require patternpunk
#lappend chunks [list stderr [>punk . rhs]]
append mascotblock [textblock::frame -checkargs 0 [>punk . banner -title "Punk Shell" -left Tcl -right [package provide Tcl]]]
set argd [punk::args::parse $args withid ::punk::help_chunks]
lassign [dict values $argd] leaders opts values received
if {[dict exists $values arg]} {
set topicparts [dict get $values arg]
} else {
set topicparts [list ""]
}
#set topic [lindex $args end]
#set argopts [lrange $args 0 end-1]
set topic [lindex $args end]
set argopts [lrange $args 0 end-1]
set chunks [list]
set linesep [string repeat - 76]
set warningblock ""
set title "[a+ brightgreen] Punk core navigation commands: "
set I [punk::ansi::a+ italic]
set NI [punk::ansi::a+ noitalic]
#todo - load from source code annotation?
# -------------------------------------------------------
set logoblock ""
if {[catch {
package require patternpunk
#lappend chunks [list stderr [>punk . rhs]]
append logoblock [textblock::frame -title "Punk Shell [package provide punk]" -width 29 -checkargs 0 [>punk . banner -title "" -left Tcl -right [package provide Tcl]]]
}]} {
append logoblock [textblock::frame -title "Punk Shell [package provide punk]" -subtitle "TCL [package provide Tcl]" -width 29 -height 10 -checkargs 0 ""]
}
set title "[a+ brightgreen] Help System: "
set cmdinfo [list]
lappend cmdinfo [list help "?topics?" "This help. To see available subitems type: help topics"]
lappend cmdinfo [list i "cmd ?subcommand...?" "Show usage for a command or ensemble subcommand"]
lappend cmdinfo [list s "cmd ?subcommand...?" "Show synopsis for a command or ensemble subcommand"]
lappend cmdinfo [list eg "cmd ?subcommand...?" "Show example from manpage"]
lappend cmdinfo [list ./ "?subdir?" "view/change directory"]
lappend cmdinfo [list ../ "" "go up one directory"]
lappend cmdinfo [list ./new "subdir" "make new directory and switch to it"]
lappend cmdinfo [list n/ "?glob...?" "view/change namespace\n (accepts ns path globs e.g **::*get* to match comands at any level )"]
lappend cmdinfo [list n// "" "view/change namespace (with command listing)"]
lappend cmdinfo [list "nn/" "" "go up one namespace"]
lappend cmdinfo [list "n/new" "<ns>" "make child namespace and switch to it"]
lappend cmdinfo [list dev "?subcommand?" "(ensemble command to make new projects/modules and to generate docs)"]
lappend cmdinfo [list a? "?subcommand...?" "view ANSI colours\n e.g a? web"]
#set cmds [lsearch -all -inline -index 0 -subindices $cmdinfo *]
#set descr [lsearch -all -inline -index 1 -subindices $cmdinfo *]
#set widest1 [tcl::mathfunc::max {*}[lmap v $cmds {string length $v}]]
#set widest2 [tcl::mathfunc::max {*}[lmap v $descr {string length $v}]]
set t [textblock::class::table new -show_seps 0]
#foreach c $cmds d $descr {
# $t add_row [list $c $d]
#}
lappend cmdinfo [list help "?${I}topic${NI}?" "This help.\nTo see available subitems type:\nhelp topics\n\n\n\n\n"]
set t [textblock::class::table new -minwidth 51 -show_seps 0]
foreach row $cmdinfo {
$t add_row $row
}
@ -7838,272 +7887,387 @@ namespace eval punk {
set width_1 [$t column_datawidth 1]
$t configure_column 1 -minwidth [expr {$width_1 + 1}]
$t configure -title $title
set text ""
append text [$t print]
set warningblock ""
set introblock $mascotblock
append introblock \n $text
#if {[catch {package require textblock} errM]} {
# append warningblock \n "WARNING: textblock package couldn't be loaded. Side-by-side display not available"
#} else {
# set introblock [textblock::join -- " " \n$mascotblock " " $text]
#}
lappend chunks [list stdout $introblock]
if {$topic in [list tcl]} {
if {[punk::lib::check::has_tclbug_script_var]} {
append warningblock \n "minor warning: punk::lib::check::has_tclbug_script_var returned true! (string rep for list variable in script generated when script changed)"
}
if {[punk::lib::check::has_tclbug_safeinterp_compile]} {
set indent " "
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_safeinterp returned true!" \n
append warningblock "${indent}(ensemble commands not compiled in safe interps - heavy performance impact in safe interps)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/1095bf7f75]"
append warningblock [a]
}
if {[catch {lsearch -stride 2 {a b} b}]} {
set indent " "
append warningblock \n "[a+ web-red]warning: lsearch does not seem to support -stride option" \n
append warningblock "${indent}(Consider upgrading to a late release of tcl 8.6 or tcl 9+ )" \n
append warningblock [a]
} else {
if {[punk::lib::check::has_tclbug_lsearch_strideallinline]} {
set text [$t print]
set introblock [textblock::join -- $logoblock $text]
lappend chunks [list stdout $introblock\n]
# -------------------------------------------------------
switch -- [lindex $topicparts 0] {
"" {
# -------------------------------------------------------
set title "[a+ brightgreen] Filesystem navigation: "
set cmdinfo [list]
lappend cmdinfo [list ./ "?${I}subdir${NI}?" "view/change directory"]
lappend cmdinfo [list ../ "" "go up one directory"]
lappend cmdinfo [list ./new "${I}subdir${NI}" "make new directory and switch to it"]
set t [textblock::class::table new -minwidth 80 -show_seps 0]
foreach row $cmdinfo {
$t add_row $row
}
set width_0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$width_0 + 2}]
set width_1 [$t column_datawidth 1]
$t configure_column 1 -minwidth [expr {$width_1 + 1}]
$t configure -title $title
set text ""
append text [$t print]
lappend chunks [list stdout $text\n]
# -------------------------------------------------------
# -------------------------------------------------------
set title "[a+ brightgreen] Namespace navigation: "
set cmdinfo [list]
lappend cmdinfo [list n/ "?${I}ns${NI}|${I}glob${NI}?" "view/change namespace\n (accepts ns path globs e.g **::*get* to match\n commands at any level )"]
lappend cmdinfo [list n// "?${I}ns${NI}|${I}glob${NI}?" "view/change namespace (with command listing)"]
lappend cmdinfo [list "nn/" "" "go up one namespace"]
lappend cmdinfo [list "n/new" "${I}ns${NI}" "make child namespace and switch to it"]
set t [textblock::class::table new -minwidth 80 -show_seps 0]
foreach row $cmdinfo {
$t add_row $row
}
set width_0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$width_0 + 2}]
set width_1 [$t column_datawidth 1]
$t configure_column 1 -minwidth [expr {$width_1 + 1}]
$t configure -title $title
set text ""
append text [$t print]
lappend chunks [list stdout $text\n]
# -------------------------------------------------------
# -------------------------------------------------------
set title "[a+ brightgreen] Command help: "
set cmdinfo [list]
lappend cmdinfo [list i "${I}cmd${NI} ?${I}subcommand${NI}...?" "Show usage for a command or ensemble subcommand"]
lappend cmdinfo [list s "${I}cmd${NI} ?${I}subcommand${NI}...?" "Show synopsis for a command or ensemble subcommand"]
lappend cmdinfo [list eg "${I}cmd${NI} ?${I}subcommand${NI}...?" "Show example from manpage"]
lappend cmdinfo [list corp "${I}proc${NI}" "View proc body and arguments"]
set t [textblock::class::table new -minwidth 80 -show_seps 0]
foreach row $cmdinfo {
$t add_row $row
}
set width_0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$width_0 + 2}]
set width_1 [$t column_datawidth 1]
$t configure_column 1 -minwidth [expr {$width_1 + 1}]
$t configure -title $title
set text ""
append text [$t print]
lappend chunks [list stdout $text\n]
# -------------------------------------------------------
set title "[a+ brightgreen] Miscellaneous: "
#todo - load from source code annotation?
set cmdinfo [list]
lappend cmdinfo [list dev "?${I}subcommand${NI}?" "(ensemble command to make new projects/modules and\n to generate docs)"]
lappend cmdinfo [list a? "?${I}subcommand${NI}...?" "view ANSI colours\n e.g a? web\n or individual code samples/diagnostics\n e.g a? red bold\n e.g a? underline web-orange Term-purple3"]
lappend cmdinfo [list a+ "?${I}colourcode${NI}...?" "Return ANSI codes\n e.g puts \"\[a+ purple\]purple\[a+ Green\]purple on green\[a\]\"\n [a+ purple]purple[a+ Green]purple on green[a] "]
lappend cmdinfo [list a "?${I}colourcode${NI}...?" "Return ANSI codes (with leading reset)\n e.g puts \"\[a+ purple\]purple\[a Green\]normal on green\[a\]\"\n [a+ purple]purple[a Green]normal on green[a] "]
set t [textblock::class::table new -minwidth 80 -show_seps 0]
foreach row $cmdinfo {
$t add_row $row
}
set width_0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$width_0 + 2}]
set width_1 [$t column_datawidth 1]
$t configure_column 1 -minwidth [expr {$width_1 + 1}]
$t configure -title $title
set text ""
append text [$t print]
lappend chunks [list stdout $text]
# -------------------------------------------------------
}
tcl {
set text "Tcl Patchlevel: [info patchlevel]"
catch {
append text \n "Tcl build-info: [::tcl::build-info]"
}
if {[punk::lib::check::has_tclbug_script_var]} {
append warningblock \n "minor warning: punk::lib::check::has_tclbug_script_var returned true! (string rep for list variable in script generated when script changed)"
}
if {[punk::lib::check::has_tclbug_safeinterp_compile]} {
set indent " "
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_lsearch_strideallinline returned true!" \n
append warningblock "${indent}(lsearch using -stride -all -inline -subindices does not return values corresponding to subindex when a single -index value is used)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/5a1aaa201d]"
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_safeinterp returned true!" \n
append warningblock "${indent}(ensemble commands not compiled in safe interps - heavy performance impact in safe interps)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/1095bf7f75]"
append warningblock [a]
}
}
if {[punk::lib::check::has_tclbug_list_quoting_emptyjoin]} {
set indent " "
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_list_quoting returned true!" \n
append warningblock "${indent}lists elements not properly quoted in some cases. e.g 'list {*}[lindex {etc #foo} 1] {*}[list]' (#foo not braced)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/e38dce74e2]"
}
}
set text ""
if {$topic in [list env environment]} {
#todo - move to punk::config?
upvar ::punk::config::punk_env_vars_config punkenv_config
upvar ::punk::config::other_env_vars_config otherenv_config
set known_punk [dict keys $punkenv_config]
set known_other [dict keys $otherenv_config]
append text \n
set usetable 1
if {$usetable} {
set t [textblock::class::table new -show_hseps 0 -show_header 1 -ansiborder_header [a+ web-green]]
if {"windows" eq $::tcl_platform(platform)} {
#If any env vars have been set to empty string - this is considered a deletion of the variable on windows.
#The Tcl ::env array is linked to the underlying process view of the environment
#- but info exists ::env(var) can misreport as true if it has been deleted by setting to empty string rather than using unset.
#an 'array get' will resynchronise.
#Even if an env variable didn't exist before - setting it to empty string can get it to this inconsistent state.
array get ::env
}
#do an array read on ::env
foreach {v vinfo} $punkenv_config {
if {[info exists ::env($v)]} {
set c2 [set ::env($v)]
} else {
set c2 "(NOT SET)"
}
set help ""
if {[dict exists $vinfo help]} {
set help [dict get $vinfo help]
if {[catch {lsearch -stride 2 {a b} b}]} {
set indent " "
append warningblock \n "[a+ web-red]warning: lsearch does not seem to support -stride option" \n
append warningblock "${indent}(Consider upgrading to a late release of tcl 8.6 or tcl 9+ )" \n
append warningblock [a]
} else {
if {[punk::lib::check::has_tclbug_lsearch_strideallinline]} {
set indent " "
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_lsearch_strideallinline returned true!" \n
append warningblock "${indent}(lsearch using -stride -all -inline -subindices does not return values corresponding to subindex when a single -index value is used)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/5a1aaa201d]"
append warningblock [a]
}
$t add_row [list $v $c2 $help]
}
$t configure_column 0 -headers [list "Punk environment vars"]
$t configure_column 0 -minwidth [expr {[$t column_datawidth 0]+4}] -blockalign left -textalign left -header_colspans {any}
if {[punk::lib::check::has_tclbug_list_quoting_emptyjoin]} {
set indent " "
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_list_quoting returned true!" \n
append warningblock "${indent}lists elements not properly quoted in some cases. e.g 'list {*}[lindex {etc #foo} 1] {*}[list]' (#foo not braced)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/e38dce74e2]"
}
lappend chunks [list stdout $text]
}
env - environment {
set text ""
#todo - move to punk::config?
upvar ::punk::config::punk_env_vars_config punkenv_config
upvar ::punk::config::other_env_vars_config otherenv_config
set known_punk [dict keys $punkenv_config]
set known_other [dict keys $otherenv_config]
append text \n
set usetable 1
if {$usetable} {
set t [textblock::class::table new -show_hseps 0 -show_header 1 -ansiborder_header [a+ web-green]]
if {"windows" eq $::tcl_platform(platform)} {
#If any env vars have been set to empty string - this is considered a deletion of the variable on windows.
#The Tcl ::env array is linked to the underlying process view of the environment
#- but info exists ::env(var) can misreport as true if it has been deleted by setting to empty string rather than using unset.
#an 'array get' will resynchronise.
#Even if an env variable didn't exist before - setting it to empty string can get it to this inconsistent state.
array get ::env
}
#do an array read on ::env
foreach {v vinfo} $punkenv_config {
if {[info exists ::env($v)]} {
set c2 [set ::env($v)]
} else {
set c2 "(NOT SET)"
}
set help ""
if {[dict exists $vinfo help]} {
set help [dict get $vinfo help]
}
$t add_row [list $v $c2 $help]
}
$t configure_column 0 -headers [list "Punk environment vars"]
$t configure_column 0 -minwidth [expr {[$t column_datawidth 0]+4}] -blockalign left -textalign left -header_colspans {any}
set punktable [$t print]
$t destroy
set punktable [$t print]
$t destroy
set t [textblock::class::table new -show_hseps 0 -show_header 1 -ansiborder_header [a+ web-green]]
foreach {v vinfo} $otherenv_config {
if {[info exists ::env($v)]} {
set env_val [set ::env($v)]
if {[string match "*_TM_PATH" $v]} {
set entries [split $env_val $::tcl_platform(pathSeparator)]
set c2 [join $entries \n]
set t [textblock::class::table new -show_hseps 0 -show_header 1 -ansiborder_header [a+ web-green]]
foreach {v vinfo} $otherenv_config {
if {[info exists ::env($v)]} {
set env_val [set ::env($v)]
if {[string match "*_TM_PATH" $v]} {
set entries [split $env_val $::tcl_platform(pathSeparator)]
set c2 [join $entries \n]
} else {
set c2 $::env($v)
}
} else {
set c2 $::env($v)
set c2 "(NOT SET)"
}
} else {
set c2 "(NOT SET)"
$t add_row [list $v $c2]
}
$t add_row [list $v $c2]
}
$t configure_column 0 -headers [list "Other environment vars"]
$t configure_column 0 -minwidth [expr {[$t column_datawidth 0]+4}] -blockalign left -textalign left -header_colspans {any}
$t configure_column 0 -headers [list "Other environment vars"]
$t configure_column 0 -minwidth [expr {[$t column_datawidth 0]+4}] -blockalign left -textalign left -header_colspans {any}
set othertable [$t print]
$t destroy
#append text [textblock::join -- $punktable " " $othertable]\n
append text $punktable\n$othertable\n
} else {
set othertable [$t print]
$t destroy
#append text [textblock::join -- $punktable " " $othertable]\n
append text $punktable\n$othertable\n
} else {
append text $linesep\n
append text "punk environment vars:\n"
append text $linesep\n
set col1 [string repeat " " 25]
set col2 [string repeat " " 50]
foreach v $known_punk {
set c1 [overtype::left $col1 $v]
if {[info exists ::env($v)]} {
set c2 [overtype::left $col2 [set ::env($v)]]
} else {
set c2 [overtype::right $col2 "(NOT SET)"]
}
append text "$c1 $c2\n"
}
append text $linesep\n
}
lappend chunks [list stdout $text]
}
if {$topic in [list console terminal]} {
set indent [string repeat " " [string length "WARNING: "]]
lappend cstring_tests [dict create\
type "PM "\
msg "UN"\
f7 punk::ansi::controlstring_PM\
f7prefix "7bit ESC ^ secret "\
f7suffix "safe"\
f8 punk::ansi::controlstring_PM8\
f8prefix "8bit \\x9e secret "\
f8suffix "safe"\
]
lappend cstring_tests [dict create\
type SOS\
msg "NOT"\
f7 punk::ansi::controlstring_SOS\
f7prefix "7bit ESC X string "\
f7suffix " hidden"\
f8 punk::ansi::controlstring_SOS8\
f8prefix "8bit \\x98 string "\
f8suffix " hidden"\
]
lappend cstring_tests [dict create\
type APC\
msg "NOT"\
f7 punk::ansi::controlstring_APC\
f7prefix "7bit ESC _ APPLICATION PROGRAM COMMAND "\
f7suffix " hidden"\
f8 punk::ansi::controlstring_APC8\
f8prefix "8bit \\x9f APPLICATION PROGRAM COMMAND "\
f8suffix " hidden"\
]
foreach test $cstring_tests {
set m [[dict get $test f7] [dict get $test msg]]
set hidden_width_m [punk::console::test_char_width $m]
set m8 [[dict get $test f8] [dict get $test msg]]
set hidden_width_m8 [punk::console::test_char_width $m8]
if {$hidden_width_m != 0 || $hidden_width_m8 != 0} {
if {$hidden_width_m == 0} {
set d "[a+ green bold][dict get $test f7prefix][a red]${m}[a][a+ green bold][dict get $test f7suffix][a]"
} else {
set d "[a+ yellow bold][dict get $test f7prefix][a red]$m[a][a+ yellow bold][dict get $test f7suffix][a]"
append text $linesep\n
append text "punk environment vars:\n"
append text $linesep\n
set col1 [string repeat " " 25]
set col2 [string repeat " " 50]
foreach v $known_punk {
set c1 [overtype::left $col1 $v]
if {[info exists ::env($v)]} {
set c2 [overtype::left $col2 [set ::env($v)]]
} else {
set c2 [overtype::right $col2 "(NOT SET)"]
}
append text "$c1 $c2\n"
}
append text $linesep\n
}
lappend chunks [list stdout $text]
}
console - term - terminal {
set indent [string repeat " " [string length "WARNING: "]]
lappend cstring_tests [dict create\
type "PM "\
msg "UN"\
f7 punk::ansi::controlstring_PM\
f7prefix "7bit ESC ^ secret "\
f7suffix "safe"\
f8 punk::ansi::controlstring_PM8\
f8prefix "8bit \\x9e secret "\
f8suffix "safe"\
]
lappend cstring_tests [dict create\
type SOS\
msg "NOT"\
f7 punk::ansi::controlstring_SOS\
f7prefix "7bit ESC X string "\
f7suffix " hidden"\
f8 punk::ansi::controlstring_SOS8\
f8prefix "8bit \\x98 string "\
f8suffix " hidden"\
]
lappend cstring_tests [dict create\
type APC\
msg "NOT"\
f7 punk::ansi::controlstring_APC\
f7prefix "7bit ESC _ APPLICATION PROGRAM COMMAND "\
f7suffix " hidden"\
f8 punk::ansi::controlstring_APC8\
f8prefix "8bit \\x9f APPLICATION PROGRAM COMMAND "\
f8suffix " hidden"\
]
foreach test $cstring_tests {
set m [[dict get $test f7] [dict get $test msg]]
set hidden_width_m [punk::console::test_char_width $m]
set m8 [[dict get $test f8] [dict get $test msg]]
set hidden_width_m8 [punk::console::test_char_width $m8]
if {$hidden_width_m != 0 || $hidden_width_m8 != 0} {
if {$hidden_width_m == 0} {
set d "[a+ green bold][dict get $test f7prefix][a red]${m}[a][a+ green bold][dict get $test f7suffix][a]"
} else {
set d "[a+ yellow bold][dict get $test f7prefix][a red]$m[a][a+ yellow bold][dict get $test f7suffix][a]"
}
if {$hidden_width_m8 == 0} {
set d8 "[a+ green ][dict get $test f8prefix][a red]$m8[a][a+ green][dict get $test f8suffix][a]"
} else {
set d8 "[a+ yellow bold][dict get $test f8prefix][a red]$m8[a][a+ yellow bold][dict get $test f8suffix][a]"
}
append warningblock \n "WARNING: terminal doesn't hide all [dict get $test type] control strings: $d $d8"
}
if {$hidden_width_m8 == 0} {
set d8 "[a+ green ][dict get $test f8prefix][a red]$m8[a][a+ green][dict get $test f8suffix][a]"
} else {
set d8 "[a+ yellow bold][dict get $test f8prefix][a red]$m8[a][a+ yellow bold][dict get $test f8suffix][a]"
}
if {![catch {punk::console::check::has_bug_legacysymbolwidth} result]} {
if {$result} {
append warningblock \n "WARNING: terminal has legacysymbolwidth bug - screen position for symbol reports 2 wide but displays 1 wide."
append warningblock \n $indent "Layout using 'legacy symbols for computing' affected."
append warningblock \n $indent "(e.g textblock frametype block2 unsupported)"
append warningblock \n $indent "This can cause extreme layout deformation when ANSI is present"
append warningblock \n $indent "In some cases unwanted spacing effects occur at a distance from the characters causing it"
}
append warningblock \n "WARNING: terminal doesn't hide all [dict get $test type] control strings: $d $d8"
} else {
append warningblock \n "WARNING: terminal unable to check for legacysymbolwidth bug. err:$result"
}
}
if {![catch {punk::console::check::has_bug_legacysymbolwidth} result]} {
if {$result} {
append warningblock \n "WARNING: terminal has legacysymbolwidth bug - screen position for symbol reports 2 wide but displays 1 wide."
append warningblock \n $indent "Layout using 'legacy symbols for computing' affected."
append warningblock \n $indent "(e.g textblock frametype block2 unsupported)"
append warningblock \n $indent "This can cause extreme layout deformation when ANSI is present"
append warningblock \n $indent "In some cases unwanted spacing effects occur at a distance from the characters causing it"
if {![catch {punk::console::check::has_bug_zwsp} result]} {
if {$result} {
append warningblock \n "WARNING: terminal has zero width space (\\u200b) bug - cursor position incremented when it shouldn't be."
append warningblock \n $indent "The zwsp may or may not be displayed. zwsp contributes to line length and wrapping point"
}
} else {
append warningblock \n "WARNING: terminal unable to check for zwsp bug. err:$result"
}
} else {
append warningblock \n "WARNING: terminal unable to check for legacysymbolwidth bug. err:$result"
}
if {![catch {punk::console::check::has_bug_zwsp} result]} {
if {$result} {
append warningblock \n "WARNING: terminal has zero width space (\\u200b) bug - cursor position incremented when it shouldn't be."
append warningblock \n $indent "The zwsp may or may not be displayed. zwsp contributes to line length and wrapping point"
set grapheme_support [punk::console::grapheme_cluster_support]
#mode, 1 = set, 2 = unset. (0 = mode not recognised, 3 = permanently set, 4 = permanently unset)
if {![dict size $grapheme_support] || [dict get $grapheme_support mode] eq "unsupported" } {
append warningblock \n "WARNING: terminal either doesn't support grapheme clusters, or doesn't report so via decmode 2027 query."
if {[dict size $grapheme_support] && [dict get $grapheme_support available]} {
append warningblock \n $indent "(but punk::console::grapheme_cluster_support has determined it is probably available)"
}
} else {
if {![dict get $grapheme_support available]} {
switch -- [dict get $grapheme_support mode] {
"unset" {
append warningblock \n "WARNING: terminal reports via decmode 2027 that grapheme cluster support is off."
}
"permanently_unset" {
append warningblock \n "WARNING: terminal reports via decmode 2027 that grapheme cluster support is permanently off."
}
"BAD_RESPONSE" {
append warningblock \n "WARNING: terminal doesn't seem to recognize decmode 2027 query. No grapheme cluster support."
}
}
}
}
if {![string length $warningblock]} {
set text "No terminal warnings\n"
lappend chunks [list stdout $text]
}
} else {
append warningblock \n "WARNING: terminal unable to check for zwsp bug. err:$result"
}
topics - help {
set text ""
set topics [dict create\
"topics|help" "List help topics"\
"tcl" "Tcl version warnings"\
"env|environment" "punkshell environment vars"\
"console|terminal" "Some console behaviour tests and warnings"\
]
set grapheme_support [punk::console::grapheme_cluster_support]
#mode, 1 = set, 2 = unset. (0 = mode not recognised, 3 = permanently set, 4 = permanently unset)
if {![dict size $grapheme_support] || [dict get $grapheme_support mode] eq "unsupported" } {
append warningblock \n "WARNING: terminal either doesn't support grapheme clusters, or doesn't report so via decmode 2027 query."
if {[dict size $grapheme_support] && [dict get $grapheme_support available]} {
append warningblock \n $indent "(but punk::console::grapheme_cluster_support has determined it is probably available)"
set t [textblock::class::table new -show_seps 0]
$t add_column -headers [list "Topic"]
$t add_column
foreach {k v} $topics {
$t add_row [list $k $v]
}
} else {
if {![dict get $grapheme_support available]} {
switch -- [dict get $grapheme_support mode] {
"unset" {
append warningblock \n "WARNING: terminal reports via decmode 2027 that grapheme cluster support is off."
}
"permanently_unset" {
append warningblock \n "WARNING: terminal reports via decmode 2027 that grapheme cluster support is permanently off."
}
"BAD_RESPONSE" {
append warningblock \n "WARNING: terminal doesn't seem to recognize decmode 2027 query. No grapheme cluster support."
set widest0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$widest0 + 4}]
append text \n [$t print]
lappend chunks [list stdout $text]
}
default {
set text ""
set cinfo [uplevel 1 [list punk::ns::cmdwhich [lindex $topicparts 0]]]
set wtype [dict get $cinfo whichtype]
if {$wtype eq "notfound"} {
set externalinfo [auto_execok [lindex $topicparts 0]]
if {[string length $externalinfo]} {
set text "$topicparts"
append text \n "Base type: External command"
append text \n "$externalinfo [lrange $topicparts 1 end]"
} else {
set text "$topicparts\n"
append text "No matching internal or external command found"
}
} else {
set text "[dict get $cinfo which] [lrange $topicparts 1 end]"
append text \n "Base type: $wtype"
set synopsis [uplevel 1 [list punk::ns::synopsis {*}$topicparts]]
set synshow ""
foreach sline [split $synopsis \n] {
if {[regexp {\s*#.*} $sline]} {
append synshow [punk::ansi::a+ bold term-darkgreen Term-white]$sline[punk::ansi::a] \n
} else {
append synshow $sline \n
}
}
if {[string index $synshow end] eq "\n"} {
set synshow [string range $synshow 0 end-1]
}
append text \n $synshow
}
lappend chunks [list stdout $text]
}
}
lappend chunks [list stderr $warningblock]
if {$topic in [list topics help]} {
set text ""
set topics [dict create\
"topics|help" "List help topics"\
"tcl" "Tcl version warnings"\
"env|environment" "punkshell environment vars"\
"console|terminal" "Some console behaviour tests and warnings"\
]
set t [textblock::class::table new -show_seps 0]
$t add_column -headers [list "Topic"]
$t add_column
foreach {k v} $topics {
$t add_row [list $k $v]
}
set widest0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$widest0 + 4}]
append text \n[$t print]
lappend chunks [list stdout $text]
}
lappend chunks [list stderr $warningblock]
return $chunks
}
proc help {args} {
set chunks [help_chunks {*}$args]
foreach chunk $chunks {
lassign $chunk chan text
puts -nonewline $chan $text
}
}
proc mode {{raw_or_line query}} {
package require punk::console
tailcall ::punk::console::mode $raw_or_line

177
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/ansi-0.1.1.tm

@ -340,6 +340,28 @@ tcl::namespace::eval punk::ansi::class {
}
}
tcl::namespace::eval punk::ansi {
namespace eval argdoc {
variable PUNKARGS
# -- --- --- --- ---
#non colour SGR codes
# we can use these directly via ${$I} etc without marking a definition with @dynamic
#This is because they don't need to change when colour switched on and off.
#chicken/egg - need to use literals here
set I "\x1b\[3m" ;# [a+ italic]
set NI "\x1b\[23m" ;# [a+ noitalic]
set B "\x1b\[1m" ;# [a+ bold]
set N "\x1b\[22m" ;# [a+ normal]
set T "\x1b\[1\;4m" ;# [a+ bold underline]
set NT "\x1b\[22\;24m\x1b\[4:0m" ;# [a+ normal nounderline]
set LC \u007b ;#left curly brace
set RC \u007d ;#right curly brace
# -- --- --- --- ---
#namespace import ::punk::args::helpers::*
}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
tcl::namespace::eval punk::ansi {
@ -2262,59 +2284,74 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
return $displaytable
}
lappend PUNKARGS [list {
@id -id ::punk::ansi::a?
@cmd -name "punk::ansi::a?"\
-summary\
"ANSI colour information"\
-help\
""
@form -form "sgr_overview"
@values -form "sgr_overview" -min 0 -max 0
@form -form "term"
@leaders -form "term" -min 1 -max 1
term -type literal(term) -help\
"256 term colours"
@opts -min 0 -max 0
@values -form "term" -min 0 -max -1
panel -type string -optional 1 -multiple 1 -default {16 main greyscale note}\
-choices {16 main greyscale pastel rainbow note}
@form -form "tk"
@leaders -form "tk" -min 1 -max 1
tk -type literal(tk)|literal(TK) -help\
"Tk colours"
@opts -form "tk"
-merged -type none -help\
"If this flag is supplied - show colour names with the same RGB
values together."
@values -form "tk" -min 0 -max -1
glob -type string -optional 1 -multiple 1 -help\
"A glob string such as *green*.
Multiple glob entries can be provided. The search is case insensitive"
@form -form "web"
@values -form "web" -min 1 -max -1
web -type literal(web) -help\
"Web colours"
panel -type string -optional 1 -multiple 1 -choices {basic pink red orange yellow brown purple blue cyan green white gray}
@form -form "x11"
@values -form "x11" -min 1 -max 1
x11 -type literal(x11) -help\
"x11 colours"
@form -form "sample"
@values -form "sample" -min 1 -max -1
colourcode -type sgr|Sgr|literalprefix(term-)|literalprefix(Term-)|literalprefix(web-)|literalprefix(Web-)|literalprefix(rgb)|literalprefix(Rgb)\
-optional 0\
-multiple 1
}]
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::ansi::a?
@cmd -name "punk::ansi::a?"\
-summary\
"ANSI colour information"\
-help\
"With no arguments - display an overview information panel.
With the first argument one of:
${$B}term tk TK web x11${$N}
Display a more specific panel of colour information.
With arguments of individual colourcodes from any of the above
sets, display a small diagnostic table showing a sample of
the individual and combined effect(s), along with indications
of the raw ANSI codes."
@form -form "sgr_overview"
@values -form "sgr_overview" -min 0 -max 0
@form -form "term"
@leaders -form "term" -min 1 -max 1
term -type literal(term) -help\
"256 term colours"
@opts -min 0 -max 0
@values -form "term" -min 0 -max -1
panel -type string -optional 1 -multiple 1 -default {16 main greyscale note}\
-choices {16 main greyscale pastel rainbow note}
@form -form "tk"
@leaders -form "tk" -min 1 -max 1
tk -type literal(tk)|literal(TK) -help\
"Tk colours"
@opts -form "tk"
-merged -type none -help\
"If this flag is supplied - show colour names with the same RGB
values together."
@values -form "tk" -min 0 -max -1
glob -type string -optional 1 -multiple 1 -help\
"A glob string such as *green*.
Multiple glob entries can be provided. The search is case insensitive"
@form -form "web"
@values -form "web" -min 1 -max -1
web -type literal(web) -help\
"Web colours"
panel -type string -optional 1 -multiple 1 -choices {basic pink red orange yellow brown purple blue cyan green white gray}
@form -form "x11"
@values -form "x11" -min 1 -max 1
x11 -type literal(x11) -help\
"x11 colours"
@form -form "sample"
@values -form "sample" -min 1 -max -1
#review literalprefix is not the right thing here! (literalprefix means a tcl::prefix::match on the value)
#colourcode -type sgr|Sgr|stringstartswith(term-)|stringstartswith(Term-)|stringstartswith(web-)|stringstartswith(Web-)|stringstartswith(rgb)|stringstartswith(Rgb)
colourcode -type string|stringstartswith(term-)|stringstartswith(Term-)|stringstartswith(web-)|stringstartswith(Web-)|stringstartswith(rgb)|stringstartswith(Rgb)\
-typesynopsis {${$I}sgr${$NI}|${$I}Sgr${$NI}|${$I}colourcode${$NI}}\
-optional 0\
-multiple 1
}]
}
proc a? {args} {
#*** !doctools
#[call [fun a?] [opt {ansicode...}]]
@ -2414,8 +2451,8 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
append out \n
append out "[a+ {*}$fc web-white]Combination testing[a]" \n
append out [textblock::join -- $indent "Example: a? red brightgreen underline Tk-slategrey italic"] \n
append out [textblock::join -- $indent "This will show a small table of each applied code and a RESULT row. The 'red' in this case is redundant,"] \n
append out [textblock::join -- $indent "so a final MERGED row displays with an alert 'REDUNDANCIES FOUND'."] \n
append out [textblock::join -- $indent "This will show a small table of each applied code and a RESULT row. The 'red' in this case is redundant, because there are"] \n
append out [textblock::join -- $indent "two foreground colours (lowercase first letter) so a final MERGED row displays with an alert 'REDUNDANCIES FOUND'."] \n
append out [textblock::join -- $indent "The final columns of RESULT and MERGED (showing raw ANSI sequence) will differ if the arguments aren't in canonical order."] \n
append out [textblock::join -- $indent "The MERGED line will only display if there are redundancies or different ordering."] \n
@ -2674,14 +2711,26 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
} else {
set tkcolours [list]
}
foreach c $webcolours {
append info \n web-$c
}
foreach c $x11colours {
append info \n x11-$c
}
foreach c $tkcolours {
append info \n tk-$c
if {[string is upper -strict [string index $pfx 0]]} {
foreach c $webcolours {
append info \n Web-$c
}
foreach c $x11colours {
append info \n X11-$c
}
foreach c $tkcolours {
append info \n Tk-$c
}
} else {
foreach c $webcolours {
append info \n web-$c
}
foreach c $x11colours {
append info \n x11-$c
}
foreach c $tkcolours {
append info \n tk-$c
}
}
$t add_row [list $i "$info" $s [ansistring VIEW $s]]
}
@ -8723,7 +8772,7 @@ interp alias {} ansistring {} ::punk::ansi::ansistring
namespace eval ::punk::args::register {
#use fully qualified so 8.6 doesn't find existing var in global namespace
lappend ::punk::args::register::NAMESPACES ::punk::ansi ::punk::ansi::class ::punk::ansi::ta
lappend ::punk::args::register::NAMESPACES ::punk::ansi ::punk::ansi::argdoc ::punk::ansi::class ::punk::ansi::ta
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++

153
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/args-0.2.1.tm

@ -359,16 +359,22 @@ tcl::namespace::eval ::punk::args::helpers {
#puts $str
#puts stderr -------------------
#rudimentary colourising (not full tcl syntax parsing)
#highlight comments first - so that we can also highlight braces within comments to help with detecting unbalanced braces/square brackets in comments
switch -- $opt_syntax {
tcl {
#rudimentary colourising (not full tcl syntax parsing)
#Note that this can highlight ;# in some places as a comment where it's not appropriate
# e.g inside a regexp
#highlight comments first - so that we can also highlight braces within comments to help with detecting unbalanced braces/square brackets in comments
#result lines often indicated in examples by \u2192 →
#however - it's not present on each line of output, instead indents are used - so we can't so easily highlight all appropriate rows(?)
set str [punk::grepstr -return all -highlight {Term-grey term-darkgreen} {^\s*#.*} $str] ;#Note, will not highlight comments at end of line - like this one
set str [punk::grepstr -return all -highlight {Term-grey term-darkgreen} {;\s*(#.*)} $str]
#TODO - fix grepstr highlighting (bg issues - why?)
set str [punk::grepstr -return all -highlight {Term-grey term-darkblue} {\{|\}} $str]
#Note that if we were to highlight based on the regexp {\{|\}} then the inserted ansi would come between
# the backslash and brace in \{ or \} - this breaks the syntactic structure causing problems.
set str [punk::grepstr -return all -highlight {Term-grey tk-darkblue} {^\{|[^\\](\{+)} $str]
set str [punk::grepstr -return all -highlight {Term-grey tk-darkblue} {[^\\](\}+)} $str]
set str [punk::grepstr -return all -highlight {Term-grey term-orange1} {\[|\]} $str]
#puts stderr -------------------
#puts $str
@ -460,7 +466,7 @@ tcl::namespace::eval ::punk::args::helpers {
tcl::namespace::eval punk::args {
package require punk::assertion
#if 'package forget' was called on this package (e.g when loading test::punk::args) then assert may already exist in the namespace
#procs can be overridden silently, but not imports
#namespace import will fail if target exists
catch {
namespace import ::punk::assertion::assert
}
@ -469,9 +475,9 @@ tcl::namespace::eval punk::args {
variable PUNKARGS ;#list of our own punk::args function argument definitions - loaded with punk::args::define at the end.
tcl::namespace::export {[a-z]*}
variable rawdef_cache
if {![info exists rawdef_cache]} {
set rawdef_cache [tcl::dict::create] ;#key on rawdef list - return dict of {-id <id> -dynamic 0|1}
variable rawdef_cache_about
if {![info exists rawdef_cache_about]} {
set rawdef_cache_about [tcl::dict::create] ;#key on rawdef list - return dict of {-id <id> -dynamic 0|1}
}
variable id_cache_rawdef
if {![info exists id_cache_rawdef]} {
@ -487,9 +493,9 @@ tcl::namespace::eval punk::args {
set argdefcache_unresolved [tcl::dict::create] ;#key = original template list supplied to 'define', value = 2-element list: (tstr-parsed split of template) & (unresolved params)
}
variable argdata_cache
if {![info exists argdata_cache]} {
set argdata_cache [tcl::dict::create]
variable rawdef_cache_argdata
if {![info exists rawdef_cache_argdata]} {
set rawdef_cache_argdata [tcl::dict::create]
}
variable id_counter
@ -979,11 +985,11 @@ tcl::namespace::eval punk::args {
error todo
}
proc define {args} {
variable rawdef_cache
variable rawdef_cache_about
variable id_cache_rawdef
variable argdata_cache
if {[dict exists $rawdef_cache $args]} {
return [dict get [dict get $rawdef_cache $args] -id]
#variable rawdef_cache_argdata
if {[dict exists $rawdef_cache_about $args]} {
return [dict get [dict get $rawdef_cache_about $args] -id]
} else {
set lvl 2
set id [rawdef_id $args $lvl]
@ -991,46 +997,40 @@ tcl::namespace::eval punk::args {
#we seem to be re-creating a previously defined id...
#clear any existing caches for this id
undefine $id 0
##dict unset argdata_cache $prevraw ;#silently does nothing if key not present
#dict for {k v} $argdata_cache {
# if {[dict get $v id] eq $id} {
# dict unset argdata_cache $k
# }
#}
#dict for {k v} $rawdef_cache {
# if {[dict get $v -id] eq $id} {
# dict unset rawdef_cache $k
# }
#}
#dict unset id_cache_rawdef $id
}
set is_dynamic [rawdef_is_dynamic $args]
set defspace [uplevel 1 {::namespace current}]
dict set rawdef_cache $args [dict create -id $id -dynamic $is_dynamic -defspace $defspace]
dict set rawdef_cache_about $args [dict create -id $id -dynamic $is_dynamic -defspace $defspace]
dict set id_cache_rawdef $id $args
return $id
}
}
proc undefine {id {quiet 0}} {
variable rawdef_cache
#review - alias?
variable rawdef_cache_about
variable id_cache_rawdef
variable argdata_cache
variable rawdef_cache_argdata
if {[id_exists $id]} {
if {!$quiet} {
puts stderr "punk::args::undefine clearing existing data for id:$id"
}
dict for {k v} $argdata_cache {
if {[dict get $v id] eq $id} {
dict unset argdata_cache $k
if {[dict exists $id_cache_rawdef $id]} {
set deflist [dict get $id_cache_rawdef $id]
dict unset rawdef_cache_about $deflist
dict unset rawdef_cache_argdata $deflist
dict unset id_cache_rawdef $id
} else {
dict for {k v} $rawdef_cache_argdata {
if {[dict get $v id] eq $id} {
dict unset rawdef_cache_argdata $k
}
}
}
dict for {k v} $rawdef_cache {
if {[dict get $v -id] eq $id} {
dict unset rawdef_cache $k
dict for {k v} $rawdef_cache_about {
if {[dict get $v -id] eq $id} {
dict unset rawdef_cache_about $k
}
}
}
dict unset id_cache_rawdef $id
} else {
if {!$quiet} {
puts stderr "punk::args::undefine unable to find id: '$id'"
@ -1039,17 +1039,26 @@ tcl::namespace::eval punk::args {
}
#'punk::args::parse $args withdef $deflist' can raise parsing error after an autoid was generated
# In this case we don't see the autoid in order to delete it
#proc undefine_deflist {deflist} {
#}
proc undefine_deflist {deflist} {
variable rawdef_cache_about
variable id_cache_rawdef
variable rawdef_cache_argdata
if {[dict exists $rawdef_cache_about $deflist -id]} {
set id [dict get $rawdef_cache_about $deflist -id]
dict unset rawdef_cache_about $deflist
dict unset rawdef_cache_argdata $deflist
dict unset id_cache_rawdef $id
}
}
proc idquery_info {id} {
variable id_cache_rawdef
variable rawdef_cache
variable rawdef_cache_about
if {[dict exists $id_cache_rawdef $id]} {
set sep [string repeat - 40]
set rawdef [dict get $id_cache_rawdef $id]
if {[dict exists $rawdef_cache $rawdef]} {
set idinfo [dict get $rawdef_cache $rawdef]
if {[dict exists $rawdef_cache_about $rawdef]} {
set idinfo [dict get $rawdef_cache_about $rawdef]
} else {
set idinfo ""
}
@ -1060,11 +1069,11 @@ tcl::namespace::eval punk::args {
append result \n "id info:"
append result \n $idinfo
append result \n $sep
variable argdata_cache
variable rawdef_cache_argdata
#lsearch -stride not avail (or buggy) in some 8.6 interps - search manually for now (2025). todo - modernize some time after Tcl 9.0/9.1 more widespread.(2027?)
#check for and report if id is present multiple times
set argdata_records [list]
dict for {k v} $argdata_cache {
dict for {k v} $rawdef_cache_argdata {
if {[dict get $v id] eq $id} {
if {$k eq $rawdef} {
lappend argdata_records [list 1 $k $v]
@ -1100,12 +1109,20 @@ tcl::namespace::eval punk::args {
dict get [resolve {*}$args] id
}
lappend PUNKARGS [list {
@id -id ::punk::args::resolve
@cmd -name punk::args::resolve -help\
""
@values -min 0 -max -1
arg -type any -multiple 1 -help\
"rawdef line block"
}]
proc resolve {args} {
variable rawdef_cache
variable rawdef_cache_about
variable id_cache_rawdef
set defspace ""
if {[dict exists $rawdef_cache $args]} {
set cinfo [dict get $rawdef_cache $args]
if {[dict exists $rawdef_cache_about $args]} {
set cinfo [dict get $rawdef_cache_about $args]
set id [dict get $cinfo -id]
set is_dynamic [dict get $cinfo -dynamic]
if {[dict exists $cinfo -defspace]} {
@ -1116,12 +1133,13 @@ tcl::namespace::eval punk::args {
set id [rawdef_id $args]
puts stderr "Warning: punk::args::resolve called with undefined id:$id"
set is_dynamic [rawdef_is_dynamic $args]
dict set rawdef_cache $args [dict create -id $id -dynamic $is_dynamic]
#-defspace ???
dict set rawdef_cache_about $args [dict create -id $id -dynamic $is_dynamic]
dict set id_cache_rawdef $id $args
}
variable argdata_cache
variable rawdef_cache_argdata
variable argdefcache_unresolved
@ -1155,8 +1173,8 @@ tcl::namespace::eval punk::args {
if {!$is_dynamic} {
#todo - don't use cached version if 'colour off' vs 'colour on' different to when last resolved!
#(e.g example blocks will still have colour if previously resolved)
if {[tcl::dict::exists $argdata_cache $cache_key]} {
return [tcl::dict::get $argdata_cache $cache_key]
if {[tcl::dict::exists $rawdef_cache_argdata $cache_key]} {
return [tcl::dict::get $rawdef_cache_argdata $cache_key]
}
set normargs [list]
foreach a $textargs {
@ -1235,10 +1253,10 @@ tcl::namespace::eval punk::args {
tcl::dict::set argdefcache_unresolved $cache_key $pt_params
}
}
#argdata_cache should be limited in some fashion or will be a big memory leak???
if {[tcl::dict::exists $argdata_cache [list $optionspecs]]} {
#rawdef_cache_argdata should be limited in some fashion or will be a big memory leak???
if {[tcl::dict::exists $rawdef_cache_argdata [list $optionspecs]]} {
#resolved cache version exists
return [tcl::dict::get $argdata_cache [list $optionspecs]]
return [tcl::dict::get $rawdef_cache_argdata [list $optionspecs]]
}
}
@ -2669,10 +2687,12 @@ tcl::namespace::eval punk::args {
form_info $form_info\
]
tcl::dict::set argdata_cache $cache_key $argdata_dict
#REVIEW
tcl::dict::set rawdef_cache_argdata $cache_key $argdata_dict
if {$is_dynamic} {
#also cache resolved version
tcl::dict::set argdata_cache [list $optionspecs] $argdata_dict
tcl::dict::set rawdef_cache_argdata [list $optionspecs] $argdata_dict
}
#tcl::dict::set id_cache_rawdef $DEF_definition_id $args
@ -3094,10 +3114,10 @@ tcl::namespace::eval punk::args {
}
proc is_dynamic {id} {
variable id_cache_rawdef
variable rawdef_cache
variable rawdef_cache_about
set deflist [raw_def $id]
if {[dict exists $rawdef_cache $deflist -dynamic]} {
return [dict get $rawdef_cache $deflist -dynamic]
if {[dict exists $rawdef_cache_about $deflist -dynamic]} {
return [dict get $rawdef_cache_about $deflist -dynamic]
}
return [rawdef_is_dynamic $deflist]
#@dynamic only has meaning as 1st element of a def in the deflist
@ -4008,7 +4028,16 @@ tcl::namespace::eval punk::args {
}
if {$synopsis ne ""} {
if {$use_table} {
$t configure_header $h -colspans $arg_colspans -values [list Synopsis: [punk::ansi::ansiwrap brightwhite $synopsis]]
set form_names [dict get $spec_dict form_names]
set synhelp "Synopsis:"
if {[llength $form_names] > 1} {
set fn 0
foreach fname $form_names {
append synhelp \n " i -form $fn \U2026"
incr fn
}
}
$t configure_header $h -colspans $arg_colspans -values [list $synhelp [punk::ansi::ansiwrap brightwhite $synopsis]]
} else {
#todo
lappend errlines "Synopsis:\n$synopsis"

1550
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/args/moduledoc/tclcore-0.1.0.tm

File diff suppressed because it is too large Load Diff

40
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/assertion-0.1.0.tm

@ -66,38 +66,6 @@ package require Tcl 8.6-
#*** !doctools
#[section API]
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# oo::class namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
tcl::namespace::eval punk::assertion::class {
#*** !doctools
#[subsection {Namespace punk::assertion::class}]
#[para] class definitions
if {[tcl::info::commands [tcl::namespace::current]::interface_sample1] eq ""} {
#*** !doctools
#[list_begin enumerated]
# oo::class create interface_sample1 {
# #*** !doctools
# #[enum] CLASS [class interface_sample1]
# #[list_begin definitions]
# method test {arg1} {
# #*** !doctools
# #[call class::interface_sample1 [method test] [arg arg1]]
# #[para] test method
# puts "test: $arg1"
# }
# #*** !doctools
# #[list_end] [comment {-- end definitions interface_sample1}]
# }
#*** !doctools
#[list_end] [comment {--- end class enumeration ---}]
}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#keep 2 namespaces for assertActive and assertInactive so there is introspection available via namespace origin
tcl::namespace::eval punk::assertion::primary {
@ -149,9 +117,13 @@ tcl::namespace::eval punk::assertion {
proc do_ns_import {} {
uplevel 1 [list tcl::namespace::import ::punk::assertion::primary::assertActive]
}
do_ns_import
#puts --------BBB
rename assertActive assert
if {[catch {
do_ns_import
rename assertActive assert
} errM]} {
puts stderr "punk::assertion error during load - assert/assertActive functions already present?\n$errM"
}
}

4
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/du-0.1.0.tm

@ -961,9 +961,9 @@ namespace eval punk::du {
proc get_vfsmounts_in_folder {folderpath} {
set vfsmounts [list]
if {![llength [package provide vfs]]} {
return [list]
return [list]
}
set fpath [punk::objclone $folderpath]
set fpath [punk::valcopy $folderpath]
set is_rel 0
if {[file pathtype $fpath] ne "absolute"} {
set fpath [file normalize $fpath]

12
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/lib-0.1.3.tm

@ -4071,11 +4071,11 @@ namespace eval punk::lib {
interp eval $interp [list tcl::tm::add {*}[lreverse [tcl::tm::list]]]
}
proc objclone {obj} {
proc valcopy {obj} {
append obj2 $obj {}
}
proc set_clone {varname obj} {
#used by repl's codeinterp. Maintains internal rep, easier to call e.g interp eval code [list punk::set_clone varnmame $val]
proc set_valcopy {varname obj} {
#used by repl's codeinterp. Maintains internal rep, easier to call e.g interp eval code [list punk::set_valcopy varnmame $val]
append obj2 $obj {}
uplevel 1 [list set $varname $obj2]
}
@ -4095,9 +4095,9 @@ namespace eval punk::lib {
set default_groupsize 3
set results [list]
set nums [objclone $numbers_or_commaformattednumbers] ;#stops single num from getting internal rep of list
set nums [valcopy $numbers_or_commaformattednumbers] ;#stops single num from getting internal rep of list
foreach inputnum $nums {
set number [objclone $inputnum]
set number [valcopy $inputnum]
#also handle tcl 8.7+ underscores in numbers
set number [string map [list _ "" , ""] $number]
#normalize e.g 2e4 -> 20000.0
@ -4145,7 +4145,7 @@ namespace eval punk::lib {
# and possibly containing a decimal point, i.e.: [:space:]*[:digit:]*\.[:digit:]*
proc delimit_number {unformattednumber {delim ","} {GroupSize 3}} {
set number [objclone $unformattednumber]
set number [valcopy $unformattednumber]
set number [string map {_ ""} $number]
#normalize using expr - e.g 2e4 -> 20000.0
set number [expr {$number}]

8
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm

@ -34,7 +34,7 @@ namespace eval punk::mix::commandset::loadedlib {
"(unimplemented) Display only those that are 0:absent 1:present 2:either"
-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"
-refresh -type none -help "Re-scan the tm and library folders"
searchstring -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.
@ -45,11 +45,11 @@ namespace eval punk::mix::commandset::loadedlib {
}
proc search {args} {
set argd [punk::args::parse $args withid ::punk::mix::commandset::loadedlib::search]
set searchstrings [dict get $argd values searchstring]
set opts [dict get $argd opts]
lassign [dict values $argd] leaders opts values received
set searchstrings [dict get $values searchstring]
set opt_return [dict get $opts -return]
set opt_highlight [dict get $opts -highlight]
set opt_refresh [dict get $opts -refresh]
set opt_refresh [dict exists $received -refresh]
if {$opt_refresh} {
catch {package require frobznodule666} ;#ensure pkg system has loaded/searched for everything REVIEW - this doesn't result in full scans

2328
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/ns-0.1.0.tm

File diff suppressed because it is too large Load Diff

58
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/repl-0.1.2.tm

@ -3177,7 +3177,7 @@ namespace eval repl {
variable errstack {}
variable outstack {}
variable run_command_cache
proc set_clone {varname obj} {
proc set_valcopy {varname obj} {
append obj2 $obj {}
uplevel 1 [list set $varname $obj2]
}
@ -3241,6 +3241,7 @@ namespace eval repl {
#}
set pkgs [list\
punk::ansi::colourmap\
punk::assertion\
punk::args\
punk::pipe\
cmdline\
@ -3256,7 +3257,6 @@ namespace eval repl {
textutil\
punk::encmime\
punk::char\
punk::assertion\
punk::ansi\
punk::lib\
overtype\
@ -3290,37 +3290,41 @@ namespace eval repl {
set prior_infoscript [code eval {info script}] ;#probably empty that's ok
foreach pkg $pkgs {
#puts stderr "---> init_script safe pkg: $pkg"
if {[catch {
set nsquals [namespace qualifiers $pkg]
if {$nsquals ne ""} {
if {![dict exists $ns_scanned $nsquals]} {
catch {package require $pkg 1-0} ;#force scan with nonsatisfiable version
dict set ns_scanned $nsquals 1
#only load from source if not already loaded (perhaps already present from another package loading it)
set vloaded [code eval [list package provide $pkg]]
if {$vloaded eq ""} {
if {[catch {
set nsquals [namespace qualifiers $pkg]
if {$nsquals ne ""} {
if {![dict exists $ns_scanned $nsquals]} {
catch {package require $pkg 1-0} ;#force scan with nonsatisfiable version
dict set ns_scanned $nsquals 1
}
}
}
set versions [lsort -command {package vcompare} [package versions $pkg]]
if {[llength $versions]} {
set v [lindex $versions end]
set path [lindex [package ifneeded $pkg $v] end]
if {[file extension $path] in {.tcl .tm}} {
if {![catch {readFile $path} data]} {
code eval [list info script $path]
code eval $data
code eval [list info script $prior_infoscript]
set versions [lsort -command {package vcompare} [package versions $pkg]]
if {[llength $versions]} {
set v [lindex $versions end]
set path [lindex [package ifneeded $pkg $v] end]
if {[file extension $path] in {.tcl .tm}} {
if {![catch {readFile $path} data]} {
code eval [list info script $path]
code eval $data
code eval [list info script $prior_infoscript]
} else {
error "safe - failed to read $path"
}
} else {
error "safe - failed to read $path"
error "safe - refusing to attempt load of $pkg from $path - (allowed extensions .tcl .tm)"
}
} else {
error "safe - refusing to attempt load of $pkg from $path - (allowed extensions .tcl .tm)"
error "safe - no versions of $pkg found"
}
} errMsg]} {
puts stderr "safe - failed to load package $pkg\n$errMsg\n$::errorInfo"
} else {
error "safe - no versions of $pkg found"
#puts stdout "---> init_script safe - loaded $pkg from $path"
#puts stdout "---> v [code eval [list package provide $pkg]]"
}
} errMsg]} {
puts stderr "safe - failed to load package $pkg\n$errMsg\n$::errorInfo"
} else {
#puts stdout "---> init_script safe - loaded $pkg from $path"
#puts stdout "---> v [code eval [list package provide $pkg]]"
}
}
@ -3337,7 +3341,7 @@ namespace eval repl {
#review
code alias ::shellfilter::stack ::shellfilter::stack
#code alias ::punk::lib::set_clone ::punk::lib::set_clone
#code alias ::punk::lib::set_valcopy ::punk::lib::set_valcopy
#code alias ::aliases ::punk::ns::aliases
code alias ::punk::ns::aliases ::punk::ns::aliases
namespace eval ::codeinterp {}

4
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/repl/codethread-0.1.1.tm

@ -196,8 +196,8 @@ tcl::namespace::eval punk::repl::codethread {
#otherwise when $script goes out of scope - internal rep of vars set in script changes.
#The shimmering may be no big deal(?) - but debug/analysis using tcl::unsupported::representation becomes impossible.
#interp eval code [list ::punk::lib::set_clone ::codeinterp::clonescript $script] ;#like objclone
interp eval code [list ::codeinterp::set_clone ::codeinterp::clonescript $script] ;#like objclone
#interp eval code [list ::punk::lib::set_valcopy ::codeinterp::clonescript $script] ;#like valcopy
interp eval code [list ::codeinterp::set_valcopy ::codeinterp::clonescript $script] ;#like valcopy
interp eval code {lappend ::codeinterp::run_command_cache $::codeinterp::clonescript}

18
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/unixywindows-0.1.0.tm

@ -35,35 +35,35 @@ namespace eval punk::unixywindows {
if {![string length $cachedunixyroot]} {
if {![catch {
set result [exec cygpath -m /] ;# -m gives result with forward-slashes - which is ok for windows paths in a Tcl context.
set cachedunixyroot [punk::objclone $result]
set cachedunixyroot [punk::valcopy $result]
file pathtype $cachedunixyroot; #this call causes the int-rep to be path
#set ::punk::last_run_display [list] ;#hack - review shouldn't really be necessary.. but because we call winpath from ./ - the repl looks for last_run_display
} errM]} {
} else {
puts stderr "Warning: Failed to determine base for unix-like paths - using default of c:/msys2"
file pathtype [set cachedunixyroot [punk::objclone "c:/msys2"]]
file pathtype [set cachedunixyroot [punk::valcopy "c:/msys2"]]
}
}
#will have been shimmered from string to 'path' internal rep by 'file pathtype' call
#let's return a different copy as it's so easy to lose path-rep
set copy [punk::objclone $cachedunixyroot]
set copy [punk::valcopy $cachedunixyroot]
return $copy
}
proc refresh_unixyroot {} {
variable cachedunixyroot
set result [exec cygpath -m /] ;# -m gives result with forward-slashes - which is ok for windows paths in a Tcl context.
set cachedunixyroot [punk::objclone $result]
set cachedunixyroot [punk::valcopy $result]
file pathtype $cachedunixyroot; #this call causes the int-rep to be path
set copy [punk::objclone $cachedunixyroot]
set copy [punk::valcopy $cachedunixyroot]
return $copy
}
proc set_unixyroot {windows_path} {
variable cachedunixyroot
file pathtype $windows_path
set cachedunixyroot [punk::objclone $windows_path]
set cachedunixyroot [punk::valcopy $windows_path]
#return the original - but probably int-rep will have shimmered to path even if started out as string
#- that's probably what we want. Caller can use as string or path without affecting cachedunixyroot
return $windows_path
@ -131,13 +131,13 @@ namespace eval punk::unixywindows {
}
#puts stderr "->$driveletters"
set path [punk::objclone $unixypath] ;#take another copy that we can deliberatley shimmer to path and know is separate to the supplied argument
set path [punk::valcopy $unixypath] ;#take another copy that we can deliberatley shimmer to path and know is separate to the supplied argument
set supplied_pathtype [file pathtype $path] ;#we don't care about the pathtype so much as the act of making this call shimmers to a path internal-rep
#copy of var that we can treat as a string without affecting path rep
#Note that some but not all read operations will lose path rep e.g 'string length $x' will lose any path-rep $x had, (but 'string equal $x something' won't)
#Todo - make int-rep tests to check stability of these behaviours across Tcl versions!
set strcopy_path [punk::objclone $path]
set strcopy_path [punk::valcopy $path]
set str_newpath ""
@ -174,7 +174,7 @@ namespace eval punk::unixywindows {
#dunno - pass through
set pathobj $path
} else {
set pathobj [punk::objclone $str_newpath]
set pathobj [punk::valcopy $str_newpath]
file pathtype $pathobj
}
}

12
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/winpath-0.1.0.tm

@ -30,7 +30,7 @@ namespace eval punk::winpath {
#\\servername\share etc or \\?\UNC\servername\share etc.
proc is_unc_path {path} {
set strcopy_path [punk::winpath::system::objclone $path]
set strcopy_path [punk::winpath::system::valcopy $path]
set strcopy_path [string map {\\ /} $strcopy_path] ;#normalize to forward slashes for testing purposes (and forward slashes seem to be auto-translated by windows anyway)
if {[string first "//" $strcopy_path] == 0} {
#check for "Dos device path" syntax
@ -77,7 +77,7 @@ namespace eval punk::winpath {
#dos device path syntax allows windows api to acces extended-length paths and filenames with illegal path characters such as trailing dots or whitespace
#(can exist on server shares and on NTFS - but standard apps can't access without dos device syntax)
proc is_dos_device_path {path} {
set strcopy_path [punk::winpath::system::objclone $path]
set strcopy_path [punk::winpath::system::valcopy $path]
set strcopy_path [string map {\\ /} $strcopy_path] ;#normalize to forward slashes for testing purposes (and forward slashes seem to be auto-translated by windows anyway)
if {[string range $strcopy_path 0 3] in {//?/ //./}} {
return 1
@ -100,13 +100,13 @@ namespace eval punk::winpath {
proc strip_unc_path_prefix {path} {
if {[is_unc_path_plain $path]} {
#plain unc //server
set strcopy_path [punk::winpath::system::objclone $path]
set strcopy_path [punk::winpath::system::valcopy $path]
set trimmedpath [string range $strcopy_path 2 end]
file pathtype $trimmedpath
return $trimmedpath
} elseif {is_unc_path $path} {
#//?/UNC/server/subpath or //./UNC/server/subpath
set strcopy_path [punk::winpath::system::objclone $path]
set strcopy_path [punk::winpath::system::valcopy $path]
set trimmedpath [string range $strcopy_path 7 end]
file pathtype $trimmedpath ;#shimmer it to path rep
return $trimmedpath
@ -153,7 +153,7 @@ namespace eval punk::winpath {
error $err
}
set strcopy_path [punk::winpath::system::objclone $path]
set strcopy_path [punk::winpath::system::valcopy $path]
#Note: path could still have leading double slash if it is a Dos device path: e.g. //?/c:/etc
@ -339,7 +339,7 @@ namespace eval punk::winpath {
namespace eval punk::winpath::system {
#get a copy of the item without affecting internal rep
proc objclone {obj} {
proc valcopy {obj} {
append obj2 $obj {}
}
}

1940
src/vfs/_vfscommon.vfs/modules/patterndispatcher-1.2.4.tm

File diff suppressed because it is too large Load Diff

664
src/vfs/_vfscommon.vfs/modules/patternpredator1-1.0.tm

@ -0,0 +1,664 @@
package provide patternpredator1 1.0
proc ::p::internals::trailing, {map command stack i arglist pending} {
error "trailing ',' is not a valid sequence. If the comma was meant to be an argument, precede it with the -- operator."
}
proc ::p::internals::trailing.. {map command stack i arglist pending} {
error "trailing .. references not implemented."
}
proc ::p::internals::trailing. {map command _ID_ stack i arglist pending} {
if {![llength $map]} {
error "Trailing . but no map - command:'$command' stack:$stack i:$i arglist:$arglist pending:$pending"
}
#trailing dot - get reference.
#puts ">>trailing dot>> map:$map command:$command _ID_:$_ID_ stack:$stack i:$i arglist:$arglist pending:$pending"
lassign [lindex $map 0] OID alias itemCmd cmd
#lassign $command command _ID_
if {$pending eq {}} {
#no pending operation requiring evaluation.
#presumably we're getting a ref to the object, not a property or method.
#set traceCmd [::list ::p::objectRef_TraceHandler $cmd $_self $OID]
#if {[::list {array read write unset} $traceCmd] ni [trace info variable $refname]} {
# trace add variable $refname {array read write unset} $traceCmd
#}
set refname ::p::${OID}::_ref::__OBJECT ;#!todo - avoid potential collision with ref to member named '__OBJECT'.
#object ref - ensure trace-var is an array upon which the objects methods & properties can be accessed as indices
array set $refname [list]
#!todo?- populate array with object methods/properties now?
set _ID_ [list i [list this [list [list $OID [list map $map]]]]]
#!todo - review. What if $map is out of date?
set traceCmd [list ::p::predator::object_read_trace $OID $_ID_]
if {[list {read} $traceCmd] ni [trace info variable $refname]} {
trace add variable $refname {read} $traceCmd
}
set traceCmd [list ::p::predator::object_array_trace $OID $_ID_]
if {[list {array} $traceCmd] ni [trace info variable $refname]} {
trace add variable $refname {array} $traceCmd
}
set traceCmd [list ::p::predator::object_write_trace $OID $_ID_]
if {[list {write} $traceCmd] ni [trace info variable $refname]} {
trace add variable $refname {write} $traceCmd
}
set traceCmd [list ::p::predator::object_unset_trace $OID $_ID_]
if {[list {unset} $traceCmd] ni [trace info variable $refname]} {
trace add variable $refname {unset} $traceCmd
}
#set command $refname
return $refname
} else {
#puts "- 11111111 '$command' '$stack'"
if {[string range $command 0 171] eq "::p::-1::"} {
#!todo - review/enable this branch?
#reference to meta-member
#STALE map problem!!
puts "\naaaaa command: $command\n"
set field [namespace tail [lindex $command 0]]
set map [lindex $stack 0]
set OID [lindex $map 0 0]
if {[llength $stack]} {
set refname ::p::${OID}::_ref::[join [concat $field [lrange $stack 1 end]] +]
set command [interp alias {} $refname {} {*}$command {*}$stack]
} else {
set refname ::p::${OID}::_ref::$field
set command [interp alias {} $refname {} {*}$command]
}
puts "???? command '$command' \n refname '$refname' \n"
} else {
#Property or Method reference (possibly with curried indices or arguments)
#we don't want our references to look like objects.
#(If they did, they might be found by namespace tidyup code and treated incorrectly)
set field [string map {> __OBJECT_} [namespace tail $command]]
#!todo?
# - make every object's OID unpredictable and sparse (UUID) and modify 'namespace child' etc to prevent iteration/inspection of ::p namespace.
#references created under ::p::${OID}::_ref are effectively inside a 'varspace' within the object itself.
# - this would in theory allow a set of interface functions on the object which have direct access to the reference variables.
if {[llength $stack]} {
set refname ::p::${OID}::_ref::[join [concat $field $stack] +]
#puts stdout " ------------>>>> refname:$refname"
if {[string length $_ID_]} {
set command [interp alias {} $refname {} $command $_ID_ {*}$stack]
} else {
set command [interp alias {} $refname {} $command {*}$stack]
}
} else {
set refname ::p::${OID}::_ref::$field
#!review - for consistency.. we don't directly return method name.
if {[string length $_ID_]} {
set command [interp alias {} $refname {} $command $_ID_]
} else {
set command [interp alias {} $refname {} $command]
}
}
#puts ">>>!>>>> refname $refname \n"
#NOTE! - we always create a command alias even if $field is not a method.
#(
#!todo? - build a list of properties from all interfaces (cache it on object??)
set iflist [lindex $map 1 0]
set found 0
foreach IFID [lreverse $iflist] {
#if {$field in [dict keys [set ::p::${IFID}::_iface::o_methods]]} {
# set found 1
# break
#}
if {[llength [info commands ::p::${IFID}::_iface::(GET)$field]] || [llength [info commands ::p::${IFID}::_iface::(SET)$field]]} {
set found 1
break
}
}
if {$found} {
#property reference
#?
#set readref [string map [list ::_ref:: ::_ref::(GET)
#set writeref [string map [list ::_ref:: ::_ref::(SET)
#puts "-2222222222 $refname"
#puts "---HERE! $OID $property ::p::${OID}::_ref::${property}"
#trace remove variable $refname {read write unset} [list ::p::internals::commandrefMisuse_TraceHandler $alias $field]
foreach tinfo [trace info variable $refname] {
#puts "-->removing traces on $refname: $tinfo"
if {[lindex $tinfo 1 0] eq "::p::internals::commandrefMisuse_TraceHandler"} {
trace remove variable $refname {*}$tinfo
}
}
#!todo - move to within trace info test below.. no need to test for refsync trace if no other trace?
#We only trace on entire property.. not array elements (if references existed to both the array and an element both traces would be fired -(entire array trace first))
set Hndlr [::list ::p::predator::refsync_TraceHandler $OID $alias "" $field]
if { [::list {write unset} $Hndlr] ni [trace info variable ::p::${OID}::o_${field}]} {
trace add variable ::p::${OID}::o_${field} {write unset} $Hndlr
}
set _ID_ [dict create i [dict create this [list [list $OID [list map $map] ]]]]
#supply all data in easy-access form so that prop_trace_read is not doing any extra work.
set get_cmd ::p::${OID}::(GET)$field
set traceCmd [list ::p::predator::prop_trace_read $get_cmd $_ID_ $refname $field $stack]
if {[list {read} $traceCmd] ni [trace info variable $refname]} {
#synch the refvar with the real var if it exists
#catch {set $refname [$refname]}
if {[array exists ::p::${OID}::o_$field]} {
if {![llength $stack]} {
#unindexed reference
array set $refname [array get ::p::${OID}::o_$field]
} else {
#refs to nonexistant array members common? (catch vs 'info exists')
if {[info exists ::p::${OID}::o_${field}([lindex $stack 0])]} {
set $refname [set ::p::${OID}::o_${field}([lindex $stack 0])]
}
}
} else {
#catch means retrieving refs to non-initialised props slightly slower.
set errorInfo_prev $::errorInfo ;#preserve errorInfo across these catches!
if {![llength $stack]} {
catch {set $refname [set ::p::${OID}::o_$field]}
} else {
if {[llength $stack] == 1} {
catch {set $refname [lindex [set ::p::${OID}::o_$field] [lindex $stack 0]]}
} else {
catch {set $refname [lindex [set ::p::${OID}::o_$field] $stack]}
}
}
#! what if someone has put a trace on ::errorInfo??
set ::errorInfo $errorInfo_prev
}
trace add variable $refname {read} $traceCmd
set traceCmd [list ::p::predator::prop_trace_write $_ID_ $OID $alias $refname]
trace add variable $refname {write} $traceCmd
set traceCmd [list ::p::predator::prop_trace_unset $_ID_ $OID $alias $refname]
trace add variable $refname {unset} $traceCmd
set traceCmd [list ::p::predator::prop_trace_array $_ID_ $OID $alias $refname]
trace add variable $refname {array} $traceCmd
}
} else {
#matching variable in order to detect attempted use as property and throw error
#puts "$refname ====> adding refMisuse_traceHandler $alias $field"
trace add variable $refname {read write unset} [list ::p::internals::commandrefMisuse_TraceHandler $alias $field]
}
}
return $command
}
}
#script to inline at placeholder @reduce_pending_stack@
set ::p::internals::reduce_pending_stack {
if {$pending eq {idx}} {
if {$OID ne {null}} {
#pattern object
#set command [list ::p::${OID}::$itemCmd [dict create i [dict create this [list $OID]]]]
set command ::p::${OID}::$itemCmd
set _ID_ [dict create i [dict create this [list [list $OID [list map $map]]]]]
#todo: set _ID_ [dict create i [dict create this [list [list $OID - - - {}]]] context {}]
} else {
set command [list $itemCmd $command]
}
}
if {![llength [info commands [lindex $command 0]]]} {
set cmdname [namespace tail [lindex $command 0]]
if {[llength [info commands ::p::${OID}::(UNKNOWN)]]} {
lset command 0 ::p::${OID}::(UNKNOWN)
#puts stdout "!\n calling UNKNOWN $command $cmdname $stack\n\n"
if {[string length $_ID_]} {
set interim [uplevel 1 [list {*}$command $_ID_ $cmdname {*}$stack]] ;#delegate to UNKNOWN, along with original commandname as 1st arg.
} else {
set interim [uplevel 1 [list {*}$command $cmdname {*}$stack]] ;#delegate to UNKNOWN, along with original commandname as 1st arg.
}
} else {
return -code error -errorinfo "1)error running command:'$command' argstack:'$stack'\n - command not found and no 'unknown' handler" "method '$command' not found"
}
} else {
#puts "---??? uplevelling $command $_ID_ $stack"
if {[string length $_ID_]} {
set interim [uplevel 1 [list {*}$command $_ID_ {*}$stack]]
} else {
set interim [uplevel 1 [list {*}$command {*}$stack]]
}
#puts "---?2? interim:$interim"
}
if {[string first ::> $interim] >= 0} {
#puts "--- ---> tailcalling $interim [lrange $args $i end]"
tailcall {*}$interim {*}[lrange $args $i end] ;#don't use a return statement as tailcall implies return
} else {
#the interim result is not a pattern object - but the . indicates we should treat it as a command
#tailcall ::p::internals::predator [list [list {null} {} {lindex} $interim {}]] {*}[lrange $args $i end]
#set nextmap [list [list {null} {} {lindex} $interim {}]]
#tailcall ::p::internals::predator [dict create i [dict create this [list null ]] map $nextmap] {*}[lrange $args $i end]
#tailcall ::p::internals::predator [dict create i [dict create this [list [list null [list]] ]] xmap $nextmap] {*}[lrange $args $i end]
tailcall ::p::internals::predator [dict create i [dict create this [list [list null [list map [list [list {null} {} {lindex} $interim {}]]]] ]] callerinfo ""] {*}[lrange $args $i end]
}
}
proc ::p::predator1 {subject args} [string map [list @reduce_pending_stack@ $::p::internals::reduce_pending_stack] {
#set OID [lindex [dict get $subject i this] 0 0]
set this_invocant [lindex [dict get $subject i this] 0] ;#for the role 'this' we assume only one invocant in the list.
lassign $this_invocant OID this_info
if {$OID ne {null}} {
#upvar #0 ::p::${OID}::_meta::map map
#if {![dict exists [lindex [dict get $subject i this] 0 1] map]} {
# set map [set ::p::${OID}::_meta::map]
#} else {
# set map [dict get [lindex [dict get $subject i this] 0 1] map]
#}
#seems to be faster just to grab from the variable, than to unwrap from $_ID_ !?
#set map [set ::p::${OID}::_meta::map]
# if {![dict exists $this_info map]} {
set map [set ::p::${OID}::_meta::map]
#} else {
# set map [dict get $this_info map]
#}
lassign [lindex $map 0] OID alias itemCmd cmd
set cheat 1
#-------
#the common optimised case first. A single . followed by args which contain no other operators (non-chained call)
#(it should be functionally equivalent to remove this shortcut block)
if {$cheat} {
if {([llength $args] > 1) && ([lindex $args 0] eq {.}) && ([llength [lsearch -all $args .]] == 1) && ({,} ni $args) && ({..} ni $args) && ({--} ni $args)} {
set command ::p::${OID}::[lindex $args 1]
if {![llength [info commands $command]]} {
if {[llength [info commands ::p::${OID}::(UNKNOWN)]]} {
set cmdname [namespace tail $command]
lset command 0 ::p::${OID}::(UNKNOWN)
#return [uplevel 1 [list {*}$command [dict create i [dict create this [list [list $OID [list map $map]]]]] $cmdname {*}[lrange $args 2 end]]] ;#delegate to UNKNOWN, along with original commandname as 1st arg.
tailcall {*}$command [dict create i [dict create this [list [list $OID [list map [list [lindex $map 0] {{} {}} ] ] ]]]] $cmdname {*}[lrange $args 2 end] ;#delegate to UNKNOWN, along with original commandname as 1st arg.
} else {
return -code error -errorinfo "2)error running command:'$command' argstack:'[lrange $args 2 end]'\n - command not found and no 'unknown' handler" "method '$command' not found"
}
} else {
#puts " -->> tailcalling $command [lrange $args 2 end]"
#tailcall $command [dict create i [dict create this [list [list $OID [list map $map]] ]]] {*}[lrange $args 2 end]
#tailcall $command [dict create i [dict create this [list [list $OID {}] ]]] {*}[lrange $args 2 end]
#jjj
#tailcall $command [list i [list this [list [list $OID [list map $map]] ]]] {*}[lrange $args 2 end]
tailcall $command [list i [list this [list [list $OID [list map [list [lindex $map 0] { {} {} } ]]] ]]] {*}[lrange $args 2 end]
}
}
}
#------------
if {![llength $args]} {
#return $map
return [lindex $map 0 1]
} elseif {[llength $args] == 1} {
#short-circuit the single index case for speed.
if {$args ni {.. . -- - & @}} {
if {$cheat} {
lassign [lindex $map 0] OID alias itemCmd
#return [::p::${OID}::$itemCmd [lindex $args 0]]
#tailcall ::p::${OID}::$itemCmd [dict create i [dict create this [list [list $OID [list map $map]]]]] [lindex $args 0]
tailcall ::p::${OID}::$itemCmd [list i [list this [list [list $OID [list map $map]]]]] [lindex $args 0]
}
} elseif {[lindex $args 0] eq {--}} {
#!todo - we could hide the invocant by only allowing this call from certain uplevel procs..
# - combined with using UUIDs for $OID, and a secured/removed metaface on the object
# - (and also hiding of [interp aliases] command so they can't iterate and examine all aliases)
# - this could effectively hide the object's namespaces,vars etc from the caller (?)
return $map
}
}
} else {
#null OID - assume map is included in the _ID_ dict.
#set map [dict get $subject map]
set map [dict get $this_info map]
lassign [lindex $map 0] OID alias itemCmd cmd
}
#puts "predator==== subject:$subject args:$args map:$map cmd:$cmd "
#set map $invocant ;#retain 'invocant' in case we need original caller info.. 'map' is the data for whatever is at base of stack.
set command $cmd
set stack [list]
#set operators [list . , ..] ;#(exclude --)
#!todo? short-circuit/inline commonest/simplest case {llength $args == 2}
set argProtect 0
set pending "" ;#pending operator e.g . , idx .. & @
set _ID_ ""
set i 0
while {$i < [llength $args]} {
set word [lindex $args $i]
if {$argProtect} {
#argProtect must be checked first.
# We are here because a previous operator necessitates that this word is an argument, not another operator.
set argProtect 0
lappend stack $word
if {$pending eq {}} {
set pending idx ;#An argument only implies the index operator if no existing operator is in effect. (because '>obj $arg' must be equivalent to '>obj . item $arg'
}
incr i
} else {
switch -- $word {.} {
#$i is the operator, $i + 1 is the command.
if {[llength $args] > ($i + 1)} {
#there is at least a command, possibly args too
if {$pending ne {}} {
#puts ">>>> >>>>> about to reduce. pending:$pending command:$command _ID_:$_ID_ stack:$stack"
#always bounces back into the predator via tailcall
@reduce_pending_stack@
} else {
if {$OID ne {null}} {
#set command ::p::${OID}::[lindex $args $i+1]
#lappend stack [dict create i [dict create this [list $OID]]]
set command ::p::${OID}::[lindex $args $i+1]
set _ID_ [list i [list this [list [list $OID [list map $map]]]]]
} else {
#set command [list $command [lindex $args $i+1]]
lappend stack [lindex $args $i+1]
}
set pending .
set argProtect 0
incr i 2
}
} else {
#this is a trailing .
#puts "----> MAP $map ,command $command ,stack $stack"
if {$OID ne {null}} {
return [::p::internals::trailing. $map $command $_ID_ $stack $i $args $pending]
} else {
#!todo - fix. This is broken!
#the usefulness of a reference to an already-resolved value is questionable.. but for consistency it should probably be made to work.
#for a null object - we need to supply the map in the invocation data
set command ::p::internals::predator
set this_info [dict create map [list [list {null} {} {lindex} $command {}]] ]
set this_invocant [list null $this_info]
set _ID_ [dict create i [dict create this [list $this_invocant]] ]
return [::p::internals::trailing. $map $command $_ID_ $stack $i $args $pending]
}
}
} {--} {
#argSafety operator (see also "," & -* below)
set argProtect 1
incr i
} {,} {
set argProtect 1
if {$i+1 < [llength $args]} {
#not trailing
if {$pending ne {}} {
@reduce_pending_stack@
} else {
if {$OID ne {null}} {
#set command [list ::p::[lindex $map 0 0]::$itemCmd [lindex $args $i+1]]
#set command [list $command . $itemCmd [lindex $args $i+1]]
set stack [list . $itemCmd [lindex $args $i+1]]
set _ID_ ""
#lappend stack [dict create i [dict create this [list $OID]]]
set pending "."
} else {
# this is an index operation into what is presumed to be a previously returned standard tcl list. (as opposed to an index operation into a returned pattern object)
#set command [list $itemCmd $command [lindex $args $i+1]]
#set command [list ::p::internals::predator [list [list {null} {} {lindex} $command {}]] [lindex $args $i+1] ]
#set command ::p::internals::predator
#set _ID_ [dict create i [dict create this [list null]] map [list [list {null} {} {lindex} [lindex $args $i+1] {}]] ]
#lappend stack [lindex $args $i+1]
set command [list $itemCmd $command] ;#e.g {lindex {a b c}}
#set command ::p::internals::predator
#set _ID_ [dict create i [dict create this [list null]] map [list [list {null} {} {lindex} $nextcommand {}]]]
set _ID_ {}
lappend stack [lindex $args $i+1]
set pending "." ;#*not* idx or ","
}
set argProtect 0
incr i 2
}
} else {
return [::p::internals::trailing, $map $command $stack $i $args $pending]
}
} {..} {
#Metaface operator
if {$i+1 < [llength $args]} {
#operator is not trailing.
if {$pending ne {}} {
@reduce_pending_stack@
} else {
incr i
#set command [list ::p::-1::[lindex $args $i] [dict create i [dict create this [list $OID]]]]
set command ::p::-1::[lindex $args $i]
#_ID_ is a list, 1st element being a dict of invocants.
# Each key of the dict is an invocant 'role'
# Each value is a list of invocant-aliases fulfilling that role
#lappend stack [list [list caller [lindex $map 0 1] ]]
#lappend stack [list [dict create this [lindex $map 0 1]]] ;#'this' being the 'subject' in a standard singledispatch method call.
#lappend stack [dict create i [dict create this [list [lindex $map 0 1]]]]
set _ID_ [dict create i [dict create this [list [list $OID [list map $map]]]]]
set pending ..
incr i
}
} else {
return [::p::internals::trailing.. $map $command $stack $i $args $pending]
}
} {&} {
#conglomeration operator
if {$i+1 < [llength $args]} {
if {$pending ne {} } {
@reduce_pending_stack@
#set interim [uplevel 1 [list {*}$command {*}$stack]]
#tailcall {*}$interim {*}[lrange $args $i end] ;#don't use a return statement as tailcall implies return
}
set command [list ::p::-1::Conglomerate $command]
lappend stack [lindex $args $i+1]
set pending &
incr i
} else {
error "trailing & not supported"
}
} {@} {
#named-invocant operator
if {$i+1 < [llength $args]} {
if {$pending ne {} } {
@reduce_pending_stack@
} else {
error "@ not implemented"
set pending @
incr i
}
} else {
error "trailing @ not supported"
}
} default {
if {[string index $word 0] ni {. -}} {
lappend stack $word
if {$pending eq {}} {
set pending idx
}
incr i
} else {
if {[string match "-*" $word]} {
#argSafety operator (tokens that appear to be Tcl 'options' automatically 'protect' the subsequent argument)
set argProtect 1
lappend stack $word
incr i
} else {
if {([string index $word 0] eq ".") && ([string index $word end] eq ".") } {
#interface accessor!
error "interface casts not yet implemented!"
set ifspec [string range $word 1 end]
if {$ifspec eq "!"} {
#create 'snapshot' reference with all current interfaces
} else {
foreach ifname [split $ifspec ,] {
#make each comma-separated interface-name accessible via the 'casted object'
}
}
} else {
#has a leading . only. treat as an argument not an operator.
lappend stack $word
if {$pending eq {}} {
set pending idx
}
incr i
}
}
}
}
}
}
#assert: $pending ne ""
#(we've run out of args, and if no 'operators' were found, then $pending will have been set to 'idx' - equivalent to '. item' )
#puts "---> evalling command + stack:'$command' '$stack' (pending:'$pending')"
if {$pending in {idx}} {
if {$OID ne {null}} {
#pattern object
set command ::p::${OID}::$itemCmd
set _ID_ [dict create i [dict create this [list [list $OID [list map $map] ] ]]]
} else {
# some other kind of command
set command [list $itemCmd $command]
}
}
if {![llength [info commands [lindex $command 0]]]} {
set cmdname [namespace tail [lindex $command 0]]
if {[llength [info commands ::p::${OID}::(UNKNOWN)]]} {
lset command 0 ::p::${OID}::(UNKNOWN)
#puts stdout "!\n calling UNKNOWN $command $cmdname $stack\n\n"
if {[string length $_ID_]} {
tailcall {*}$command $_ID_ $cmdname {*}$stack ;#delegate to UNKNOWN, along with original commandname as 1st arg.
} else {
tailcall {*}$command $cmdname {*}$stack ;#delegate to UNKNOWN, along with original commandname as 1st arg.
}
} else {
return -code error -errorinfo "3)error running command:'$command' argstack:'$stack'\n - command not found and no 'unknown' handler" "method '$command' not found"
}
}
#puts "... tailcalling $command $stack"
if {[string length $_ID_]} {
tailcall {*}$command $_ID_ {*}$stack
} else {
tailcall {*}$command {*}$stack
}
}]

664
src/vfs/_vfscommon.vfs/modules/patternpredator1-1.2.4.tm

@ -0,0 +1,664 @@
package provide patternpredator1 1.2.4
proc ::p::internals::trailing, {map command stack i arglist pending} {
error "trailing ',' is not a valid sequence. If the comma was meant to be an argument, precede it with the -- operator."
}
proc ::p::internals::trailing.. {map command stack i arglist pending} {
error "trailing .. references not implemented."
}
proc ::p::internals::trailing. {map command _ID_ stack i arglist pending} {
if {![llength $map]} {
error "Trailing . but no map - command:'$command' stack:$stack i:$i arglist:$arglist pending:$pending"
}
#trailing dot - get reference.
#puts ">>trailing dot>> map:$map command:$command _ID_:$_ID_ stack:$stack i:$i arglist:$arglist pending:$pending"
lassign [lindex $map 0] OID alias itemCmd cmd
#lassign $command command _ID_
if {$pending eq {}} {
#no pending operation requiring evaluation.
#presumably we're getting a ref to the object, not a property or method.
#set traceCmd [::list ::p::objectRef_TraceHandler $cmd $_self $OID]
#if {[::list {array read write unset} $traceCmd] ni [trace info variable $refname]} {
# trace add variable $refname {array read write unset} $traceCmd
#}
set refname ::p::${OID}::_ref::__OBJECT ;#!todo - avoid potential collision with ref to member named '__OBJECT'.
#object ref - ensure trace-var is an array upon which the objects methods & properties can be accessed as indices
array set $refname [list]
#!todo?- populate array with object methods/properties now?
set _ID_ [list i [list this [list [list $OID [list map $map]]]]]
#!todo - review. What if $map is out of date?
set traceCmd [list ::p::predator::object_read_trace $OID $_ID_]
if {[list {read} $traceCmd] ni [trace info variable $refname]} {
trace add variable $refname {read} $traceCmd
}
set traceCmd [list ::p::predator::object_array_trace $OID $_ID_]
if {[list {array} $traceCmd] ni [trace info variable $refname]} {
trace add variable $refname {array} $traceCmd
}
set traceCmd [list ::p::predator::object_write_trace $OID $_ID_]
if {[list {write} $traceCmd] ni [trace info variable $refname]} {
trace add variable $refname {write} $traceCmd
}
set traceCmd [list ::p::predator::object_unset_trace $OID $_ID_]
if {[list {unset} $traceCmd] ni [trace info variable $refname]} {
trace add variable $refname {unset} $traceCmd
}
#set command $refname
return $refname
} else {
#puts "- 11111111 '$command' '$stack'"
if {[string range $command 0 171] eq "::p::-1::"} {
#!todo - review/enable this branch?
#reference to meta-member
#STALE map problem!!
puts "\naaaaa command: $command\n"
set field [namespace tail [lindex $command 0]]
set map [lindex $stack 0]
set OID [lindex $map 0 0]
if {[llength $stack]} {
set refname ::p::${OID}::_ref::[join [concat $field [lrange $stack 1 end]] +]
set command [interp alias {} $refname {} {*}$command {*}$stack]
} else {
set refname ::p::${OID}::_ref::$field
set command [interp alias {} $refname {} {*}$command]
}
puts "???? command '$command' \n refname '$refname' \n"
} else {
#Property or Method reference (possibly with curried indices or arguments)
#we don't want our references to look like objects.
#(If they did, they might be found by namespace tidyup code and treated incorrectly)
set field [string map {> __OBJECT_} [namespace tail $command]]
#!todo?
# - make every object's OID unpredictable and sparse (UUID) and modify 'namespace child' etc to prevent iteration/inspection of ::p namespace.
#references created under ::p::${OID}::_ref are effectively inside a 'varspace' within the object itself.
# - this would in theory allow a set of interface functions on the object which have direct access to the reference variables.
if {[llength $stack]} {
set refname ::p::${OID}::_ref::[join [concat $field $stack] +]
#puts stdout " ------------>>>> refname:$refname"
if {[string length $_ID_]} {
set command [interp alias {} $refname {} $command $_ID_ {*}$stack]
} else {
set command [interp alias {} $refname {} $command {*}$stack]
}
} else {
set refname ::p::${OID}::_ref::$field
#!review - for consistency.. we don't directly return method name.
if {[string length $_ID_]} {
set command [interp alias {} $refname {} $command $_ID_]
} else {
set command [interp alias {} $refname {} $command]
}
}
#puts ">>>!>>>> refname $refname \n"
#NOTE! - we always create a command alias even if $field is not a method.
#(
#!todo? - build a list of properties from all interfaces (cache it on object??)
set iflist [lindex $map 1 0]
set found 0
foreach IFID [lreverse $iflist] {
#if {$field in [dict keys [set ::p::${IFID}::_iface::o_methods]]} {
# set found 1
# break
#}
if {[llength [info commands ::p::${IFID}::_iface::(GET)$field]] || [llength [info commands ::p::${IFID}::_iface::(SET)$field]]} {
set found 1
break
}
}
if {$found} {
#property reference
#?
#set readref [string map [list ::_ref:: ::_ref::(GET)
#set writeref [string map [list ::_ref:: ::_ref::(SET)
#puts "-2222222222 $refname"
#puts "---HERE! $OID $property ::p::${OID}::_ref::${property}"
#trace remove variable $refname {read write unset} [list ::p::internals::commandrefMisuse_TraceHandler $alias $field]
foreach tinfo [trace info variable $refname] {
#puts "-->removing traces on $refname: $tinfo"
if {[lindex $tinfo 1 0] eq "::p::internals::commandrefMisuse_TraceHandler"} {
trace remove variable $refname {*}$tinfo
}
}
#!todo - move to within trace info test below.. no need to test for refsync trace if no other trace?
#We only trace on entire property.. not array elements (if references existed to both the array and an element both traces would be fired -(entire array trace first))
set Hndlr [::list ::p::predator::refsync_TraceHandler $OID $alias "" $field]
if { [::list {write unset} $Hndlr] ni [trace info variable ::p::${OID}::o_${field}]} {
trace add variable ::p::${OID}::o_${field} {write unset} $Hndlr
}
set _ID_ [dict create i [dict create this [list [list $OID [list map $map] ]]]]
#supply all data in easy-access form so that prop_trace_read is not doing any extra work.
set get_cmd ::p::${OID}::(GET)$field
set traceCmd [list ::p::predator::prop_trace_read $get_cmd $_ID_ $refname $field $stack]
if {[list {read} $traceCmd] ni [trace info variable $refname]} {
#synch the refvar with the real var if it exists
#catch {set $refname [$refname]}
if {[array exists ::p::${OID}::o_$field]} {
if {![llength $stack]} {
#unindexed reference
array set $refname [array get ::p::${OID}::o_$field]
} else {
#refs to nonexistant array members common? (catch vs 'info exists')
if {[info exists ::p::${OID}::o_${field}([lindex $stack 0])]} {
set $refname [set ::p::${OID}::o_${field}([lindex $stack 0])]
}
}
} else {
#catch means retrieving refs to non-initialised props slightly slower.
set errorInfo_prev $::errorInfo ;#preserve errorInfo across these catches!
if {![llength $stack]} {
catch {set $refname [set ::p::${OID}::o_$field]}
} else {
if {[llength $stack] == 1} {
catch {set $refname [lindex [set ::p::${OID}::o_$field] [lindex $stack 0]]}
} else {
catch {set $refname [lindex [set ::p::${OID}::o_$field] $stack]}
}
}
#! what if someone has put a trace on ::errorInfo??
set ::errorInfo $errorInfo_prev
}
trace add variable $refname {read} $traceCmd
set traceCmd [list ::p::predator::prop_trace_write $_ID_ $OID $alias $refname]
trace add variable $refname {write} $traceCmd
set traceCmd [list ::p::predator::prop_trace_unset $_ID_ $OID $alias $refname]
trace add variable $refname {unset} $traceCmd
set traceCmd [list ::p::predator::prop_trace_array $_ID_ $OID $alias $refname]
trace add variable $refname {array} $traceCmd
}
} else {
#matching variable in order to detect attempted use as property and throw error
#puts "$refname ====> adding refMisuse_traceHandler $alias $field"
trace add variable $refname {read write unset} [list ::p::internals::commandrefMisuse_TraceHandler $alias $field]
}
}
return $command
}
}
#script to inline at placeholder @reduce_pending_stack@
set ::p::internals::reduce_pending_stack {
if {$pending eq {idx}} {
if {$OID ne {null}} {
#pattern object
#set command [list ::p::${OID}::$itemCmd [dict create i [dict create this [list $OID]]]]
set command ::p::${OID}::$itemCmd
set _ID_ [dict create i [dict create this [list [list $OID [list map $map]]]]]
#todo: set _ID_ [dict create i [dict create this [list [list $OID - - - {}]]] context {}]
} else {
set command [list $itemCmd $command]
}
}
if {![llength [info commands [lindex $command 0]]]} {
set cmdname [namespace tail [lindex $command 0]]
if {[llength [info commands ::p::${OID}::(UNKNOWN)]]} {
lset command 0 ::p::${OID}::(UNKNOWN)
#puts stdout "!\n calling UNKNOWN $command $cmdname $stack\n\n"
if {[string length $_ID_]} {
set interim [uplevel 1 [list {*}$command $_ID_ $cmdname {*}$stack]] ;#delegate to UNKNOWN, along with original commandname as 1st arg.
} else {
set interim [uplevel 1 [list {*}$command $cmdname {*}$stack]] ;#delegate to UNKNOWN, along with original commandname as 1st arg.
}
} else {
return -code error -errorinfo "1)error running command:'$command' argstack:'$stack'\n - command not found and no 'unknown' handler" "method '$command' not found"
}
} else {
#puts "---??? uplevelling $command $_ID_ $stack"
if {[string length $_ID_]} {
set interim [uplevel 1 [list {*}$command $_ID_ {*}$stack]]
} else {
set interim [uplevel 1 [list {*}$command {*}$stack]]
}
#puts "---?2? interim:$interim"
}
if {[string first ::> $interim] >= 0} {
#puts "--- ---> tailcalling $interim [lrange $args $i end]"
tailcall {*}$interim {*}[lrange $args $i end] ;#don't use a return statement as tailcall implies return
} else {
#the interim result is not a pattern object - but the . indicates we should treat it as a command
#tailcall ::p::internals::predator [list [list {null} {} {lindex} $interim {}]] {*}[lrange $args $i end]
#set nextmap [list [list {null} {} {lindex} $interim {}]]
#tailcall ::p::internals::predator [dict create i [dict create this [list null ]] map $nextmap] {*}[lrange $args $i end]
#tailcall ::p::internals::predator [dict create i [dict create this [list [list null [list]] ]] xmap $nextmap] {*}[lrange $args $i end]
tailcall ::p::internals::predator [dict create i [dict create this [list [list null [list map [list [list {null} {} {lindex} $interim {}]]]] ]] callerinfo ""] {*}[lrange $args $i end]
}
}
proc ::p::predator1 {subject args} [string map [list @reduce_pending_stack@ $::p::internals::reduce_pending_stack] {
#set OID [lindex [dict get $subject i this] 0 0]
set this_invocant [lindex [dict get $subject i this] 0] ;#for the role 'this' we assume only one invocant in the list.
lassign $this_invocant OID this_info
if {$OID ne {null}} {
#upvar #0 ::p::${OID}::_meta::map map
#if {![dict exists [lindex [dict get $subject i this] 0 1] map]} {
# set map [set ::p::${OID}::_meta::map]
#} else {
# set map [dict get [lindex [dict get $subject i this] 0 1] map]
#}
#seems to be faster just to grab from the variable, than to unwrap from $_ID_ !?
#set map [set ::p::${OID}::_meta::map]
# if {![dict exists $this_info map]} {
set map [set ::p::${OID}::_meta::map]
#} else {
# set map [dict get $this_info map]
#}
lassign [lindex $map 0] OID alias itemCmd cmd
set cheat 1
#-------
#the common optimised case first. A single . followed by args which contain no other operators (non-chained call)
#(it should be functionally equivalent to remove this shortcut block)
if {$cheat} {
if {([llength $args] > 1) && ([lindex $args 0] eq {.}) && ([llength [lsearch -all $args .]] == 1) && ({,} ni $args) && ({..} ni $args) && ({--} ni $args)} {
set command ::p::${OID}::[lindex $args 1]
if {![llength [info commands $command]]} {
if {[llength [info commands ::p::${OID}::(UNKNOWN)]]} {
set cmdname [namespace tail $command]
lset command 0 ::p::${OID}::(UNKNOWN)
#return [uplevel 1 [list {*}$command [dict create i [dict create this [list [list $OID [list map $map]]]]] $cmdname {*}[lrange $args 2 end]]] ;#delegate to UNKNOWN, along with original commandname as 1st arg.
tailcall {*}$command [dict create i [dict create this [list [list $OID [list map [list [lindex $map 0] {{} {}} ] ] ]]]] $cmdname {*}[lrange $args 2 end] ;#delegate to UNKNOWN, along with original commandname as 1st arg.
} else {
return -code error -errorinfo "2)error running command:'$command' argstack:'[lrange $args 2 end]'\n - command not found and no 'unknown' handler" "method '$command' not found"
}
} else {
#puts " -->> tailcalling $command [lrange $args 2 end]"
#tailcall $command [dict create i [dict create this [list [list $OID [list map $map]] ]]] {*}[lrange $args 2 end]
#tailcall $command [dict create i [dict create this [list [list $OID {}] ]]] {*}[lrange $args 2 end]
#jjj
#tailcall $command [list i [list this [list [list $OID [list map $map]] ]]] {*}[lrange $args 2 end]
tailcall $command [list i [list this [list [list $OID [list map [list [lindex $map 0] { {} {} } ]]] ]]] {*}[lrange $args 2 end]
}
}
}
#------------
if {![llength $args]} {
#return $map
return [lindex $map 0 1]
} elseif {[llength $args] == 1} {
#short-circuit the single index case for speed.
if {$args ni {.. . -- - & @}} {
if {$cheat} {
lassign [lindex $map 0] OID alias itemCmd
#return [::p::${OID}::$itemCmd [lindex $args 0]]
#tailcall ::p::${OID}::$itemCmd [dict create i [dict create this [list [list $OID [list map $map]]]]] [lindex $args 0]
tailcall ::p::${OID}::$itemCmd [list i [list this [list [list $OID [list map $map]]]]] [lindex $args 0]
}
} elseif {[lindex $args 0] eq {--}} {
#!todo - we could hide the invocant by only allowing this call from certain uplevel procs..
# - combined with using UUIDs for $OID, and a secured/removed metaface on the object
# - (and also hiding of [interp aliases] command so they can't iterate and examine all aliases)
# - this could effectively hide the object's namespaces,vars etc from the caller (?)
return $map
}
}
} else {
#null OID - assume map is included in the _ID_ dict.
#set map [dict get $subject map]
set map [dict get $this_info map]
lassign [lindex $map 0] OID alias itemCmd cmd
}
#puts "predator==== subject:$subject args:$args map:$map cmd:$cmd "
#set map $invocant ;#retain 'invocant' in case we need original caller info.. 'map' is the data for whatever is at base of stack.
set command $cmd
set stack [list]
#set operators [list . , ..] ;#(exclude --)
#!todo? short-circuit/inline commonest/simplest case {llength $args == 2}
set argProtect 0
set pending "" ;#pending operator e.g . , idx .. & @
set _ID_ ""
set i 0
while {$i < [llength $args]} {
set word [lindex $args $i]
if {$argProtect} {
#argProtect must be checked first.
# We are here because a previous operator necessitates that this word is an argument, not another operator.
set argProtect 0
lappend stack $word
if {$pending eq {}} {
set pending idx ;#An argument only implies the index operator if no existing operator is in effect. (because '>obj $arg' must be equivalent to '>obj . item $arg'
}
incr i
} else {
switch -- $word {.} {
#$i is the operator, $i + 1 is the command.
if {[llength $args] > ($i + 1)} {
#there is at least a command, possibly args too
if {$pending ne {}} {
#puts ">>>> >>>>> about to reduce. pending:$pending command:$command _ID_:$_ID_ stack:$stack"
#always bounces back into the predator via tailcall
@reduce_pending_stack@
} else {
if {$OID ne {null}} {
#set command ::p::${OID}::[lindex $args $i+1]
#lappend stack [dict create i [dict create this [list $OID]]]
set command ::p::${OID}::[lindex $args $i+1]
set _ID_ [list i [list this [list [list $OID [list map $map]]]]]
} else {
#set command [list $command [lindex $args $i+1]]
lappend stack [lindex $args $i+1]
}
set pending .
set argProtect 0
incr i 2
}
} else {
#this is a trailing .
#puts "----> MAP $map ,command $command ,stack $stack"
if {$OID ne {null}} {
return [::p::internals::trailing. $map $command $_ID_ $stack $i $args $pending]
} else {
#!todo - fix. This is broken!
#the usefulness of a reference to an already-resolved value is questionable.. but for consistency it should probably be made to work.
#for a null object - we need to supply the map in the invocation data
set command ::p::internals::predator
set this_info [dict create map [list [list {null} {} {lindex} $command {}]] ]
set this_invocant [list null $this_info]
set _ID_ [dict create i [dict create this [list $this_invocant]] ]
return [::p::internals::trailing. $map $command $_ID_ $stack $i $args $pending]
}
}
} {--} {
#argSafety operator (see also "," & -* below)
set argProtect 1
incr i
} {,} {
set argProtect 1
if {$i+1 < [llength $args]} {
#not trailing
if {$pending ne {}} {
@reduce_pending_stack@
} else {
if {$OID ne {null}} {
#set command [list ::p::[lindex $map 0 0]::$itemCmd [lindex $args $i+1]]
#set command [list $command . $itemCmd [lindex $args $i+1]]
set stack [list . $itemCmd [lindex $args $i+1]]
set _ID_ ""
#lappend stack [dict create i [dict create this [list $OID]]]
set pending "."
} else {
# this is an index operation into what is presumed to be a previously returned standard tcl list. (as opposed to an index operation into a returned pattern object)
#set command [list $itemCmd $command [lindex $args $i+1]]
#set command [list ::p::internals::predator [list [list {null} {} {lindex} $command {}]] [lindex $args $i+1] ]
#set command ::p::internals::predator
#set _ID_ [dict create i [dict create this [list null]] map [list [list {null} {} {lindex} [lindex $args $i+1] {}]] ]
#lappend stack [lindex $args $i+1]
set command [list $itemCmd $command] ;#e.g {lindex {a b c}}
#set command ::p::internals::predator
#set _ID_ [dict create i [dict create this [list null]] map [list [list {null} {} {lindex} $nextcommand {}]]]
set _ID_ {}
lappend stack [lindex $args $i+1]
set pending "." ;#*not* idx or ","
}
set argProtect 0
incr i 2
}
} else {
return [::p::internals::trailing, $map $command $stack $i $args $pending]
}
} {..} {
#Metaface operator
if {$i+1 < [llength $args]} {
#operator is not trailing.
if {$pending ne {}} {
@reduce_pending_stack@
} else {
incr i
#set command [list ::p::-1::[lindex $args $i] [dict create i [dict create this [list $OID]]]]
set command ::p::-1::[lindex $args $i]
#_ID_ is a list, 1st element being a dict of invocants.
# Each key of the dict is an invocant 'role'
# Each value is a list of invocant-aliases fulfilling that role
#lappend stack [list [list caller [lindex $map 0 1] ]]
#lappend stack [list [dict create this [lindex $map 0 1]]] ;#'this' being the 'subject' in a standard singledispatch method call.
#lappend stack [dict create i [dict create this [list [lindex $map 0 1]]]]
set _ID_ [dict create i [dict create this [list [list $OID [list map $map]]]]]
set pending ..
incr i
}
} else {
return [::p::internals::trailing.. $map $command $stack $i $args $pending]
}
} {&} {
#conglomeration operator
if {$i+1 < [llength $args]} {
if {$pending ne {} } {
@reduce_pending_stack@
#set interim [uplevel 1 [list {*}$command {*}$stack]]
#tailcall {*}$interim {*}[lrange $args $i end] ;#don't use a return statement as tailcall implies return
}
set command [list ::p::-1::Conglomerate $command]
lappend stack [lindex $args $i+1]
set pending &
incr i
} else {
error "trailing & not supported"
}
} {@} {
#named-invocant operator
if {$i+1 < [llength $args]} {
if {$pending ne {} } {
@reduce_pending_stack@
} else {
error "@ not implemented"
set pending @
incr i
}
} else {
error "trailing @ not supported"
}
} default {
if {[string index $word 0] ni {. -}} {
lappend stack $word
if {$pending eq {}} {
set pending idx
}
incr i
} else {
if {[string match "-*" $word]} {
#argSafety operator (tokens that appear to be Tcl 'options' automatically 'protect' the subsequent argument)
set argProtect 1
lappend stack $word
incr i
} else {
if {([string index $word 0] eq ".") && ([string index $word end] eq ".") } {
#interface accessor!
error "interface casts not yet implemented!"
set ifspec [string range $word 1 end]
if {$ifspec eq "!"} {
#create 'snapshot' reference with all current interfaces
} else {
foreach ifname [split $ifspec ,] {
#make each comma-separated interface-name accessible via the 'casted object'
}
}
} else {
#has a leading . only. treat as an argument not an operator.
lappend stack $word
if {$pending eq {}} {
set pending idx
}
incr i
}
}
}
}
}
}
#assert: $pending ne ""
#(we've run out of args, and if no 'operators' were found, then $pending will have been set to 'idx' - equivalent to '. item' )
#puts "---> evalling command + stack:'$command' '$stack' (pending:'$pending')"
if {$pending in {idx}} {
if {$OID ne {null}} {
#pattern object
set command ::p::${OID}::$itemCmd
set _ID_ [dict create i [dict create this [list [list $OID [list map $map] ] ]]]
} else {
# some other kind of command
set command [list $itemCmd $command]
}
}
if {![llength [info commands [lindex $command 0]]]} {
set cmdname [namespace tail [lindex $command 0]]
if {[llength [info commands ::p::${OID}::(UNKNOWN)]]} {
lset command 0 ::p::${OID}::(UNKNOWN)
#puts stdout "!\n calling UNKNOWN $command $cmdname $stack\n\n"
if {[string length $_ID_]} {
tailcall {*}$command $_ID_ $cmdname {*}$stack ;#delegate to UNKNOWN, along with original commandname as 1st arg.
} else {
tailcall {*}$command $cmdname {*}$stack ;#delegate to UNKNOWN, along with original commandname as 1st arg.
}
} else {
return -code error -errorinfo "3)error running command:'$command' argstack:'$stack'\n - command not found and no 'unknown' handler" "method '$command' not found"
}
}
#puts "... tailcalling $command $stack"
if {[string length $_ID_]} {
tailcall {*}$command $_ID_ {*}$stack
} else {
tailcall {*}$command {*}$stack
}
}]

746
src/vfs/_vfscommon.vfs/modules/punk-0.1.tm

@ -496,23 +496,25 @@ namespace eval punk {
#-----------------------------------------------------------------------------------
#get a copy of the item without affecting internal rep
proc objclone {obj} {
proc valcopy {obj} {
append obj2 $obj {}
}
proc set_clone {varname obj} {
#maintenance: also punk::lib::set_clone
#e.g used by repl's codeinterp. Maintains internal rep, easier to call e.g interp eval code [list punk::set_clone varnmame $val]
proc set_valcopy {varname obj} {
#maintenance: also punk::lib::set_valcopy
#e.g used by repl's codeinterp. Maintains internal rep, easier to call e.g interp eval code [list punk::set_valcopy varnmame $val]
append obj2 $obj {}
uplevel 1 [list set $varname $obj2]
}
interp alias "" strlen "" ::punk::strlen
interp alias "" str_len "" ::punk::strlen
interp alias "" objclone "" ::punk::objclone
interp alias "" valcopy "" ::punk::valcopy
#proc ::strlen {str} {
# string length [append str2 $str {}]
#}
#proc ::objclone {obj} {
#proc ::valcopy {obj} {
# append obj2 $obj {}
#}
@ -629,10 +631,24 @@ namespace eval punk {
-- -type none
@values
pattern -type string -help\
"regex pattern to match in plaintext portion of ANSI string
{regex pattern to match in plaintext portion of ANSI string
The pattern may contain bracketed capturing groups, which
will be highlighted (todo) in the result. If there is no capturing
group, the entire match will be highlighted."
will be highlighted in the result. If there is no capturing
group, the entire match will be highlighted.
Note that if we were to attempt to highlight curly braces based
on the regexp {\{|\}} then the inserted ansi would come between
the backslash and brace in cases where a curly brace is escaped
ie \{ or \}
Depending on how the output is used, this can break the syntactic
structure causing problems.
Instead a pair of regexes such as
{^\{|[^\\](\{+)}
{[^\\](\}+)}
should be used to
exclude braces that are escaped.
(note the capturing groups around each curly brace)
}
string -type string
}
proc grepstr {args} {
@ -706,9 +722,12 @@ namespace eval punk {
}
if {$lineindex in $matched_line_indices} {
set plain_ln [lindex $plainlines $lineindex]
#first test the regexp with a single match to determine number of capturing groups
set matchparts [regexp {*}$nocase -inline -- $pattern $plain_ln] ;#get fullmatch capturegroup...
set numgroups [expr {[llength $matchparts] -1}]
#first - determine the number of capturing groups (subexpressions)
#option 1: test the regexp with a single match
#set testparts [regexp {*}$nocase -inline -- $pattern $plain_ln] ;#get fullmatch capturegroup...
#set numgroups [expr {[llength $testparts] -1}]
#option 2: use the regexp -about flag
set numgroups [lindex [regexp -about $pattern] 0]
set allparts [regexp -all {*}$nocase -indices -inline -- $pattern $plain_ln]
#allparts includes each full match as well as each capturing group
@ -730,9 +749,14 @@ namespace eval punk {
# restrict ourself to just the capture groups, excluding the full match (if there are capture groups)
set highlight_ranges [list]
set i 0
#{-1 -1} returned for non-matching group when there are capture-group alternatives
#e.g {(a)|(b)}
foreach range $allparts {
if {($i % ($numgroups+1)) != 0} {
lappend highlight_ranges $range
lassign $range a b
if {$range ne {-1 -1} & $a <= $b} {
lappend highlight_ranges $range
}
}
incr i
}
@ -917,10 +941,8 @@ namespace eval punk {
return [twapi::new_uuid]
}
}
#get last command result that was run through the repl
proc ::punk::get_runchunk {args} {
set argd [punk::args::parse $args withdef {
namespace eval argdoc {
punk::args::define {
@id -id ::punk::get_runchunk
@cmd -name "punk::get_runchunk" -help\
"experimental"
@ -928,7 +950,19 @@ namespace eval punk {
-1 -optional 1 -type none
-2 -optional 1 -type none
@values -min 0 -max 0
}]
}
}
#get last command result that was run through the repl
proc ::punk::get_runchunk {args} {
#set argd [punk::args::parse $args withdef {
# @id -id ::punk::get_runchunk
# @cmd -name "punk::get_runchunk" -help\
# "experimental"
# @opts
# -1 -optional 1 -type none
# -2 -optional 1 -type none
# @values -min 0 -max 0
#}]
#todo - make this command run without truncating previous runchunks
set runchunks [tsv::array names repl runchunks-*]
@ -4148,7 +4182,7 @@ namespace eval punk {
#pipealias should capture the namespace context of the pipeline so that commands are resolved in the namespace in which the pipealias is created
proc pipealias {targetcmd args} {
set cmdcopy [punk::objclone $args]
set cmdcopy [punk::valcopy $args]
set nscaller [uplevel 1 [list namespace current]]
tailcall interp alias {} $targetcmd {} apply [list args [append cmdcopy " {*}\$args"] $nscaller]
}
@ -4159,7 +4193,7 @@ namespace eval punk {
}
#although the pipealias2 'concat' alias is cleaner in that the original pipeline can be extracted using list commands - it runs much slower
proc pipealias2 {targetcmd args} {
set cmdcopy [punk::objclone $args]
set cmdcopy [punk::valcopy $args]
set nscaller [uplevel 1 [list namespace current]]
tailcall interp alias {} $targetcmd {} apply [list args [concat "\[concat" [list $cmdcopy] "\$args]"] $nscaller]
}
@ -6654,15 +6688,16 @@ namespace eval punk {
#interp alias {} ~ {} file join $::env(HOME) ;#HOME must be capitalized to work cross platform (lowercase home works on windows - but probably not elsewhere)
#interp alias {} ~ {} apply {args {file join $::env(HOME) $args}}
proc ~ {args} {
set hdir [punk::objclone $::env(HOME)]
set hdir [punk::valcopy $::env(HOME)]
file pathtype $hdir
set d $hdir
#use the file join 2-arg optimisation to avoid losing path-rep - probably doesn't give any advantage on all Tcl versions
#review - for what versions does/did the 2-arg version of file join not just return a string?
foreach a $args {
set d [file join $d $a]
}
file pathtype $d
return [punk::objclone $d]
return [punk::valcopy $d]
}
interp alias {} ~ {} punk::~
@ -7789,47 +7824,61 @@ namespace eval punk {
namespace eval argdoc {
punk::args::define {
@id -id ::punk::help_chunks
@cmd -name "punk::help_chunks"\
-summary\
""\
-help\
""
@opts
-- -type none
@values -min 0 -max -1
arg -type any -optional 1 -multiple 1
}
}
proc help {args} {
set chunks [uplevel 1 [list ::punk::help_chunks {*}$args]]
foreach chunk $chunks {
lassign $chunk chan text
puts -nonewline $chan $text
}
}
#return list of {chan chunk} elements
proc help_chunks {args} {
set chunks [list]
set linesep [string repeat - 76]
set mascotblock ""
catch {
package require patternpunk
#lappend chunks [list stderr [>punk . rhs]]
append mascotblock [textblock::frame -checkargs 0 [>punk . banner -title "Punk Shell" -left Tcl -right [package provide Tcl]]]
set argd [punk::args::parse $args withid ::punk::help_chunks]
lassign [dict values $argd] leaders opts values received
if {[dict exists $values arg]} {
set topicparts [dict get $values arg]
} else {
set topicparts [list ""]
}
#set topic [lindex $args end]
#set argopts [lrange $args 0 end-1]
set topic [lindex $args end]
set argopts [lrange $args 0 end-1]
set chunks [list]
set linesep [string repeat - 76]
set warningblock ""
set title "[a+ brightgreen] Punk core navigation commands: "
set I [punk::ansi::a+ italic]
set NI [punk::ansi::a+ noitalic]
#todo - load from source code annotation?
# -------------------------------------------------------
set logoblock ""
if {[catch {
package require patternpunk
#lappend chunks [list stderr [>punk . rhs]]
append logoblock [textblock::frame -title "Punk Shell [package provide punk]" -width 29 -checkargs 0 [>punk . banner -title "" -left Tcl -right [package provide Tcl]]]
}]} {
append logoblock [textblock::frame -title "Punk Shell [package provide punk]" -subtitle "TCL [package provide Tcl]" -width 29 -height 10 -checkargs 0 ""]
}
set title "[a+ brightgreen] Help System: "
set cmdinfo [list]
lappend cmdinfo [list help "?topics?" "This help. To see available subitems type: help topics"]
lappend cmdinfo [list i "cmd ?subcommand...?" "Show usage for a command or ensemble subcommand"]
lappend cmdinfo [list s "cmd ?subcommand...?" "Show synopsis for a command or ensemble subcommand"]
lappend cmdinfo [list eg "cmd ?subcommand...?" "Show example from manpage"]
lappend cmdinfo [list ./ "?subdir?" "view/change directory"]
lappend cmdinfo [list ../ "" "go up one directory"]
lappend cmdinfo [list ./new "subdir" "make new directory and switch to it"]
lappend cmdinfo [list n/ "?glob...?" "view/change namespace\n (accepts ns path globs e.g **::*get* to match comands at any level )"]
lappend cmdinfo [list n// "" "view/change namespace (with command listing)"]
lappend cmdinfo [list "nn/" "" "go up one namespace"]
lappend cmdinfo [list "n/new" "<ns>" "make child namespace and switch to it"]
lappend cmdinfo [list dev "?subcommand?" "(ensemble command to make new projects/modules and to generate docs)"]
lappend cmdinfo [list a? "?subcommand...?" "view ANSI colours\n e.g a? web"]
#set cmds [lsearch -all -inline -index 0 -subindices $cmdinfo *]
#set descr [lsearch -all -inline -index 1 -subindices $cmdinfo *]
#set widest1 [tcl::mathfunc::max {*}[lmap v $cmds {string length $v}]]
#set widest2 [tcl::mathfunc::max {*}[lmap v $descr {string length $v}]]
set t [textblock::class::table new -show_seps 0]
#foreach c $cmds d $descr {
# $t add_row [list $c $d]
#}
lappend cmdinfo [list help "?${I}topic${NI}?" "This help.\nTo see available subitems type:\nhelp topics\n\n\n\n\n"]
set t [textblock::class::table new -minwidth 51 -show_seps 0]
foreach row $cmdinfo {
$t add_row $row
}
@ -7838,272 +7887,387 @@ namespace eval punk {
set width_1 [$t column_datawidth 1]
$t configure_column 1 -minwidth [expr {$width_1 + 1}]
$t configure -title $title
set text ""
append text [$t print]
set warningblock ""
set introblock $mascotblock
append introblock \n $text
#if {[catch {package require textblock} errM]} {
# append warningblock \n "WARNING: textblock package couldn't be loaded. Side-by-side display not available"
#} else {
# set introblock [textblock::join -- " " \n$mascotblock " " $text]
#}
lappend chunks [list stdout $introblock]
if {$topic in [list tcl]} {
if {[punk::lib::check::has_tclbug_script_var]} {
append warningblock \n "minor warning: punk::lib::check::has_tclbug_script_var returned true! (string rep for list variable in script generated when script changed)"
}
if {[punk::lib::check::has_tclbug_safeinterp_compile]} {
set indent " "
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_safeinterp returned true!" \n
append warningblock "${indent}(ensemble commands not compiled in safe interps - heavy performance impact in safe interps)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/1095bf7f75]"
append warningblock [a]
}
if {[catch {lsearch -stride 2 {a b} b}]} {
set indent " "
append warningblock \n "[a+ web-red]warning: lsearch does not seem to support -stride option" \n
append warningblock "${indent}(Consider upgrading to a late release of tcl 8.6 or tcl 9+ )" \n
append warningblock [a]
} else {
if {[punk::lib::check::has_tclbug_lsearch_strideallinline]} {
set text [$t print]
set introblock [textblock::join -- $logoblock $text]
lappend chunks [list stdout $introblock\n]
# -------------------------------------------------------
switch -- [lindex $topicparts 0] {
"" {
# -------------------------------------------------------
set title "[a+ brightgreen] Filesystem navigation: "
set cmdinfo [list]
lappend cmdinfo [list ./ "?${I}subdir${NI}?" "view/change directory"]
lappend cmdinfo [list ../ "" "go up one directory"]
lappend cmdinfo [list ./new "${I}subdir${NI}" "make new directory and switch to it"]
set t [textblock::class::table new -minwidth 80 -show_seps 0]
foreach row $cmdinfo {
$t add_row $row
}
set width_0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$width_0 + 2}]
set width_1 [$t column_datawidth 1]
$t configure_column 1 -minwidth [expr {$width_1 + 1}]
$t configure -title $title
set text ""
append text [$t print]
lappend chunks [list stdout $text\n]
# -------------------------------------------------------
# -------------------------------------------------------
set title "[a+ brightgreen] Namespace navigation: "
set cmdinfo [list]
lappend cmdinfo [list n/ "?${I}ns${NI}|${I}glob${NI}?" "view/change namespace\n (accepts ns path globs e.g **::*get* to match\n commands at any level )"]
lappend cmdinfo [list n// "?${I}ns${NI}|${I}glob${NI}?" "view/change namespace (with command listing)"]
lappend cmdinfo [list "nn/" "" "go up one namespace"]
lappend cmdinfo [list "n/new" "${I}ns${NI}" "make child namespace and switch to it"]
set t [textblock::class::table new -minwidth 80 -show_seps 0]
foreach row $cmdinfo {
$t add_row $row
}
set width_0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$width_0 + 2}]
set width_1 [$t column_datawidth 1]
$t configure_column 1 -minwidth [expr {$width_1 + 1}]
$t configure -title $title
set text ""
append text [$t print]
lappend chunks [list stdout $text\n]
# -------------------------------------------------------
# -------------------------------------------------------
set title "[a+ brightgreen] Command help: "
set cmdinfo [list]
lappend cmdinfo [list i "${I}cmd${NI} ?${I}subcommand${NI}...?" "Show usage for a command or ensemble subcommand"]
lappend cmdinfo [list s "${I}cmd${NI} ?${I}subcommand${NI}...?" "Show synopsis for a command or ensemble subcommand"]
lappend cmdinfo [list eg "${I}cmd${NI} ?${I}subcommand${NI}...?" "Show example from manpage"]
lappend cmdinfo [list corp "${I}proc${NI}" "View proc body and arguments"]
set t [textblock::class::table new -minwidth 80 -show_seps 0]
foreach row $cmdinfo {
$t add_row $row
}
set width_0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$width_0 + 2}]
set width_1 [$t column_datawidth 1]
$t configure_column 1 -minwidth [expr {$width_1 + 1}]
$t configure -title $title
set text ""
append text [$t print]
lappend chunks [list stdout $text\n]
# -------------------------------------------------------
set title "[a+ brightgreen] Miscellaneous: "
#todo - load from source code annotation?
set cmdinfo [list]
lappend cmdinfo [list dev "?${I}subcommand${NI}?" "(ensemble command to make new projects/modules and\n to generate docs)"]
lappend cmdinfo [list a? "?${I}subcommand${NI}...?" "view ANSI colours\n e.g a? web\n or individual code samples/diagnostics\n e.g a? red bold\n e.g a? underline web-orange Term-purple3"]
lappend cmdinfo [list a+ "?${I}colourcode${NI}...?" "Return ANSI codes\n e.g puts \"\[a+ purple\]purple\[a+ Green\]purple on green\[a\]\"\n [a+ purple]purple[a+ Green]purple on green[a] "]
lappend cmdinfo [list a "?${I}colourcode${NI}...?" "Return ANSI codes (with leading reset)\n e.g puts \"\[a+ purple\]purple\[a Green\]normal on green\[a\]\"\n [a+ purple]purple[a Green]normal on green[a] "]
set t [textblock::class::table new -minwidth 80 -show_seps 0]
foreach row $cmdinfo {
$t add_row $row
}
set width_0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$width_0 + 2}]
set width_1 [$t column_datawidth 1]
$t configure_column 1 -minwidth [expr {$width_1 + 1}]
$t configure -title $title
set text ""
append text [$t print]
lappend chunks [list stdout $text]
# -------------------------------------------------------
}
tcl {
set text "Tcl Patchlevel: [info patchlevel]"
catch {
append text \n "Tcl build-info: [::tcl::build-info]"
}
if {[punk::lib::check::has_tclbug_script_var]} {
append warningblock \n "minor warning: punk::lib::check::has_tclbug_script_var returned true! (string rep for list variable in script generated when script changed)"
}
if {[punk::lib::check::has_tclbug_safeinterp_compile]} {
set indent " "
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_lsearch_strideallinline returned true!" \n
append warningblock "${indent}(lsearch using -stride -all -inline -subindices does not return values corresponding to subindex when a single -index value is used)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/5a1aaa201d]"
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_safeinterp returned true!" \n
append warningblock "${indent}(ensemble commands not compiled in safe interps - heavy performance impact in safe interps)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/1095bf7f75]"
append warningblock [a]
}
}
if {[punk::lib::check::has_tclbug_list_quoting_emptyjoin]} {
set indent " "
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_list_quoting returned true!" \n
append warningblock "${indent}lists elements not properly quoted in some cases. e.g 'list {*}[lindex {etc #foo} 1] {*}[list]' (#foo not braced)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/e38dce74e2]"
}
}
set text ""
if {$topic in [list env environment]} {
#todo - move to punk::config?
upvar ::punk::config::punk_env_vars_config punkenv_config
upvar ::punk::config::other_env_vars_config otherenv_config
set known_punk [dict keys $punkenv_config]
set known_other [dict keys $otherenv_config]
append text \n
set usetable 1
if {$usetable} {
set t [textblock::class::table new -show_hseps 0 -show_header 1 -ansiborder_header [a+ web-green]]
if {"windows" eq $::tcl_platform(platform)} {
#If any env vars have been set to empty string - this is considered a deletion of the variable on windows.
#The Tcl ::env array is linked to the underlying process view of the environment
#- but info exists ::env(var) can misreport as true if it has been deleted by setting to empty string rather than using unset.
#an 'array get' will resynchronise.
#Even if an env variable didn't exist before - setting it to empty string can get it to this inconsistent state.
array get ::env
}
#do an array read on ::env
foreach {v vinfo} $punkenv_config {
if {[info exists ::env($v)]} {
set c2 [set ::env($v)]
} else {
set c2 "(NOT SET)"
}
set help ""
if {[dict exists $vinfo help]} {
set help [dict get $vinfo help]
if {[catch {lsearch -stride 2 {a b} b}]} {
set indent " "
append warningblock \n "[a+ web-red]warning: lsearch does not seem to support -stride option" \n
append warningblock "${indent}(Consider upgrading to a late release of tcl 8.6 or tcl 9+ )" \n
append warningblock [a]
} else {
if {[punk::lib::check::has_tclbug_lsearch_strideallinline]} {
set indent " "
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_lsearch_strideallinline returned true!" \n
append warningblock "${indent}(lsearch using -stride -all -inline -subindices does not return values corresponding to subindex when a single -index value is used)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/5a1aaa201d]"
append warningblock [a]
}
$t add_row [list $v $c2 $help]
}
$t configure_column 0 -headers [list "Punk environment vars"]
$t configure_column 0 -minwidth [expr {[$t column_datawidth 0]+4}] -blockalign left -textalign left -header_colspans {any}
if {[punk::lib::check::has_tclbug_list_quoting_emptyjoin]} {
set indent " "
append warningblock \n "[a+ web-red]warning: punk::lib::check::has_tclbug_list_quoting returned true!" \n
append warningblock "${indent}lists elements not properly quoted in some cases. e.g 'list {*}[lindex {etc #foo} 1] {*}[list]' (#foo not braced)" \n
append warningblock "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/e38dce74e2]"
}
lappend chunks [list stdout $text]
}
env - environment {
set text ""
#todo - move to punk::config?
upvar ::punk::config::punk_env_vars_config punkenv_config
upvar ::punk::config::other_env_vars_config otherenv_config
set known_punk [dict keys $punkenv_config]
set known_other [dict keys $otherenv_config]
append text \n
set usetable 1
if {$usetable} {
set t [textblock::class::table new -show_hseps 0 -show_header 1 -ansiborder_header [a+ web-green]]
if {"windows" eq $::tcl_platform(platform)} {
#If any env vars have been set to empty string - this is considered a deletion of the variable on windows.
#The Tcl ::env array is linked to the underlying process view of the environment
#- but info exists ::env(var) can misreport as true if it has been deleted by setting to empty string rather than using unset.
#an 'array get' will resynchronise.
#Even if an env variable didn't exist before - setting it to empty string can get it to this inconsistent state.
array get ::env
}
#do an array read on ::env
foreach {v vinfo} $punkenv_config {
if {[info exists ::env($v)]} {
set c2 [set ::env($v)]
} else {
set c2 "(NOT SET)"
}
set help ""
if {[dict exists $vinfo help]} {
set help [dict get $vinfo help]
}
$t add_row [list $v $c2 $help]
}
$t configure_column 0 -headers [list "Punk environment vars"]
$t configure_column 0 -minwidth [expr {[$t column_datawidth 0]+4}] -blockalign left -textalign left -header_colspans {any}
set punktable [$t print]
$t destroy
set punktable [$t print]
$t destroy
set t [textblock::class::table new -show_hseps 0 -show_header 1 -ansiborder_header [a+ web-green]]
foreach {v vinfo} $otherenv_config {
if {[info exists ::env($v)]} {
set env_val [set ::env($v)]
if {[string match "*_TM_PATH" $v]} {
set entries [split $env_val $::tcl_platform(pathSeparator)]
set c2 [join $entries \n]
set t [textblock::class::table new -show_hseps 0 -show_header 1 -ansiborder_header [a+ web-green]]
foreach {v vinfo} $otherenv_config {
if {[info exists ::env($v)]} {
set env_val [set ::env($v)]
if {[string match "*_TM_PATH" $v]} {
set entries [split $env_val $::tcl_platform(pathSeparator)]
set c2 [join $entries \n]
} else {
set c2 $::env($v)
}
} else {
set c2 $::env($v)
set c2 "(NOT SET)"
}
} else {
set c2 "(NOT SET)"
$t add_row [list $v $c2]
}
$t add_row [list $v $c2]
}
$t configure_column 0 -headers [list "Other environment vars"]
$t configure_column 0 -minwidth [expr {[$t column_datawidth 0]+4}] -blockalign left -textalign left -header_colspans {any}
$t configure_column 0 -headers [list "Other environment vars"]
$t configure_column 0 -minwidth [expr {[$t column_datawidth 0]+4}] -blockalign left -textalign left -header_colspans {any}
set othertable [$t print]
$t destroy
#append text [textblock::join -- $punktable " " $othertable]\n
append text $punktable\n$othertable\n
} else {
set othertable [$t print]
$t destroy
#append text [textblock::join -- $punktable " " $othertable]\n
append text $punktable\n$othertable\n
} else {
append text $linesep\n
append text "punk environment vars:\n"
append text $linesep\n
set col1 [string repeat " " 25]
set col2 [string repeat " " 50]
foreach v $known_punk {
set c1 [overtype::left $col1 $v]
if {[info exists ::env($v)]} {
set c2 [overtype::left $col2 [set ::env($v)]]
} else {
set c2 [overtype::right $col2 "(NOT SET)"]
}
append text "$c1 $c2\n"
}
append text $linesep\n
}
lappend chunks [list stdout $text]
}
if {$topic in [list console terminal]} {
set indent [string repeat " " [string length "WARNING: "]]
lappend cstring_tests [dict create\
type "PM "\
msg "UN"\
f7 punk::ansi::controlstring_PM\
f7prefix "7bit ESC ^ secret "\
f7suffix "safe"\
f8 punk::ansi::controlstring_PM8\
f8prefix "8bit \\x9e secret "\
f8suffix "safe"\
]
lappend cstring_tests [dict create\
type SOS\
msg "NOT"\
f7 punk::ansi::controlstring_SOS\
f7prefix "7bit ESC X string "\
f7suffix " hidden"\
f8 punk::ansi::controlstring_SOS8\
f8prefix "8bit \\x98 string "\
f8suffix " hidden"\
]
lappend cstring_tests [dict create\
type APC\
msg "NOT"\
f7 punk::ansi::controlstring_APC\
f7prefix "7bit ESC _ APPLICATION PROGRAM COMMAND "\
f7suffix " hidden"\
f8 punk::ansi::controlstring_APC8\
f8prefix "8bit \\x9f APPLICATION PROGRAM COMMAND "\
f8suffix " hidden"\
]
foreach test $cstring_tests {
set m [[dict get $test f7] [dict get $test msg]]
set hidden_width_m [punk::console::test_char_width $m]
set m8 [[dict get $test f8] [dict get $test msg]]
set hidden_width_m8 [punk::console::test_char_width $m8]
if {$hidden_width_m != 0 || $hidden_width_m8 != 0} {
if {$hidden_width_m == 0} {
set d "[a+ green bold][dict get $test f7prefix][a red]${m}[a][a+ green bold][dict get $test f7suffix][a]"
} else {
set d "[a+ yellow bold][dict get $test f7prefix][a red]$m[a][a+ yellow bold][dict get $test f7suffix][a]"
append text $linesep\n
append text "punk environment vars:\n"
append text $linesep\n
set col1 [string repeat " " 25]
set col2 [string repeat " " 50]
foreach v $known_punk {
set c1 [overtype::left $col1 $v]
if {[info exists ::env($v)]} {
set c2 [overtype::left $col2 [set ::env($v)]]
} else {
set c2 [overtype::right $col2 "(NOT SET)"]
}
append text "$c1 $c2\n"
}
append text $linesep\n
}
lappend chunks [list stdout $text]
}
console - term - terminal {
set indent [string repeat " " [string length "WARNING: "]]
lappend cstring_tests [dict create\
type "PM "\
msg "UN"\
f7 punk::ansi::controlstring_PM\
f7prefix "7bit ESC ^ secret "\
f7suffix "safe"\
f8 punk::ansi::controlstring_PM8\
f8prefix "8bit \\x9e secret "\
f8suffix "safe"\
]
lappend cstring_tests [dict create\
type SOS\
msg "NOT"\
f7 punk::ansi::controlstring_SOS\
f7prefix "7bit ESC X string "\
f7suffix " hidden"\
f8 punk::ansi::controlstring_SOS8\
f8prefix "8bit \\x98 string "\
f8suffix " hidden"\
]
lappend cstring_tests [dict create\
type APC\
msg "NOT"\
f7 punk::ansi::controlstring_APC\
f7prefix "7bit ESC _ APPLICATION PROGRAM COMMAND "\
f7suffix " hidden"\
f8 punk::ansi::controlstring_APC8\
f8prefix "8bit \\x9f APPLICATION PROGRAM COMMAND "\
f8suffix " hidden"\
]
foreach test $cstring_tests {
set m [[dict get $test f7] [dict get $test msg]]
set hidden_width_m [punk::console::test_char_width $m]
set m8 [[dict get $test f8] [dict get $test msg]]
set hidden_width_m8 [punk::console::test_char_width $m8]
if {$hidden_width_m != 0 || $hidden_width_m8 != 0} {
if {$hidden_width_m == 0} {
set d "[a+ green bold][dict get $test f7prefix][a red]${m}[a][a+ green bold][dict get $test f7suffix][a]"
} else {
set d "[a+ yellow bold][dict get $test f7prefix][a red]$m[a][a+ yellow bold][dict get $test f7suffix][a]"
}
if {$hidden_width_m8 == 0} {
set d8 "[a+ green ][dict get $test f8prefix][a red]$m8[a][a+ green][dict get $test f8suffix][a]"
} else {
set d8 "[a+ yellow bold][dict get $test f8prefix][a red]$m8[a][a+ yellow bold][dict get $test f8suffix][a]"
}
append warningblock \n "WARNING: terminal doesn't hide all [dict get $test type] control strings: $d $d8"
}
if {$hidden_width_m8 == 0} {
set d8 "[a+ green ][dict get $test f8prefix][a red]$m8[a][a+ green][dict get $test f8suffix][a]"
} else {
set d8 "[a+ yellow bold][dict get $test f8prefix][a red]$m8[a][a+ yellow bold][dict get $test f8suffix][a]"
}
if {![catch {punk::console::check::has_bug_legacysymbolwidth} result]} {
if {$result} {
append warningblock \n "WARNING: terminal has legacysymbolwidth bug - screen position for symbol reports 2 wide but displays 1 wide."
append warningblock \n $indent "Layout using 'legacy symbols for computing' affected."
append warningblock \n $indent "(e.g textblock frametype block2 unsupported)"
append warningblock \n $indent "This can cause extreme layout deformation when ANSI is present"
append warningblock \n $indent "In some cases unwanted spacing effects occur at a distance from the characters causing it"
}
append warningblock \n "WARNING: terminal doesn't hide all [dict get $test type] control strings: $d $d8"
} else {
append warningblock \n "WARNING: terminal unable to check for legacysymbolwidth bug. err:$result"
}
}
if {![catch {punk::console::check::has_bug_legacysymbolwidth} result]} {
if {$result} {
append warningblock \n "WARNING: terminal has legacysymbolwidth bug - screen position for symbol reports 2 wide but displays 1 wide."
append warningblock \n $indent "Layout using 'legacy symbols for computing' affected."
append warningblock \n $indent "(e.g textblock frametype block2 unsupported)"
append warningblock \n $indent "This can cause extreme layout deformation when ANSI is present"
append warningblock \n $indent "In some cases unwanted spacing effects occur at a distance from the characters causing it"
if {![catch {punk::console::check::has_bug_zwsp} result]} {
if {$result} {
append warningblock \n "WARNING: terminal has zero width space (\\u200b) bug - cursor position incremented when it shouldn't be."
append warningblock \n $indent "The zwsp may or may not be displayed. zwsp contributes to line length and wrapping point"
}
} else {
append warningblock \n "WARNING: terminal unable to check for zwsp bug. err:$result"
}
} else {
append warningblock \n "WARNING: terminal unable to check for legacysymbolwidth bug. err:$result"
}
if {![catch {punk::console::check::has_bug_zwsp} result]} {
if {$result} {
append warningblock \n "WARNING: terminal has zero width space (\\u200b) bug - cursor position incremented when it shouldn't be."
append warningblock \n $indent "The zwsp may or may not be displayed. zwsp contributes to line length and wrapping point"
set grapheme_support [punk::console::grapheme_cluster_support]
#mode, 1 = set, 2 = unset. (0 = mode not recognised, 3 = permanently set, 4 = permanently unset)
if {![dict size $grapheme_support] || [dict get $grapheme_support mode] eq "unsupported" } {
append warningblock \n "WARNING: terminal either doesn't support grapheme clusters, or doesn't report so via decmode 2027 query."
if {[dict size $grapheme_support] && [dict get $grapheme_support available]} {
append warningblock \n $indent "(but punk::console::grapheme_cluster_support has determined it is probably available)"
}
} else {
if {![dict get $grapheme_support available]} {
switch -- [dict get $grapheme_support mode] {
"unset" {
append warningblock \n "WARNING: terminal reports via decmode 2027 that grapheme cluster support is off."
}
"permanently_unset" {
append warningblock \n "WARNING: terminal reports via decmode 2027 that grapheme cluster support is permanently off."
}
"BAD_RESPONSE" {
append warningblock \n "WARNING: terminal doesn't seem to recognize decmode 2027 query. No grapheme cluster support."
}
}
}
}
if {![string length $warningblock]} {
set text "No terminal warnings\n"
lappend chunks [list stdout $text]
}
} else {
append warningblock \n "WARNING: terminal unable to check for zwsp bug. err:$result"
}
topics - help {
set text ""
set topics [dict create\
"topics|help" "List help topics"\
"tcl" "Tcl version warnings"\
"env|environment" "punkshell environment vars"\
"console|terminal" "Some console behaviour tests and warnings"\
]
set grapheme_support [punk::console::grapheme_cluster_support]
#mode, 1 = set, 2 = unset. (0 = mode not recognised, 3 = permanently set, 4 = permanently unset)
if {![dict size $grapheme_support] || [dict get $grapheme_support mode] eq "unsupported" } {
append warningblock \n "WARNING: terminal either doesn't support grapheme clusters, or doesn't report so via decmode 2027 query."
if {[dict size $grapheme_support] && [dict get $grapheme_support available]} {
append warningblock \n $indent "(but punk::console::grapheme_cluster_support has determined it is probably available)"
set t [textblock::class::table new -show_seps 0]
$t add_column -headers [list "Topic"]
$t add_column
foreach {k v} $topics {
$t add_row [list $k $v]
}
} else {
if {![dict get $grapheme_support available]} {
switch -- [dict get $grapheme_support mode] {
"unset" {
append warningblock \n "WARNING: terminal reports via decmode 2027 that grapheme cluster support is off."
}
"permanently_unset" {
append warningblock \n "WARNING: terminal reports via decmode 2027 that grapheme cluster support is permanently off."
}
"BAD_RESPONSE" {
append warningblock \n "WARNING: terminal doesn't seem to recognize decmode 2027 query. No grapheme cluster support."
set widest0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$widest0 + 4}]
append text \n [$t print]
lappend chunks [list stdout $text]
}
default {
set text ""
set cinfo [uplevel 1 [list punk::ns::cmdwhich [lindex $topicparts 0]]]
set wtype [dict get $cinfo whichtype]
if {$wtype eq "notfound"} {
set externalinfo [auto_execok [lindex $topicparts 0]]
if {[string length $externalinfo]} {
set text "$topicparts"
append text \n "Base type: External command"
append text \n "$externalinfo [lrange $topicparts 1 end]"
} else {
set text "$topicparts\n"
append text "No matching internal or external command found"
}
} else {
set text "[dict get $cinfo which] [lrange $topicparts 1 end]"
append text \n "Base type: $wtype"
set synopsis [uplevel 1 [list punk::ns::synopsis {*}$topicparts]]
set synshow ""
foreach sline [split $synopsis \n] {
if {[regexp {\s*#.*} $sline]} {
append synshow [punk::ansi::a+ bold term-darkgreen Term-white]$sline[punk::ansi::a] \n
} else {
append synshow $sline \n
}
}
if {[string index $synshow end] eq "\n"} {
set synshow [string range $synshow 0 end-1]
}
append text \n $synshow
}
lappend chunks [list stdout $text]
}
}
lappend chunks [list stderr $warningblock]
if {$topic in [list topics help]} {
set text ""
set topics [dict create\
"topics|help" "List help topics"\
"tcl" "Tcl version warnings"\
"env|environment" "punkshell environment vars"\
"console|terminal" "Some console behaviour tests and warnings"\
]
set t [textblock::class::table new -show_seps 0]
$t add_column -headers [list "Topic"]
$t add_column
foreach {k v} $topics {
$t add_row [list $k $v]
}
set widest0 [$t column_datawidth 0]
$t configure_column 0 -minwidth [expr {$widest0 + 4}]
append text \n[$t print]
lappend chunks [list stdout $text]
}
lappend chunks [list stderr $warningblock]
return $chunks
}
proc help {args} {
set chunks [help_chunks {*}$args]
foreach chunk $chunks {
lassign $chunk chan text
puts -nonewline $chan $text
}
}
proc mode {{raw_or_line query}} {
package require punk::console
tailcall ::punk::console::mode $raw_or_line

177
src/vfs/_vfscommon.vfs/modules/punk/ansi-0.1.1.tm

@ -340,6 +340,28 @@ tcl::namespace::eval punk::ansi::class {
}
}
tcl::namespace::eval punk::ansi {
namespace eval argdoc {
variable PUNKARGS
# -- --- --- --- ---
#non colour SGR codes
# we can use these directly via ${$I} etc without marking a definition with @dynamic
#This is because they don't need to change when colour switched on and off.
#chicken/egg - need to use literals here
set I "\x1b\[3m" ;# [a+ italic]
set NI "\x1b\[23m" ;# [a+ noitalic]
set B "\x1b\[1m" ;# [a+ bold]
set N "\x1b\[22m" ;# [a+ normal]
set T "\x1b\[1\;4m" ;# [a+ bold underline]
set NT "\x1b\[22\;24m\x1b\[4:0m" ;# [a+ normal nounderline]
set LC \u007b ;#left curly brace
set RC \u007d ;#right curly brace
# -- --- --- --- ---
#namespace import ::punk::args::helpers::*
}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
tcl::namespace::eval punk::ansi {
@ -2262,59 +2284,74 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
return $displaytable
}
lappend PUNKARGS [list {
@id -id ::punk::ansi::a?
@cmd -name "punk::ansi::a?"\
-summary\
"ANSI colour information"\
-help\
""
@form -form "sgr_overview"
@values -form "sgr_overview" -min 0 -max 0
@form -form "term"
@leaders -form "term" -min 1 -max 1
term -type literal(term) -help\
"256 term colours"
@opts -min 0 -max 0
@values -form "term" -min 0 -max -1
panel -type string -optional 1 -multiple 1 -default {16 main greyscale note}\
-choices {16 main greyscale pastel rainbow note}
@form -form "tk"
@leaders -form "tk" -min 1 -max 1
tk -type literal(tk)|literal(TK) -help\
"Tk colours"
@opts -form "tk"
-merged -type none -help\
"If this flag is supplied - show colour names with the same RGB
values together."
@values -form "tk" -min 0 -max -1
glob -type string -optional 1 -multiple 1 -help\
"A glob string such as *green*.
Multiple glob entries can be provided. The search is case insensitive"
@form -form "web"
@values -form "web" -min 1 -max -1
web -type literal(web) -help\
"Web colours"
panel -type string -optional 1 -multiple 1 -choices {basic pink red orange yellow brown purple blue cyan green white gray}
@form -form "x11"
@values -form "x11" -min 1 -max 1
x11 -type literal(x11) -help\
"x11 colours"
@form -form "sample"
@values -form "sample" -min 1 -max -1
colourcode -type sgr|Sgr|literalprefix(term-)|literalprefix(Term-)|literalprefix(web-)|literalprefix(Web-)|literalprefix(rgb)|literalprefix(Rgb)\
-optional 0\
-multiple 1
}]
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::ansi::a?
@cmd -name "punk::ansi::a?"\
-summary\
"ANSI colour information"\
-help\
"With no arguments - display an overview information panel.
With the first argument one of:
${$B}term tk TK web x11${$N}
Display a more specific panel of colour information.
With arguments of individual colourcodes from any of the above
sets, display a small diagnostic table showing a sample of
the individual and combined effect(s), along with indications
of the raw ANSI codes."
@form -form "sgr_overview"
@values -form "sgr_overview" -min 0 -max 0
@form -form "term"
@leaders -form "term" -min 1 -max 1
term -type literal(term) -help\
"256 term colours"
@opts -min 0 -max 0
@values -form "term" -min 0 -max -1
panel -type string -optional 1 -multiple 1 -default {16 main greyscale note}\
-choices {16 main greyscale pastel rainbow note}
@form -form "tk"
@leaders -form "tk" -min 1 -max 1
tk -type literal(tk)|literal(TK) -help\
"Tk colours"
@opts -form "tk"
-merged -type none -help\
"If this flag is supplied - show colour names with the same RGB
values together."
@values -form "tk" -min 0 -max -1
glob -type string -optional 1 -multiple 1 -help\
"A glob string such as *green*.
Multiple glob entries can be provided. The search is case insensitive"
@form -form "web"
@values -form "web" -min 1 -max -1
web -type literal(web) -help\
"Web colours"
panel -type string -optional 1 -multiple 1 -choices {basic pink red orange yellow brown purple blue cyan green white gray}
@form -form "x11"
@values -form "x11" -min 1 -max 1
x11 -type literal(x11) -help\
"x11 colours"
@form -form "sample"
@values -form "sample" -min 1 -max -1
#review literalprefix is not the right thing here! (literalprefix means a tcl::prefix::match on the value)
#colourcode -type sgr|Sgr|stringstartswith(term-)|stringstartswith(Term-)|stringstartswith(web-)|stringstartswith(Web-)|stringstartswith(rgb)|stringstartswith(Rgb)
colourcode -type string|stringstartswith(term-)|stringstartswith(Term-)|stringstartswith(web-)|stringstartswith(Web-)|stringstartswith(rgb)|stringstartswith(Rgb)\
-typesynopsis {${$I}sgr${$NI}|${$I}Sgr${$NI}|${$I}colourcode${$NI}}\
-optional 0\
-multiple 1
}]
}
proc a? {args} {
#*** !doctools
#[call [fun a?] [opt {ansicode...}]]
@ -2414,8 +2451,8 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
append out \n
append out "[a+ {*}$fc web-white]Combination testing[a]" \n
append out [textblock::join -- $indent "Example: a? red brightgreen underline Tk-slategrey italic"] \n
append out [textblock::join -- $indent "This will show a small table of each applied code and a RESULT row. The 'red' in this case is redundant,"] \n
append out [textblock::join -- $indent "so a final MERGED row displays with an alert 'REDUNDANCIES FOUND'."] \n
append out [textblock::join -- $indent "This will show a small table of each applied code and a RESULT row. The 'red' in this case is redundant, because there are"] \n
append out [textblock::join -- $indent "two foreground colours (lowercase first letter) so a final MERGED row displays with an alert 'REDUNDANCIES FOUND'."] \n
append out [textblock::join -- $indent "The final columns of RESULT and MERGED (showing raw ANSI sequence) will differ if the arguments aren't in canonical order."] \n
append out [textblock::join -- $indent "The MERGED line will only display if there are redundancies or different ordering."] \n
@ -2674,14 +2711,26 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
} else {
set tkcolours [list]
}
foreach c $webcolours {
append info \n web-$c
}
foreach c $x11colours {
append info \n x11-$c
}
foreach c $tkcolours {
append info \n tk-$c
if {[string is upper -strict [string index $pfx 0]]} {
foreach c $webcolours {
append info \n Web-$c
}
foreach c $x11colours {
append info \n X11-$c
}
foreach c $tkcolours {
append info \n Tk-$c
}
} else {
foreach c $webcolours {
append info \n web-$c
}
foreach c $x11colours {
append info \n x11-$c
}
foreach c $tkcolours {
append info \n tk-$c
}
}
$t add_row [list $i "$info" $s [ansistring VIEW $s]]
}
@ -8723,7 +8772,7 @@ interp alias {} ansistring {} ::punk::ansi::ansistring
namespace eval ::punk::args::register {
#use fully qualified so 8.6 doesn't find existing var in global namespace
lappend ::punk::args::register::NAMESPACES ::punk::ansi ::punk::ansi::class ::punk::ansi::ta
lappend ::punk::args::register::NAMESPACES ::punk::ansi ::punk::ansi::argdoc ::punk::ansi::class ::punk::ansi::ta
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++

153
src/vfs/_vfscommon.vfs/modules/punk/args-0.2.1.tm

@ -359,16 +359,22 @@ tcl::namespace::eval ::punk::args::helpers {
#puts $str
#puts stderr -------------------
#rudimentary colourising (not full tcl syntax parsing)
#highlight comments first - so that we can also highlight braces within comments to help with detecting unbalanced braces/square brackets in comments
switch -- $opt_syntax {
tcl {
#rudimentary colourising (not full tcl syntax parsing)
#Note that this can highlight ;# in some places as a comment where it's not appropriate
# e.g inside a regexp
#highlight comments first - so that we can also highlight braces within comments to help with detecting unbalanced braces/square brackets in comments
#result lines often indicated in examples by \u2192 →
#however - it's not present on each line of output, instead indents are used - so we can't so easily highlight all appropriate rows(?)
set str [punk::grepstr -return all -highlight {Term-grey term-darkgreen} {^\s*#.*} $str] ;#Note, will not highlight comments at end of line - like this one
set str [punk::grepstr -return all -highlight {Term-grey term-darkgreen} {;\s*(#.*)} $str]
#TODO - fix grepstr highlighting (bg issues - why?)
set str [punk::grepstr -return all -highlight {Term-grey term-darkblue} {\{|\}} $str]
#Note that if we were to highlight based on the regexp {\{|\}} then the inserted ansi would come between
# the backslash and brace in \{ or \} - this breaks the syntactic structure causing problems.
set str [punk::grepstr -return all -highlight {Term-grey tk-darkblue} {^\{|[^\\](\{+)} $str]
set str [punk::grepstr -return all -highlight {Term-grey tk-darkblue} {[^\\](\}+)} $str]
set str [punk::grepstr -return all -highlight {Term-grey term-orange1} {\[|\]} $str]
#puts stderr -------------------
#puts $str
@ -460,7 +466,7 @@ tcl::namespace::eval ::punk::args::helpers {
tcl::namespace::eval punk::args {
package require punk::assertion
#if 'package forget' was called on this package (e.g when loading test::punk::args) then assert may already exist in the namespace
#procs can be overridden silently, but not imports
#namespace import will fail if target exists
catch {
namespace import ::punk::assertion::assert
}
@ -469,9 +475,9 @@ tcl::namespace::eval punk::args {
variable PUNKARGS ;#list of our own punk::args function argument definitions - loaded with punk::args::define at the end.
tcl::namespace::export {[a-z]*}
variable rawdef_cache
if {![info exists rawdef_cache]} {
set rawdef_cache [tcl::dict::create] ;#key on rawdef list - return dict of {-id <id> -dynamic 0|1}
variable rawdef_cache_about
if {![info exists rawdef_cache_about]} {
set rawdef_cache_about [tcl::dict::create] ;#key on rawdef list - return dict of {-id <id> -dynamic 0|1}
}
variable id_cache_rawdef
if {![info exists id_cache_rawdef]} {
@ -487,9 +493,9 @@ tcl::namespace::eval punk::args {
set argdefcache_unresolved [tcl::dict::create] ;#key = original template list supplied to 'define', value = 2-element list: (tstr-parsed split of template) & (unresolved params)
}
variable argdata_cache
if {![info exists argdata_cache]} {
set argdata_cache [tcl::dict::create]
variable rawdef_cache_argdata
if {![info exists rawdef_cache_argdata]} {
set rawdef_cache_argdata [tcl::dict::create]
}
variable id_counter
@ -979,11 +985,11 @@ tcl::namespace::eval punk::args {
error todo
}
proc define {args} {
variable rawdef_cache
variable rawdef_cache_about
variable id_cache_rawdef
variable argdata_cache
if {[dict exists $rawdef_cache $args]} {
return [dict get [dict get $rawdef_cache $args] -id]
#variable rawdef_cache_argdata
if {[dict exists $rawdef_cache_about $args]} {
return [dict get [dict get $rawdef_cache_about $args] -id]
} else {
set lvl 2
set id [rawdef_id $args $lvl]
@ -991,46 +997,40 @@ tcl::namespace::eval punk::args {
#we seem to be re-creating a previously defined id...
#clear any existing caches for this id
undefine $id 0
##dict unset argdata_cache $prevraw ;#silently does nothing if key not present
#dict for {k v} $argdata_cache {
# if {[dict get $v id] eq $id} {
# dict unset argdata_cache $k
# }
#}
#dict for {k v} $rawdef_cache {
# if {[dict get $v -id] eq $id} {
# dict unset rawdef_cache $k
# }
#}
#dict unset id_cache_rawdef $id
}
set is_dynamic [rawdef_is_dynamic $args]
set defspace [uplevel 1 {::namespace current}]
dict set rawdef_cache $args [dict create -id $id -dynamic $is_dynamic -defspace $defspace]
dict set rawdef_cache_about $args [dict create -id $id -dynamic $is_dynamic -defspace $defspace]
dict set id_cache_rawdef $id $args
return $id
}
}
proc undefine {id {quiet 0}} {
variable rawdef_cache
#review - alias?
variable rawdef_cache_about
variable id_cache_rawdef
variable argdata_cache
variable rawdef_cache_argdata
if {[id_exists $id]} {
if {!$quiet} {
puts stderr "punk::args::undefine clearing existing data for id:$id"
}
dict for {k v} $argdata_cache {
if {[dict get $v id] eq $id} {
dict unset argdata_cache $k
if {[dict exists $id_cache_rawdef $id]} {
set deflist [dict get $id_cache_rawdef $id]
dict unset rawdef_cache_about $deflist
dict unset rawdef_cache_argdata $deflist
dict unset id_cache_rawdef $id
} else {
dict for {k v} $rawdef_cache_argdata {
if {[dict get $v id] eq $id} {
dict unset rawdef_cache_argdata $k
}
}
}
dict for {k v} $rawdef_cache {
if {[dict get $v -id] eq $id} {
dict unset rawdef_cache $k
dict for {k v} $rawdef_cache_about {
if {[dict get $v -id] eq $id} {
dict unset rawdef_cache_about $k
}
}
}
dict unset id_cache_rawdef $id
} else {
if {!$quiet} {
puts stderr "punk::args::undefine unable to find id: '$id'"
@ -1039,17 +1039,26 @@ tcl::namespace::eval punk::args {
}
#'punk::args::parse $args withdef $deflist' can raise parsing error after an autoid was generated
# In this case we don't see the autoid in order to delete it
#proc undefine_deflist {deflist} {
#}
proc undefine_deflist {deflist} {
variable rawdef_cache_about
variable id_cache_rawdef
variable rawdef_cache_argdata
if {[dict exists $rawdef_cache_about $deflist -id]} {
set id [dict get $rawdef_cache_about $deflist -id]
dict unset rawdef_cache_about $deflist
dict unset rawdef_cache_argdata $deflist
dict unset id_cache_rawdef $id
}
}
proc idquery_info {id} {
variable id_cache_rawdef
variable rawdef_cache
variable rawdef_cache_about
if {[dict exists $id_cache_rawdef $id]} {
set sep [string repeat - 40]
set rawdef [dict get $id_cache_rawdef $id]
if {[dict exists $rawdef_cache $rawdef]} {
set idinfo [dict get $rawdef_cache $rawdef]
if {[dict exists $rawdef_cache_about $rawdef]} {
set idinfo [dict get $rawdef_cache_about $rawdef]
} else {
set idinfo ""
}
@ -1060,11 +1069,11 @@ tcl::namespace::eval punk::args {
append result \n "id info:"
append result \n $idinfo
append result \n $sep
variable argdata_cache
variable rawdef_cache_argdata
#lsearch -stride not avail (or buggy) in some 8.6 interps - search manually for now (2025). todo - modernize some time after Tcl 9.0/9.1 more widespread.(2027?)
#check for and report if id is present multiple times
set argdata_records [list]
dict for {k v} $argdata_cache {
dict for {k v} $rawdef_cache_argdata {
if {[dict get $v id] eq $id} {
if {$k eq $rawdef} {
lappend argdata_records [list 1 $k $v]
@ -1100,12 +1109,20 @@ tcl::namespace::eval punk::args {
dict get [resolve {*}$args] id
}
lappend PUNKARGS [list {
@id -id ::punk::args::resolve
@cmd -name punk::args::resolve -help\
""
@values -min 0 -max -1
arg -type any -multiple 1 -help\
"rawdef line block"
}]
proc resolve {args} {
variable rawdef_cache
variable rawdef_cache_about
variable id_cache_rawdef
set defspace ""
if {[dict exists $rawdef_cache $args]} {
set cinfo [dict get $rawdef_cache $args]
if {[dict exists $rawdef_cache_about $args]} {
set cinfo [dict get $rawdef_cache_about $args]
set id [dict get $cinfo -id]
set is_dynamic [dict get $cinfo -dynamic]
if {[dict exists $cinfo -defspace]} {
@ -1116,12 +1133,13 @@ tcl::namespace::eval punk::args {
set id [rawdef_id $args]
puts stderr "Warning: punk::args::resolve called with undefined id:$id"
set is_dynamic [rawdef_is_dynamic $args]
dict set rawdef_cache $args [dict create -id $id -dynamic $is_dynamic]
#-defspace ???
dict set rawdef_cache_about $args [dict create -id $id -dynamic $is_dynamic]
dict set id_cache_rawdef $id $args
}
variable argdata_cache
variable rawdef_cache_argdata
variable argdefcache_unresolved
@ -1155,8 +1173,8 @@ tcl::namespace::eval punk::args {
if {!$is_dynamic} {
#todo - don't use cached version if 'colour off' vs 'colour on' different to when last resolved!
#(e.g example blocks will still have colour if previously resolved)
if {[tcl::dict::exists $argdata_cache $cache_key]} {
return [tcl::dict::get $argdata_cache $cache_key]
if {[tcl::dict::exists $rawdef_cache_argdata $cache_key]} {
return [tcl::dict::get $rawdef_cache_argdata $cache_key]
}
set normargs [list]
foreach a $textargs {
@ -1235,10 +1253,10 @@ tcl::namespace::eval punk::args {
tcl::dict::set argdefcache_unresolved $cache_key $pt_params
}
}
#argdata_cache should be limited in some fashion or will be a big memory leak???
if {[tcl::dict::exists $argdata_cache [list $optionspecs]]} {
#rawdef_cache_argdata should be limited in some fashion or will be a big memory leak???
if {[tcl::dict::exists $rawdef_cache_argdata [list $optionspecs]]} {
#resolved cache version exists
return [tcl::dict::get $argdata_cache [list $optionspecs]]
return [tcl::dict::get $rawdef_cache_argdata [list $optionspecs]]
}
}
@ -2669,10 +2687,12 @@ tcl::namespace::eval punk::args {
form_info $form_info\
]
tcl::dict::set argdata_cache $cache_key $argdata_dict
#REVIEW
tcl::dict::set rawdef_cache_argdata $cache_key $argdata_dict
if {$is_dynamic} {
#also cache resolved version
tcl::dict::set argdata_cache [list $optionspecs] $argdata_dict
tcl::dict::set rawdef_cache_argdata [list $optionspecs] $argdata_dict
}
#tcl::dict::set id_cache_rawdef $DEF_definition_id $args
@ -3094,10 +3114,10 @@ tcl::namespace::eval punk::args {
}
proc is_dynamic {id} {
variable id_cache_rawdef
variable rawdef_cache
variable rawdef_cache_about
set deflist [raw_def $id]
if {[dict exists $rawdef_cache $deflist -dynamic]} {
return [dict get $rawdef_cache $deflist -dynamic]
if {[dict exists $rawdef_cache_about $deflist -dynamic]} {
return [dict get $rawdef_cache_about $deflist -dynamic]
}
return [rawdef_is_dynamic $deflist]
#@dynamic only has meaning as 1st element of a def in the deflist
@ -4008,7 +4028,16 @@ tcl::namespace::eval punk::args {
}
if {$synopsis ne ""} {
if {$use_table} {
$t configure_header $h -colspans $arg_colspans -values [list Synopsis: [punk::ansi::ansiwrap brightwhite $synopsis]]
set form_names [dict get $spec_dict form_names]
set synhelp "Synopsis:"
if {[llength $form_names] > 1} {
set fn 0
foreach fname $form_names {
append synhelp \n " i -form $fn \U2026"
incr fn
}
}
$t configure_header $h -colspans $arg_colspans -values [list $synhelp [punk::ansi::ansiwrap brightwhite $synopsis]]
} else {
#todo
lappend errlines "Synopsis:\n$synopsis"

1550
src/vfs/_vfscommon.vfs/modules/punk/args/moduledoc/tclcore-0.1.0.tm

File diff suppressed because it is too large Load Diff

315
src/vfs/_vfscommon.vfs/modules/punk/args/moduledoc/tkcore-0.1.1.tm

@ -111,6 +111,8 @@ tcl::namespace::eval punk::args::moduledoc::tkcore {
set NI [a+ noitalic]
set B [a+ bold]
set N [a+ normal]
set T [a+ bold underline]
set NT [a+ normal nounderline]
# -- --- --- --- ---
namespace import ::punk::args::helpers::*
@ -512,6 +514,7 @@ tcl::namespace::eval punk::args::moduledoc::tkcore {
punk::args::define {
@id -id (widgetcommand)Class_Button
# ?? (instance)Class_Button ??
@cmd -name "Tk widget: (widgetcommand)Class_Button"\
-summary\
"widgetcommand for Tk class Button"\
@ -531,6 +534,318 @@ tcl::namespace::eval punk::args::moduledoc::tkcore {
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
# ::wm subcommands
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
punk::args::define {
@id -id "::wm aspect"
@cmd -name "Tk Built-in: ::wm aspect"\
-summary\
"Get/set aspect ratio restrictions."\
-help\
"If ${$I}minNumer${$NI}, ${$I}minDenom${$NI}, ${$I}maxNumer${$NI}, and ${$I}maxDenom${$NI} are all specified, then they will be passed to
the window manager and the window manager should use them to enforce a range of acceptable
aspect ratios for window. The aspect ratio of window (width/length) will be constrained to lie
between ${$I}minNumer${$NI}/${$I}minDenom${$NI} and ${$I}maxNumer${$NI}/${$I}maxDenom${$NI}. If ${$I}minNumer${$NI} etc. are all specified as empty
strings, then any existing aspect ratio restrictions are removed. If ${$I}minNumer${$NI} etc. are
specified, then the command returns an empty string. Otherwise, it returns a Tcl list
containing four elements, which are the current values of ${$I}minNumer${$NI}, ${$I}minDenom${$NI}, ${$I}maxNumer${$NI}, and
${$I}maxDenom${$NI} (if no aspect restrictions are in effect, then an empty string is returned)."
@values -min 1
window -type string
#todo - punk::args - way to specify all number or all empty e.g
# -type {(number number number number)|(literal() literal() literal() literal())}
aspectratio\
-type {number|literal() number|literal() number|literal() number|literal()}\
-typesynopsis "${$I}minNumer${$NI} ${$I}minDenom${$NI} ${$I}maxNumer${$NI} ${$I}maxDenom${$NI}"\
-optional 1
} "@doc -name Manpage: -url [manpage wm]"
#attributes
punk::args::define {
@id -id "::wm client"
@cmd -name "Tk Built-in: ::wm client"\
-summary\
"Get/set WM_CLIENT_MACHINE name for the window."\
-help\
"If ${$I}name${$NI} is specified, this command stores ${$I}name${$NI} (which should be the name of the host on which the application
is executing) in ${$I}window${$NI}'s ${$B}WM_CLIENT_MACHINE${$N} property for use by the window manager or session manager. The
command returns an empty string in this case. If ${$I}name${$NI} is not specified, the command returns the last name set
in a wm client command for window. If ${$I}name${$NI} is specified as an empty string, the command deletes the
${$B}WM_CLIENT_MACHINE${$N} property from ${$I}window${$NI}."
@values -min 1 -max 2
window -type string
name\
-type string\
-optional 1
} "@doc -name Manpage: -url [manpage wm]"
#colormapwindows
#command
#deiconify
#focusmodel
punk::args::define {
@id -id "::wm forget"
@cmd -name "Tk Built-in: ::wm forget"\
-summary\
"Unmap/Unmanage window."\
-help\
"The window will be unmapped from the screen and will no longer be managed by ${$B}wm${$N}. Windows created with the
${$B}toplevel${$N} command will be treated like frame windows once they are no longer managed by ${$B}wm${$N}, however, the
${$B}-menu${$N} configuration will be remembered and the menus will return once the widget is managed again."
@values -min 1 -max 1
window -type string
} "@doc -name Manpage: -url [manpage wm]"
punk::args::define {
@id -id "::wm frame"
@cmd -name "Tk Built-in: ::wm frame"\
-summary\
"Identifier of outermost frame containing window."\
-help\
"If ${$I}window${$NI} has been reparented by the window manager into a decorative frame, the command returns the
platform specific window identifier for the outermost frame that contains ${$I}window${$NI} (the window whose parent
is the root or virtual root). If ${$I}window${$NI} has not been reparented by the window manager then the command
returns the platform specific window identifier for ${$I}window${$NI}."
@values -min 1 -max 1
window -type string
} "@doc -name Manpage: -url [manpage wm]"
#geometry
#grid
#group
#iconbadge
#iconbitmap
#iconify
#iconmask
#iconname
#iconphoto
#iconposition
#iconwindow
punk::args::define {
@id -id "::wm manage"
@cmd -name "Tk Built-in: ::wm manage"\
-summary\
"Make frame/labelframe a toplevel."\
-help\
"The ${$I}widget${$NI} specified will become a stand alone top-level window. The window will be decorated with the window
managers title bar, etc. Only frame, labelframe and toplevel widgets can be used with this command. Attempting
to pass any other widget type will raise an error. Attempting to manage a toplevel widget is benign and achieves
nothing. See also ${$B}GEOMETRY MANAGEMENT${$N}."
@values -min 1 -max 1
widget -type string -help\
"frame, labelframe or toplevel"
} "@doc -name Manpage: -url [manpage wm]"
#maxsize
#minsize
#overrideredirect
#positionfrom
#protocol
punk::args::define {
@id -id "::wm resizable"
@cmd -name "Tk Built-in: ::wm resizable"\
-summary\
"Get/Set window width and height resizability."\
-help\
"This command controls whether or not the user may interactively resize a top-level window. If ${$I}width${$NI} and ${$I}height${$NI} are
specified, they are boolean values that determine whether the width and height of window may be modified by the user.
In this case the command returns an empty string. If ${$I}width${$NI} and ${$I}height${$NI} are omitted then the command returns a list
with two 0/1 elements that indicate whether the width and height of window are currently resizable. By default,
windows are resizable in both dimensions. If resizing is disabled, then the window's size will be the size from the
most recent interactive resize or ${$B}wm geometry${$N} command. If there has been no such operation then the window's natural
size will be used."
@values -min 1
window -type string
width_height\
-type {boolean boolean}\
-typesynopsis "${$I}width${$NI} ${$I}height${$NI}"\
-optional 1
} "@doc -name Manpage: -url [manpage wm]"
#sizefrom
#stackorder
punk::args::define {
@id -id "::wm state"
@cmd -name "Tk Built-in: ::wm state"\
-summary\
"Get/set window state."\
-help\
"If newstate is specified, the window will be set to the new state, otherwise it returns the current state of window:
either ${$B}normal${$N}, ${$B}iconic${$N}, ${$B}withdrawn${$N}, ${$B}icon${$N}, or (Windows and macOS only) ${$B}zoomed${$N}. The difference between ${$B}iconic${$N} and ${$B}icon${$N} is
that ${$B}iconic${$N} refers to a window that has been iconified (e.g., with the wm iconify command) while ${$B}icon${$N} refers to a
window whose only purpose is to serve as the icon for some other window (via the ${$B}wm iconwindow${$N} command).
The ${$B}icon${$N} state cannot be set."
@values -min 1 -max 2
window -type string
newstate\
-type string\
-optional 1\
-choices {normal iconic withdrawn zoomed}\
-choicelabels {\
normal\
""
iconic\
""
withdrawn\
""
zoomed\
" (Windows and macOS only)"
}
} "@doc -name Manpage: -url [manpage wm]"
#title
#transient
#withdraw
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
# ::wm
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
set WM_CHOICES [list aspect attributes client colormapwindows command deiconify focusmodel forget frame\
geometry grid group iconbadge iconbitmap iconify iconmask iconname iconphoto iconposition iconwindow\
manage maxsize minsize overrideredirect positionfrom protocol resizable sizefrom stackorder\
state title transient withdraw\
]
#manual synopses for subcommands not yet defined
set WM_CHOICELABELS [subst -novariables {
}]
set WM_CHOICEGROUPS [dict create\
"" {}\
icon {iconbadge iconbitmap iconmask iconphoto iconposition iconwindow}\
]
set WM_GROUPALLOCATED [list]
dict for {g glist} $WM_CHOICEGROUPS {
lappend WM_GROUPALLOCATED {*}$glist
}
foreach sub $WM_CHOICES {
if {$sub ni $WM_GROUPALLOCATED} {
dict lappend WM_CHOICEGROUPS "" $sub
}
}
set WM_CHOICEINFO [dict create]
foreach sub $WM_CHOICES {
#default for all
dict set WM_CHOICEINFO $sub {{doctype native}}
}
foreach id [punk::args::get_ids "::wm *"] {
if {[llength $id] == 2} {
lassign $id _ sub
dict set WM_CHOICEINFO $sub {{doctype native} {doctype punkargs}}
#override manual synopsis entry
#puts stderr "override manual synopsis entry with [punk::ns::synopsis "::wm $sub"]"
dict set WM_CHOICELABELS $sub [punk::ansi::a+ normal][punk::args::synopsis "::wm $sub"]
}
}
punk::args::define {
@id -id ::wm
@cmd -name "Tk Built-in: ::wm"\
-summary\
"Communicate with window manager."\
-help\
"The ${$B}wm${$N} command is used to interact with window managers in order to control such things as the
title for a window, its geometry, or the increments in terms of which it may be resized. The ${$B}wm${$N}
command can take any of a number of different forms, depending on the option argument. All of
the forms expect at least one additional argument, window, which must be the path name of a
top-level window.
${$T}GEOMETRY MANAGMENT${$NT}
By default a top-level window appears on the screen in its natural size, which is the one determined internally
by its widgets and geometry managers. If the natural size of a top-level window changes, then the window's size
changes to match. A top-level window can be given a size other than its natural size in two ways. First, the
user can resize the window manually using the facilities of the window manager, such as resize handles. Second,
the application can request a particular size for a top-level window using the wm geometry command. These two
cases are handled identically by Tk; in either case, the requested size overrides the natural size. You can
return the window to its natural by invoking wm geometry with an empty geometry string.
Normally a top-level window can have any size from one pixel in each dimension up to the size of its screen.
However, you can use the wm minsize and wm maxsize commands to limit the range of allowable sizes. The range
set by wm minsize and wm maxsize applies to all forms of resizing, including the window's natural size as well
as manual resizes and the wm geometry command. You can use any value accepted by Tk_GetPixels. You can also use
the command wm resizable to completely disable interactive resizing in one or both dimensions.
The wm manage and wm forget commands may be used to perform undocking and docking of windows. After a widget is
managed by wm manage command, all other wm subcommands may be used with the widget. Only widgets created using
the toplevel command may have an attached menu via the -menu configure option. A toplevel widget may be used as
a frame and managed with any of the other geometry managers after using the wm forget command. Any menu
associated with a toplevel widget will be hidden when managed by another geometry managers. The menus will
reappear once the window is managed by wm. All custom bindtags for widgets in a subtree that have their top-level
widget changed via a wm manage or wm forget command, must be redone to adjust any top-level widget path in the
bindtags. Bindtags that have not been customized do not have to be redone.
${$T}GRIDDED GEOMETRY MANAGEMENT${$NT}
Gridded geometry management occurs when one of the widgets of an application supports a range of useful sizes.
This occurs, for example, in a text editor where the scrollbars, menus, and other adornments are fixed in size but
the edit widget can support any number of lines of text or characters per line. In this case, it is usually
desirable to let the user specify the number of lines or characters-per-line, either with the wm geometry command
or by interactively resizing the window. In the case of text, and in other interesting cases also, only discrete
sizes of the window make sense, such as integral numbers of lines and characters-per-line; arbitrary pixel sizes
are not useful.
Gridded geometry management provides support for this kind of application. Tk (and the window manager) assume that
there is a grid of some sort within the application and that the application should be resized in terms of grid
units rather than pixels. Gridded geometry management is typically invoked by turning on the setGrid option for a
widget; it can also be invoked with the wm grid command or by calling Tk_SetGrid. In each of these approaches the
particular widget (or sometimes code in the application as a whole) specifies the relationship between integral
grid sizes for the window and pixel sizes. To return to non-gridded geometry management, invoke wm grid with empty
argument strings.
When gridded geometry management is enabled then all the dimensions specified in wm minsize, wm maxsize, and
wm geometry commands are treated as grid units rather than pixel units. Interactive resizing is also carried out in
even numbers of grid units rather than pixels.
${$T}BUGS${$NT}
Most existing window managers appear to have bugs that affect the operation of the ${$B}wm${$N} command. For example,
some changes will not take effect if the window is already active: the window will have to be withdrawn and
de-iconified in order to make the change happen."
@leaders -min 1 -max 1
subcommand -type string\
-choicecolumns 2\
-choicegroups\
{${$WM_CHOICEGROUPS}}\
-unindentedfields {-choicelabels}\
-choicelabels\
{${$WM_CHOICELABELS}}\
-choiceinfo {${$WM_CHOICEINFO}}
@values -unnamed true
} "@doc -name Manpage: -url [manpage wm]"\
{
@examples -help {
A fixed-size window that says that it is fixed-size too:
${[example {
toplevel .fixed
${$B}wm title${$N} .fixed "Fixed-size Window"
${$B}wm resizable${$N} .fixed 0 0
}]}
A simple dialog-like window, centred on the screen:
${[example {
# Create and arrange the dialog contents.
toplevel .msg
label .msg.l -text "This is a very simple dialog demo."
button .msg.ok -text OK -default active -command {destroy .msg}
pack .msg.ok -side bottom -fill x
pack .msg.l -expand 1 -fill both
# Now set the widget up as a centred dialog.
# But first, we need the geometry managers to finish setting
# up the interior of the dialog, for which we need to run the
# event loop with the widget hidden completely...
${$B}wm withdraw${$N} .msg
update
set x [expr {([winfo screenwidth .] - [winfo width .msg]) / 2}]
set y [expr {([winfo screenheight .] - [winfo height .msg]) / 2}]
${$B}wm geometry${$N} .msg +$x+$y
${$B}wm transient${$N} .msg .
${$B}wm title${$N} .msg "Dialog demo"
${$B}wm deiconify${$N} .msg
}]}
}}
}
#*** !doctools

40
src/vfs/_vfscommon.vfs/modules/punk/assertion-0.1.0.tm

@ -66,38 +66,6 @@ package require Tcl 8.6-
#*** !doctools
#[section API]
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# oo::class namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
tcl::namespace::eval punk::assertion::class {
#*** !doctools
#[subsection {Namespace punk::assertion::class}]
#[para] class definitions
if {[tcl::info::commands [tcl::namespace::current]::interface_sample1] eq ""} {
#*** !doctools
#[list_begin enumerated]
# oo::class create interface_sample1 {
# #*** !doctools
# #[enum] CLASS [class interface_sample1]
# #[list_begin definitions]
# method test {arg1} {
# #*** !doctools
# #[call class::interface_sample1 [method test] [arg arg1]]
# #[para] test method
# puts "test: $arg1"
# }
# #*** !doctools
# #[list_end] [comment {-- end definitions interface_sample1}]
# }
#*** !doctools
#[list_end] [comment {--- end class enumeration ---}]
}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#keep 2 namespaces for assertActive and assertInactive so there is introspection available via namespace origin
tcl::namespace::eval punk::assertion::primary {
@ -149,9 +117,13 @@ tcl::namespace::eval punk::assertion {
proc do_ns_import {} {
uplevel 1 [list tcl::namespace::import ::punk::assertion::primary::assertActive]
}
do_ns_import
#puts --------BBB
rename assertActive assert
if {[catch {
do_ns_import
rename assertActive assert
} errM]} {
puts stderr "punk::assertion error during load - assert/assertActive functions already present?\n$errM"
}
}

4
src/vfs/_vfscommon.vfs/modules/punk/du-0.1.0.tm

@ -961,9 +961,9 @@ namespace eval punk::du {
proc get_vfsmounts_in_folder {folderpath} {
set vfsmounts [list]
if {![llength [package provide vfs]]} {
return [list]
return [list]
}
set fpath [punk::objclone $folderpath]
set fpath [punk::valcopy $folderpath]
set is_rel 0
if {[file pathtype $fpath] ne "absolute"} {
set fpath [file normalize $fpath]

12
src/vfs/_vfscommon.vfs/modules/punk/lib-0.1.3.tm

@ -4071,11 +4071,11 @@ namespace eval punk::lib {
interp eval $interp [list tcl::tm::add {*}[lreverse [tcl::tm::list]]]
}
proc objclone {obj} {
proc valcopy {obj} {
append obj2 $obj {}
}
proc set_clone {varname obj} {
#used by repl's codeinterp. Maintains internal rep, easier to call e.g interp eval code [list punk::set_clone varnmame $val]
proc set_valcopy {varname obj} {
#used by repl's codeinterp. Maintains internal rep, easier to call e.g interp eval code [list punk::set_valcopy varnmame $val]
append obj2 $obj {}
uplevel 1 [list set $varname $obj2]
}
@ -4095,9 +4095,9 @@ namespace eval punk::lib {
set default_groupsize 3
set results [list]
set nums [objclone $numbers_or_commaformattednumbers] ;#stops single num from getting internal rep of list
set nums [valcopy $numbers_or_commaformattednumbers] ;#stops single num from getting internal rep of list
foreach inputnum $nums {
set number [objclone $inputnum]
set number [valcopy $inputnum]
#also handle tcl 8.7+ underscores in numbers
set number [string map [list _ "" , ""] $number]
#normalize e.g 2e4 -> 20000.0
@ -4145,7 +4145,7 @@ namespace eval punk::lib {
# and possibly containing a decimal point, i.e.: [:space:]*[:digit:]*\.[:digit:]*
proc delimit_number {unformattednumber {delim ","} {GroupSize 3}} {
set number [objclone $unformattednumber]
set number [valcopy $unformattednumber]
set number [string map {_ ""} $number]
#normalize using expr - e.g 2e4 -> 20000.0
set number [expr {$number}]

8
src/vfs/_vfscommon.vfs/modules/punk/mix/commandset/loadedlib-0.1.0.tm

@ -34,7 +34,7 @@ namespace eval punk::mix::commandset::loadedlib {
"(unimplemented) Display only those that are 0:absent 1:present 2:either"
-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"
-refresh -type none -help "Re-scan the tm and library folders"
searchstring -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.
@ -45,11 +45,11 @@ namespace eval punk::mix::commandset::loadedlib {
}
proc search {args} {
set argd [punk::args::parse $args withid ::punk::mix::commandset::loadedlib::search]
set searchstrings [dict get $argd values searchstring]
set opts [dict get $argd opts]
lassign [dict values $argd] leaders opts values received
set searchstrings [dict get $values searchstring]
set opt_return [dict get $opts -return]
set opt_highlight [dict get $opts -highlight]
set opt_refresh [dict get $opts -refresh]
set opt_refresh [dict exists $received -refresh]
if {$opt_refresh} {
catch {package require frobznodule666} ;#ensure pkg system has loaded/searched for everything REVIEW - this doesn't result in full scans

2328
src/vfs/_vfscommon.vfs/modules/punk/ns-0.1.0.tm

File diff suppressed because it is too large Load Diff

58
src/vfs/_vfscommon.vfs/modules/punk/repl-0.1.2.tm

@ -3177,7 +3177,7 @@ namespace eval repl {
variable errstack {}
variable outstack {}
variable run_command_cache
proc set_clone {varname obj} {
proc set_valcopy {varname obj} {
append obj2 $obj {}
uplevel 1 [list set $varname $obj2]
}
@ -3241,6 +3241,7 @@ namespace eval repl {
#}
set pkgs [list\
punk::ansi::colourmap\
punk::assertion\
punk::args\
punk::pipe\
cmdline\
@ -3256,7 +3257,6 @@ namespace eval repl {
textutil\
punk::encmime\
punk::char\
punk::assertion\
punk::ansi\
punk::lib\
overtype\
@ -3290,37 +3290,41 @@ namespace eval repl {
set prior_infoscript [code eval {info script}] ;#probably empty that's ok
foreach pkg $pkgs {
#puts stderr "---> init_script safe pkg: $pkg"
if {[catch {
set nsquals [namespace qualifiers $pkg]
if {$nsquals ne ""} {
if {![dict exists $ns_scanned $nsquals]} {
catch {package require $pkg 1-0} ;#force scan with nonsatisfiable version
dict set ns_scanned $nsquals 1
#only load from source if not already loaded (perhaps already present from another package loading it)
set vloaded [code eval [list package provide $pkg]]
if {$vloaded eq ""} {
if {[catch {
set nsquals [namespace qualifiers $pkg]
if {$nsquals ne ""} {
if {![dict exists $ns_scanned $nsquals]} {
catch {package require $pkg 1-0} ;#force scan with nonsatisfiable version
dict set ns_scanned $nsquals 1
}
}
}
set versions [lsort -command {package vcompare} [package versions $pkg]]
if {[llength $versions]} {
set v [lindex $versions end]
set path [lindex [package ifneeded $pkg $v] end]
if {[file extension $path] in {.tcl .tm}} {
if {![catch {readFile $path} data]} {
code eval [list info script $path]
code eval $data
code eval [list info script $prior_infoscript]
set versions [lsort -command {package vcompare} [package versions $pkg]]
if {[llength $versions]} {
set v [lindex $versions end]
set path [lindex [package ifneeded $pkg $v] end]
if {[file extension $path] in {.tcl .tm}} {
if {![catch {readFile $path} data]} {
code eval [list info script $path]
code eval $data
code eval [list info script $prior_infoscript]
} else {
error "safe - failed to read $path"
}
} else {
error "safe - failed to read $path"
error "safe - refusing to attempt load of $pkg from $path - (allowed extensions .tcl .tm)"
}
} else {
error "safe - refusing to attempt load of $pkg from $path - (allowed extensions .tcl .tm)"
error "safe - no versions of $pkg found"
}
} errMsg]} {
puts stderr "safe - failed to load package $pkg\n$errMsg\n$::errorInfo"
} else {
error "safe - no versions of $pkg found"
#puts stdout "---> init_script safe - loaded $pkg from $path"
#puts stdout "---> v [code eval [list package provide $pkg]]"
}
} errMsg]} {
puts stderr "safe - failed to load package $pkg\n$errMsg\n$::errorInfo"
} else {
#puts stdout "---> init_script safe - loaded $pkg from $path"
#puts stdout "---> v [code eval [list package provide $pkg]]"
}
}
@ -3337,7 +3341,7 @@ namespace eval repl {
#review
code alias ::shellfilter::stack ::shellfilter::stack
#code alias ::punk::lib::set_clone ::punk::lib::set_clone
#code alias ::punk::lib::set_valcopy ::punk::lib::set_valcopy
#code alias ::aliases ::punk::ns::aliases
code alias ::punk::ns::aliases ::punk::ns::aliases
namespace eval ::codeinterp {}

4
src/vfs/_vfscommon.vfs/modules/punk/repl/codethread-0.1.1.tm

@ -196,8 +196,8 @@ tcl::namespace::eval punk::repl::codethread {
#otherwise when $script goes out of scope - internal rep of vars set in script changes.
#The shimmering may be no big deal(?) - but debug/analysis using tcl::unsupported::representation becomes impossible.
#interp eval code [list ::punk::lib::set_clone ::codeinterp::clonescript $script] ;#like objclone
interp eval code [list ::codeinterp::set_clone ::codeinterp::clonescript $script] ;#like objclone
#interp eval code [list ::punk::lib::set_valcopy ::codeinterp::clonescript $script] ;#like valcopy
interp eval code [list ::codeinterp::set_valcopy ::codeinterp::clonescript $script] ;#like valcopy
interp eval code {lappend ::codeinterp::run_command_cache $::codeinterp::clonescript}

18
src/vfs/_vfscommon.vfs/modules/punk/unixywindows-0.1.0.tm

@ -35,35 +35,35 @@ namespace eval punk::unixywindows {
if {![string length $cachedunixyroot]} {
if {![catch {
set result [exec cygpath -m /] ;# -m gives result with forward-slashes - which is ok for windows paths in a Tcl context.
set cachedunixyroot [punk::objclone $result]
set cachedunixyroot [punk::valcopy $result]
file pathtype $cachedunixyroot; #this call causes the int-rep to be path
#set ::punk::last_run_display [list] ;#hack - review shouldn't really be necessary.. but because we call winpath from ./ - the repl looks for last_run_display
} errM]} {
} else {
puts stderr "Warning: Failed to determine base for unix-like paths - using default of c:/msys2"
file pathtype [set cachedunixyroot [punk::objclone "c:/msys2"]]
file pathtype [set cachedunixyroot [punk::valcopy "c:/msys2"]]
}
}
#will have been shimmered from string to 'path' internal rep by 'file pathtype' call
#let's return a different copy as it's so easy to lose path-rep
set copy [punk::objclone $cachedunixyroot]
set copy [punk::valcopy $cachedunixyroot]
return $copy
}
proc refresh_unixyroot {} {
variable cachedunixyroot
set result [exec cygpath -m /] ;# -m gives result with forward-slashes - which is ok for windows paths in a Tcl context.
set cachedunixyroot [punk::objclone $result]
set cachedunixyroot [punk::valcopy $result]
file pathtype $cachedunixyroot; #this call causes the int-rep to be path
set copy [punk::objclone $cachedunixyroot]
set copy [punk::valcopy $cachedunixyroot]
return $copy
}
proc set_unixyroot {windows_path} {
variable cachedunixyroot
file pathtype $windows_path
set cachedunixyroot [punk::objclone $windows_path]
set cachedunixyroot [punk::valcopy $windows_path]
#return the original - but probably int-rep will have shimmered to path even if started out as string
#- that's probably what we want. Caller can use as string or path without affecting cachedunixyroot
return $windows_path
@ -131,13 +131,13 @@ namespace eval punk::unixywindows {
}
#puts stderr "->$driveletters"
set path [punk::objclone $unixypath] ;#take another copy that we can deliberatley shimmer to path and know is separate to the supplied argument
set path [punk::valcopy $unixypath] ;#take another copy that we can deliberatley shimmer to path and know is separate to the supplied argument
set supplied_pathtype [file pathtype $path] ;#we don't care about the pathtype so much as the act of making this call shimmers to a path internal-rep
#copy of var that we can treat as a string without affecting path rep
#Note that some but not all read operations will lose path rep e.g 'string length $x' will lose any path-rep $x had, (but 'string equal $x something' won't)
#Todo - make int-rep tests to check stability of these behaviours across Tcl versions!
set strcopy_path [punk::objclone $path]
set strcopy_path [punk::valcopy $path]
set str_newpath ""
@ -174,7 +174,7 @@ namespace eval punk::unixywindows {
#dunno - pass through
set pathobj $path
} else {
set pathobj [punk::objclone $str_newpath]
set pathobj [punk::valcopy $str_newpath]
file pathtype $pathobj
}
}

12
src/vfs/_vfscommon.vfs/modules/punk/winpath-0.1.0.tm

@ -30,7 +30,7 @@ namespace eval punk::winpath {
#\\servername\share etc or \\?\UNC\servername\share etc.
proc is_unc_path {path} {
set strcopy_path [punk::winpath::system::objclone $path]
set strcopy_path [punk::winpath::system::valcopy $path]
set strcopy_path [string map {\\ /} $strcopy_path] ;#normalize to forward slashes for testing purposes (and forward slashes seem to be auto-translated by windows anyway)
if {[string first "//" $strcopy_path] == 0} {
#check for "Dos device path" syntax
@ -77,7 +77,7 @@ namespace eval punk::winpath {
#dos device path syntax allows windows api to acces extended-length paths and filenames with illegal path characters such as trailing dots or whitespace
#(can exist on server shares and on NTFS - but standard apps can't access without dos device syntax)
proc is_dos_device_path {path} {
set strcopy_path [punk::winpath::system::objclone $path]
set strcopy_path [punk::winpath::system::valcopy $path]
set strcopy_path [string map {\\ /} $strcopy_path] ;#normalize to forward slashes for testing purposes (and forward slashes seem to be auto-translated by windows anyway)
if {[string range $strcopy_path 0 3] in {//?/ //./}} {
return 1
@ -100,13 +100,13 @@ namespace eval punk::winpath {
proc strip_unc_path_prefix {path} {
if {[is_unc_path_plain $path]} {
#plain unc //server
set strcopy_path [punk::winpath::system::objclone $path]
set strcopy_path [punk::winpath::system::valcopy $path]
set trimmedpath [string range $strcopy_path 2 end]
file pathtype $trimmedpath
return $trimmedpath
} elseif {is_unc_path $path} {
#//?/UNC/server/subpath or //./UNC/server/subpath
set strcopy_path [punk::winpath::system::objclone $path]
set strcopy_path [punk::winpath::system::valcopy $path]
set trimmedpath [string range $strcopy_path 7 end]
file pathtype $trimmedpath ;#shimmer it to path rep
return $trimmedpath
@ -153,7 +153,7 @@ namespace eval punk::winpath {
error $err
}
set strcopy_path [punk::winpath::system::objclone $path]
set strcopy_path [punk::winpath::system::valcopy $path]
#Note: path could still have leading double slash if it is a Dos device path: e.g. //?/c:/etc
@ -339,7 +339,7 @@ namespace eval punk::winpath {
namespace eval punk::winpath::system {
#get a copy of the item without affecting internal rep
proc objclone {obj} {
proc valcopy {obj} {
append obj2 $obj {}
}
}

16
src/vfs/_vfscommon.vfs/modules/punk/winrun-0.1.0.tm

@ -310,7 +310,7 @@ namespace eval punk::winrun {
set tcl_list [list]
set i 0
foreach a $cmdargs {
set copy [internal::objclone $a]
set copy [internal::valcopy $a]
append raw_cmdline "$copy "
lappend tcl_list $copy
if {$i == 0 && !$quiet} {
@ -333,7 +333,7 @@ namespace eval punk::winrun {
#set raw_parts [list]
#foreach range $wordranges {
# set word [string range $raw_cmdline {*}$range]
# lappend raw_parts [internal::objclone $word]
# lappend raw_parts [internal::valcopy $word]
#}
@ -521,7 +521,7 @@ namespace eval punk::winrun {
return "Usage: quote_cmd ?runopt? ... ?--? ?cmd? ?cmdarg? ..."
}
foreach a $cmdargs {
set copy [internal::objclone $a]
set copy [internal::valcopy $a]
append raw_cmdline "$copy "
lappend tcl_list $copy
}
@ -632,7 +632,7 @@ namespace eval punk::winrun {
set verbose [expr {"-verbose" in $runopts}]
#review - may need to force quoting of barewords by quote_win to ensure proper behaviour if bareword contains cmd specials?
#?always treatable as a list? review
set tcl_list [lmap v $cmdargs {internal::objclone $v}]
set tcl_list [lmap v $cmdargs {internal::valcopy $v}]
set meta_chars [list {<} {>} & |] ;#caret-quote when outside of cmd.exe's idea of a quoted section - carets will disappear from passed on string
set cmdline ""
set in_quotes 0
@ -721,7 +721,7 @@ namespace eval punk::winrun {
set allowvars [expr {"-allowvars" in $runopts}]
set verbose [expr {"-verbose" in $runopts}]
set tcl_list [lmap v $cmdargs {internal::objclone $v}]
set tcl_list [lmap v $cmdargs {internal::valcopy $v}]
set meta_chars [list {"} "(" ")" ^ < > & |]
if {!$allowvars} {
lappend meta_chars % !
@ -764,7 +764,7 @@ namespace eval punk::winrun {
set allowvars [expr {"-allowvars" in $runopts}]
set allowquotes [expr {"-allowquotes" in $runopts}]
set verbose [expr {"-verbose" in $runopts}]
set tcl_list [lmap v $cmdargs {internal::objclone $v}]
set tcl_list [lmap v $cmdargs {internal::valcopy $v}]
set cmdline ""
set i 0
set meta_chars [list "(" ")" ^ < > & |]
@ -797,7 +797,7 @@ namespace eval punk::winrun {
proc quote_cmd2 {args} {
set cmdargs $args
set tcl_list [lmap v $cmdargs {internal::objclone $v}]
set tcl_list [lmap v $cmdargs {internal::valcopy $v}]
set cmdline ""
set i 0
@ -906,7 +906,7 @@ namespace eval punk::winrun {
# -- --- ---
#get a copy of the item without affecting internal rep
#this isn't critical for most usecases - but can be of use for example when trying not to shimmer path objects to strings (filesystem performance impact in some cases)
proc objclone {obj} {
proc valcopy {obj} {
append obj2 $obj {}
}
# -- --- ---

BIN
src/vfs/_vfscommon.vfs/modules/treeobj-1.3.1.tm

Binary file not shown.
Loading…
Cancel
Save