Browse Source

add previous updates to bootsupport and project_layouts and common vfs

master
Julian Noble 3 days ago
parent
commit
9201234c4f
  1. 67
      src/bootsupport/modules/punk/aliascore-0.1.0.tm
  2. 246
      src/bootsupport/modules/punk/ansi-0.1.1.tm
  3. 31
      src/bootsupport/modules/punk/args-0.1.4.tm
  4. 272
      src/bootsupport/modules/punk/config-0.1.tm
  5. 6
      src/bootsupport/modules/punk/mod-0.1.tm
  6. 12
      src/bootsupport/modules/punk/ns-0.1.0.tm
  7. 6
      src/bootsupport/modules/punk/repl/codethread-0.1.1.tm
  8. 127
      src/bootsupport/modules/shellfilter-0.1.9.tm
  9. BIN
      src/bootsupport/modules/test/tomlish-1.1.5.tm
  10. 2
      src/bootsupport/modules/textblock-0.1.3.tm
  11. 1246
      src/bootsupport/modules/tomlish-1.1.6.tm
  12. 67
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/aliascore-0.1.0.tm
  13. 246
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/ansi-0.1.1.tm
  14. 31
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/args-0.1.4.tm
  15. 272
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/config-0.1.tm
  16. 6
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mod-0.1.tm
  17. 12
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/ns-0.1.0.tm
  18. 6
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/repl/codethread-0.1.1.tm
  19. 127
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/shellfilter-0.1.9.tm
  20. BIN
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/test/tomlish-1.1.5.tm
  21. 2
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/textblock-0.1.3.tm
  22. 1246
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/tomlish-1.1.6.tm
  23. 67
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/aliascore-0.1.0.tm
  24. 246
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/ansi-0.1.1.tm
  25. 31
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/args-0.1.4.tm
  26. 272
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/config-0.1.tm
  27. 6
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mod-0.1.tm
  28. 12
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/ns-0.1.0.tm
  29. 6
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/repl/codethread-0.1.1.tm
  30. 127
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/shellfilter-0.1.9.tm
  31. BIN
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/test/tomlish-1.1.5.tm
  32. 2
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/textblock-0.1.3.tm
  33. 1246
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/tomlish-1.1.6.tm
  34. 31
      src/vfs/_vfscommon.vfs/modules/dictn-0.1.2.tm
  35. 704
      src/vfs/_vfscommon.vfs/modules/modpod-0.1.3.tm
  36. 5
      src/vfs/_vfscommon.vfs/modules/punk-0.1.tm
  37. 67
      src/vfs/_vfscommon.vfs/modules/punk/aliascore-0.1.0.tm
  38. 246
      src/vfs/_vfscommon.vfs/modules/punk/ansi-0.1.1.tm
  39. 31
      src/vfs/_vfscommon.vfs/modules/punk/args-0.1.4.tm
  40. 127
      src/vfs/_vfscommon.vfs/modules/punk/args/tclcore-0.1.0.tm
  41. 13
      src/vfs/_vfscommon.vfs/modules/punk/basictelnet-0.1.0.tm
  42. 274
      src/vfs/_vfscommon.vfs/modules/punk/config-0.1.tm
  43. 6
      src/vfs/_vfscommon.vfs/modules/punk/mod-0.1.tm
  44. 23
      src/vfs/_vfscommon.vfs/modules/punk/netbox-0.1.0.tm
  45. 12
      src/vfs/_vfscommon.vfs/modules/punk/ns-0.1.0.tm
  46. 48
      src/vfs/_vfscommon.vfs/modules/punk/repl-0.1.1.tm
  47. 6
      src/vfs/_vfscommon.vfs/modules/punk/repl/codethread-0.1.1.tm
  48. 127
      src/vfs/_vfscommon.vfs/modules/shellfilter-0.1.9.tm
  49. 12
      src/vfs/_vfscommon.vfs/modules/shellrun-0.1.1.tm
  50. BIN
      src/vfs/_vfscommon.vfs/modules/test/tomlish-1.1.5.tm
  51. 2
      src/vfs/_vfscommon.vfs/modules/textblock-0.1.3.tm
  52. 1285
      src/vfs/_vfscommon.vfs/modules/tomlish-1.1.6.tm

67
src/bootsupport/modules/punk/aliascore-0.1.0.tm

@ -103,7 +103,9 @@ tcl::namespace::eval punk::aliascore {
#use absolute ns ie must be prefixed with ::
#single element commands are imported if source command already exists, otherwise aliased. multi element commands are aliased
#functions must be in export list of their source namespace
#functions should generally be covered by one of the export patterns of their source namespace
# - if they are not - e.g (separately loaded ensemble command ?)
# the aliascore::init will temporarily extend the exports list to do the import, and then reset the exports to how they were.
set aliases [tcl::dict::create\
val ::punk::pipe::val\
aliases ::punk::lib::aliases\
@ -122,8 +124,8 @@ tcl::namespace::eval punk::aliascore {
stripansi ::punk::ansi::ansistrip\
ansiwrap ::punk::ansi::ansiwrap\
colour ::punk::console::colour\
ansi ::punk::console::ansi\
color ::punk::console::colour\
ansi ::punk::console::ansi\
a? ::punk::console::code_a?\
A? {::punk::console::code_a? forcecolor}\
a+ ::punk::console::code_a+\
@ -132,6 +134,7 @@ tcl::namespace::eval punk::aliascore {
A {::punk::console::code_a forcecolour}\
smcup ::punk::console::enable_alt_screen\
rmcup ::punk::console::disable_alt_screen\
config ::punk::config\
]
#*** !doctools
@ -153,6 +156,35 @@ tcl::namespace::eval punk::aliascore {
# return "ok"
#}
proc _is_exported {ns cmd} {
set exports [::tcl::namespace::eval $ns [list namespace export]]
set is_exported 0
foreach p $exports {
if {[string match $p $cmd]} {
set is_exported 1
break
}
}
return $is_exported
}
#_nsprefix accepts entire command - not just an existing namespace for which we want the parent
proc _nsprefix {{nspath {}}} {
#maintenance: from punk::ns::nsprefix - (without unnecessary nstail)
#normalize the common case of ::::
set nspath [string map {:::: ::} $nspath]
set rawprefix [string range $nspath 0 end-[string length [namespace tail $nspath]]]
if {$rawprefix eq "::"} {
return $rawprefix
} else {
if {[string match *:: $rawprefix]} {
return [string range $rawprefix 0 end-2]
} else {
return $rawprefix
}
}
}
#todo - options as to whether we should raise an error if collisions found, undo aliases etc?
proc init {args} {
set defaults {-force 0}
@ -195,6 +227,7 @@ tcl::namespace::eval punk::aliascore {
error "punk::aliascore::init declined to create any aliases or imports because -force == 0 and conflicts found:$conflicts"
}
set failed [list]
set tempns ::temp_[info cmdcount] ;#temp ns for renames
dict for {a cmd} $aliases {
#puts "aliascore $a -> $cmd"
@ -206,16 +239,36 @@ tcl::namespace::eval punk::aliascore {
} else {
if {[tcl::info::commands $cmd] ne ""} {
#todo - ensure exported? noclobber?
if {[tcl::namespace::tail $a] eq [tcl::namespace::tail $cmd]} {
set container_ns [_nsprefix $cmd]
set cmdtail [tcl::namespace::tail $cmd]
set was_exported 1 ;#assumption
if {![_is_exported $container_ns $cmdtail]} {
set was_exported 0
set existing_exports [tcl::namespace::eval $container_ns [list ::namespace export]]
tcl::namespace::eval $container_ns [list ::namespace export $cmdtail]
}
if {[tcl::namespace::tail $a] eq $cmdtail} {
#puts stderr "importing $cmd"
tcl::namespace::eval :: [list namespace import $cmd]
try {
tcl::namespace::eval :: [list ::namespace import $cmd]
} trap {} {emsg eopts} {
lappend failed [list alias $a target $cmd errormsg $emsg]
}
} else {
#target command name differs from exported name
#e.g stripansi -> punk::ansi::ansistrip
#import and rename
#puts stderr "importing $cmd (with rename to ::$a)"
tcl::namespace::eval $tempns [list namespace import $cmd]
catch {rename ${tempns}::[namespace tail $cmd] ::$a}
try {
tcl::namespace::eval $tempns [list ::namespace import $cmd]
} trap {} {emsg eopst} {
lappend failed [list alias $a target $cmd errormsg $emsg]
}
catch {rename ${tempns}::$cmdtail ::$a}
}
#restore original exports
if {!$was_exported} {
tcl::namespace::eval $container_ns [list ::namespace export -clear {*}$existing_exports]
}
} else {
interp alias {} $a {} {*}$cmd
@ -223,7 +276,7 @@ tcl::namespace::eval punk::aliascore {
}
}
#tcl::namespace::delete $tempns
return [dict create aliases [dict keys $aliases] unchanged $ignore_aliases changed $conflicts]
return [dict create aliases [dict keys $aliases] existing $existing ignored $ignore_aliases changed $conflicts failed $failed]
}

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

@ -3357,9 +3357,20 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
lappend PUNKARGS [list {
@id -id ::punk::ansi::ansiwrap
@cmd -name punk::ansi::ansiwrap -help\
"Wrap a string with ANSI codes from
{Wrap a string with ANSI codes from
supplied codelist(s) followed by trailing
ANSI reset.
ANSI reset. The wrapping is done such that
after every reset in the supplied text, the
default goes back to the supplied codelist.
e.g1 in the following
ansiwrap red bold "rrr[a+ green]ggg[a]rrr"
both strings rrr will be red & bold
e.g2 bolding and underlining specific text whilst dimming the rest
ansiwrap dim [string map [list test [ansiwrap bold underline test]] "A test string"]
e.g3 reverse render a complex ansi substring
ansiwrap reverse [textblock::periodic]
Codes are numbers or strings as indicated
in the output of the colour information
@ -3372,41 +3383,172 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
For finer control use the a+ and a
functions eg
set x \"[a+ red]text [a+ bold]etc[a]\"
"
set x "[a+ red]text [a+ bold]etc[a]"
}
@leaders -min 0 -max -1
codelist -multiple 1 -default {} -type list -help\
"ANSI names/ints as understood by 'a?'
(Not actual ANSI as output by a+)
These can be supplied individually or
as a list or lists"
@opts
-rawansi -type ansi -default ""
-resetcodes -type list -default {reset}
-rawresets -type ansi -default ""
-fullcodemerge -type boolean -default 0 -help\
"experimental"
-overridecodes -type list -default {}
@values -min 1 -max 1
text -type string -help\
"String to wrap with ANSI (SGR)"
}]
#proc ansiwrap {codes text} {
# return [a {*}$codes]$text[a]
#}
proc ansiwrap2 {args} {
set argd [punk::args::parse $args withid ::punk::ansi::ansiwrap]
set codelists [dict get $argd leaders codelist]
set text [dict get $argd values text]
set codes [concat {*}$codelists] ;#flatten
return [a {*}$codes]$text[a]
}
proc ansiwrap {args} {
if {[llength $args] < 1} {
#minimal args parsing - unhappy path only
#throw to args::parse to get friendly error/usage display
punk::args::parse $args withid ::punk::ansi::ansiwrap
return
}
#we know there are no valid codes that start with -
if {[lsearch [lrange $args 0 end-1] -*] == -1} {
#no opts
set text [lindex $args end]
set codelists [lrange $args 0 end-1]
set R [a] ;#plain ansi reset
set rawansi ""
set rawresets ""
set fullmerge 0
set overrides ""
} else {
set argd [punk::args::parse $args withid ::punk::ansi::ansiwrap]
lassign [dict values $argd] leaders opts values received solos
set codelists [dict get $leaders codelist]
set text [dict get $values text]
set rawansi [dict get $opts -rawansi]
set R [a+ {*}[dict get $opts -resetcodes]]
set rawresets [dict get $opts -rawresets]
set fullmerge [dict get $opts -fullcodemerge]
set overrides [punk::ansi::ta::get_codes_single [a+ {*}[dict get $opts -overridecodes]]]
}
#note that the result of any sgr_merge or sgr_merge_singles is not necessarily a single Ansi escape sequence.
#there can be SGR unmergeables (due to enhanced underlines) as well as non SGR codes
set codes [concat {*}$codelists] ;#flatten
return [a {*}$codes]$text[a]
set base [a+ {*}$codes]
if {$rawansi ne ""} {
set rawcodes [punk::ansi::ta::get_codes_single $rawansi] ;#caller may have supplied as [a+ xxx][a+ yyy]
if {$fullmerge} {
set base [punk::ansi::codetype::sgr_merge [list $base {*}$rawcodes]]
} else {
set base [punk::ansi::codetype::sgr_merge_singles [list $base {*}$rawcodes]]
}
}
if {$rawresets ne ""} {
set rawresetcodes [punk::ansi::ta::get_codes_single $rawresets]
if {$fullmerge} {
set R [punk::ansi::codetype::sgr_merge [list $R {*}$rawresetcodes]]
} else {
set R [punk::ansi::codetype::sgr_merge_singles [list $R {*}$rawresetcodes]]
}
}
set codestack [list]
if {[punk::ansi::ta::detect $text]} {
set emit ""
set parts [punk::ansi::ta::split_codes $text]
foreach {pt code} $parts {
switch -- [llength $codestack] {
0 {
append emit $base$pt$R
}
1 {
if {[punk::ansi::codetype::is_sgr_reset [lindex $codestack 0]]} {
append emit $base$pt$R
set codestack [list]
} else {
#append emit [lindex $o_codestack 0]$pt
if {$fullmerge} {
append emit [punk::ansi::codetype::sgr_merge [list $base {*}$codestack {*}$overrides]]$pt$R
} else {
append emit [punk::ansi::codetype::sgr_merge_singles [list $base {*}$codestack {*}$overrides]]$pt$R
}
}
}
default {
if {$fullmerge} {
append emit [punk::ansi::codetype::sgr_merge [list $base {*}$codestack {*}$overrides]]$pt$R
} else {
append emit [punk::ansi::codetype::sgr_merge_singles [list $base {*}$codestack {*}$overrides]]$pt$R
}
}
}
#parts ends on a pt - last code always empty string
if {$code ne ""} {
set c1c2 [tcl::string::range $code 0 1]
set leadernorm [tcl::string::range [tcl::string::map [list\
\x1b\[ 7CSI\
\x9b 8CSI\
\x1b\( 7GFX\
] $c1c2] 0 3]
switch -- $leadernorm {
7CSI - 8CSI {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set codestack [list "\x1b\[m"]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set codestack [list $code]
} elseif {[punk::ansi::codetype::is_sgr $code]} {
#todo - make caching is_sgr method
set dup_posns [lsearch -all -exact $codestack $code]
set codestack [lremove $codestack {*}$dup_posns]
lappend codestack $code
} else {
}
}
7GFX {
switch -- [tcl::string::index $code 2] {
"0" {
set o_gx_state on
}
"B" {
set o_gx_state off
}
}
}
default {
#other ansi codes
}
}
append emit $code
}
}
return $emit$R
} else {
return $base$text$R
}
}
proc ansiwrap_naive {codes text} {
return [a_ {*}$codes]$text[a]
}
#a silly trick... temporary? probably - todo - tests and work on sgr_merge + sgr_merge_singles before relying on this
#when we use sgr_merge_singles on a 'single' containing a non SGR code e.g <esc>[5h (inverse) it puts this code at the end of the list
#furthermore - it carries any SGR codes along with it (Can/should we rely on this behaviour??? probably not) REVIEW
#P% ansistring VIEW $s1
#- ␛[31m␛[?5h
#P% ansistring VIEW [punk::ansi::codetype::sgr_merge_singles [list $s1 [a+ cyan]]]
#- ␛[36m␛[31m␛[?5h
#P% ansistring VIEW [punk::ansi::codetype::sgr_merge [list $s1 [a+ cyan]]]
#- ␛[36m␛[?5h
#we can use this trick to override background and/or foreground colours using ansiwrap - which uses sgr_merge_singles
#Note - this trick is not composable - e.g ansioverride Red [ansiioverride Green [textblock::periodic]] doesn't work as expected.
proc ansioverride2 {args} {
set text [lindex $args end]
set codes [lrange $args 0 end-1]
ansiwrap {*}$codes -rawansi [punk::ansi::enable_inverse] -rawresets [punk::ansi::disable_inverse] $text
}
proc ansireverse {text} {
ansioverride2 normal reverse $text
}
proc get_code_name {code} {
#*** !doctools
@ -4491,6 +4633,77 @@ tcl::namespace::eval punk::ansi {
return 0
}
}
#e.g has_any_effective $str bg fg
proc has_any_effective {str args} {
set singlecodes [punk::ansi::ta::get_codes_single $str]
set mergeinfo [punk::ansi::codetype::sgr_merge_singles $singlecodes -info 1]
foreach t $args {
switch -- $t {
sgr - unmergeable - othercodes {
if {[dict get $mergeinfo $t] ne ""} {
return 1
}
}
intensity - italic - underline - underextended - blink - reverse - hide - strike - font - gothic - doubleunderline
- proportional - frame_or_circle - ideogram_underline - ideogram_doubleunderline - ideogram_clear - overline - underlinecolour - superscript - subscript
- nosupersub - fg - bg {
if {[dict get $mergeinfo codestate $t] ne ""} {
return 1
}
}
bold {
if {[dict get $mergeinfo codestate intensity] eq "1"} {
return 1
}
}
dim {
if {[dict get $mergeinfo codestate intensity] eq "2"} {
return 1
}
}
default {
error "punk::ansi::ta::has_any_effective invalid type '$t' specified"
}
}
}
return 0
}
proc has_all_effective {str args} {
set singlecodes [punk::ansi::ta::get_codes_single $str]
set mergeinfo [punk::ansi::codetype::sgr_merge_singles $singlecodes -info 1]
foreach t $args {
switch -- $t {
sgr - unmergeable - othercodes {
if {[dict get $mergeinfo $t] eq ""} {
return 0
}
}
intensity - italic - underline - underextended - blink - reverse - hide - strike - font - gothic - doubleunderline
- proportional - frame_or_circle - ideogram_underline - ideogram_doubleunderline - ideogram_clear - overline - underlinecolour - superscript - subscript
- nosupersub - fg - bg {
if {[dict get $mergeinfo codestate $t] eq ""} {
return 0
}
}
bold {
if {[dict get $mergeinfo codestate intensity] ne "1"} {
return 0
}
}
dim {
if {[dict get $mergeinfo codestate intensity] ne "2"} {
return 0
}
}
default {
error "punk::ansi::ta::has_any_effective invalid type '$t' specified"
}
}
}
return 1
}
proc is_gx {code} {
#g0 {(?:\x1b\(0)(?:(?!\x1b\(B).)*\x1b\(B}
#g1 {(?:\x1b\)0)(?:(?!\x1b\)B).)*\x1b\)B}
@ -4513,6 +4726,7 @@ tcl::namespace::eval punk::ansi {
set codestate_empty [tcl::dict::create]
tcl::dict::set codestate_empty rst "" ;#0 (or empty)
tcl::dict::set codestate_empty intensity "" ;#1 bold, 2 dim, 22 normal
tcl::dict::set codestate_empty shadowed "" ;
tcl::dict::set codestate_empty italic "" ;#3 on 23 off
tcl::dict::set codestate_empty underline "" ;#4 on 24 off

31
src/bootsupport/modules/punk/args-0.1.4.tm

@ -3226,7 +3226,36 @@ tcl::namespace::eval punk::args {
form1: parse $arglist ?-flag val?... withid $id
form2: parse $arglist ?-flag val?... withdef $def ?$def?
see punk::args::define"
see punk::args::define
Returns a dict of information regarding the parsed arguments
example of basic usage for single option only:
punk::args::define {
@id -id ::myns::myfunc
@cmd -name myns::myfunc
@leaders -min 0 -max 0
@opts
-configfile -type existingfile
#type none makes it a solo flag
-verbose -type none
@values -min 0 -max 0
}
proc myfunc {args} {
set argd [punk::args::parse $args withid ::myns::myfunc]
lassign [dict values $argd] leaders opts values received solos
if {[dict exists $received] -configfile} {
puts \"have option for existing file [dict get $opts -configfile]\"
}
}
The leaders, opts, values keys in the parse result dict are proper dicts.
The received key is dict-like but can have repeated keys for arguments than can
accept multiples. The value for each received element is the ordinal position.
The solos key refers to a list of solo flags received (those specified with
-type none). This is generally only useful to assist in passing arguments on
to another procedure which also requires solos, because the opts dict contains
solo flags with a 1 value or a list of 1's if it was a solo with -multiple true
specified.
"
@form -form {withid withdef}
@leaders -min 1 -max 1
arglist -type list -optional 0 -help\

272
src/bootsupport/modules/punk/config-0.1.tm

@ -1,23 +1,109 @@
tcl::namespace::eval punk::config {
variable loaded
variable startup ;#include env overrides
variable running
variable configdata [dict create] ;#key on config names. At least default, startup, running
#variable startup ;#include env overrides
#variable running
variable punk_env_vars
variable other_env_vars
variable vars
namespace export {[a-z]*}
namespace ensemble create
namespace eval punk {namespace export config}
proc _homedir {} {
if {[info exists ::env(HOME)]} {
set home [file normalize $::env(HOME)]
} else {
#not available on 8.6? ok will error out here.
set home [file tildeexpand ~]
}
return $home
}
lappend PUNKARGS [list {
@id -id ::punk::config::dir
@cmd -name punk::config::dir -help\
"Get the path for the default config folder
Config files are in toml format.
The XDG_CONFIG_HOME env var is the preferred
choice of location.
A folder under the user's home directory,
at .config/punk/shell is chosen if
XDG_CONFIG_HOME is not configured.
"
@leaders -min 0 -max 0
@opts
-quiet -type none -help\
"Suppress warning given when the folder does
not yet exist"
@values -min 0 -max 0
}]
proc dir {args} {
if {"-quiet" in $args} {
set be_quiet [dict exists $received -quiet]
}
set was_noisy 0
set config_home [punk::config::configure running xdg_config_home]
set config_dir [file join $config_home punk shell]
if {!$be_quiet && ![file exists $config_dir]} {
set msg "punk::shell data storage folder at $config_dir does not yet exist."
puts stderr $msg
set was_noisy 1
}
if {!$be_quiet && $was_noisy} {
puts stderr "punk::config::dir - call with -quiet option to suppress these messages"
}
return $config_dir
#if {[info exists ::env(XDG_CONFIG_HOME)]} {
# set config_home $::env(XDG_CONFIG_HOME)
#} else {
# set config_home [file join [_homedir] .config]
# if {!$be_quiet} {
# puts stderr "Environment variable XDG_CONFIG_HOME does not exist - consider setting it if $config_home is not a suitable location"
# set was_noisy 1
# }
#}
#if {!$be_quiet && ![file exists $config_home]} {
# #parent folder for 'punk' config dir doesn't exist
# set msg "configuration location (XDG_CONFIG_HOME or ~/.config) $config_home does not yet exist"
# append msg \n " - please create it and/or set XDG_CONFIG_HOME env var."
# puts stderr $msg
# set was_noisy 1
#}
#set config_dir [file join $config_home punk shell]
#if {!$be_quiet && ![file exists $config_dir]} {
# set msg "punk::shell data storage folder at $config_dir does not yet exist."
# append msg \n " It will be created if api_context_save is called without specifying an alternate location."
# puts stderr $msg
# set was_noisy 1
#}
#if {!$be_quiet && $was_noisy} {
# puts stderr "punk::config::dir - call with -quiet option to suppress these messages"
#}
#return [file join $configdir config.toml]
}
#todo - XDG_DATA_HOME etc
#https://specifications.freedesktop.org/basedir-spec/latest/
# see also: http://hiphish.github.io/blog/2020/08/30/dotfiles-were-a-mistake/
proc init {} {
variable defaults
variable startup
variable running
variable configdata
#variable defaults
#variable startup
#variable running
variable punk_env_vars
variable punk_env_vars_config
variable other_env_vars
@ -108,12 +194,14 @@ tcl::namespace::eval punk::config {
#we have a choice of LOCALAPPDATA vs APPDATA (local to machine vs potentially roaming/redirected in a corporate environment)
#using the roaming location should not impact users who aren't using a domain controller but is potentially much more convenient for those who do.
if {[info exists ::env(APPDATA)]} {
#Typical existing/default value for env(APPDATA) on windows is c:\Users\<username>\AppData\Roaming
set default_xdg_config_home $::env(APPDATA)
set default_xdg_data_home $::env(APPDATA)
}
#The xdg_cache_home should be kept local
if {[info exists ::env(LOCALAPPDATA)]} {
#Typical existing/default value for env(APPDATA) on windows is c:\Users\<username>\AppData\Local
set default_xdg_data_home $::env(LOCALAPPDATA)
set default_xdg_cache_home $::env(LOCALAPPDATA)
set default_xdg_state_home $::env(LOCALAPPDATA)
}
@ -133,10 +221,10 @@ tcl::namespace::eval punk::config {
}
}
set defaults [dict create\
dict set configdata defaults [dict create\
apps $default_apps\
config ""\
configset ".punkshell"\
config "startup"\
configset "main"\
scriptlib $default_scriptlib\
color_stdout $default_color_stdout\
color_stdout_repl $default_color_stdout_repl\
@ -160,7 +248,7 @@ tcl::namespace::eval punk::config {
posh_themes_path ""\
]
set startup $defaults
dict set configdata startup [dict get $configdata defaults]
#load values from saved config file - $xdg_config_home/punk/punk.config ?
#typically we want env vars to override the stored config - as env vars conventionally used on some commandlines.
#that's possibly ok for the PUNK_ vars
@ -219,9 +307,9 @@ tcl::namespace::eval punk::config {
lappend final $p
}
}
tcl::dict::set startup $varname $final
tcl::dict::set configdata startup $varname $final
} else {
tcl::dict::set startup $varname $f
tcl::dict::set configdata startup $varname $f
}
}
}
@ -273,29 +361,44 @@ tcl::namespace::eval punk::config {
lappend final $p
}
}
tcl::dict::set startup $varname $final
tcl::dict::set configdata startup $varname $final
} else {
tcl::dict::set startup $varname $f
tcl::dict::set configdata startup $varname $f
}
}
}
}
set config_home [dict get $configdata startup xdg_config_home]
if {![file exists $config_home]} {
puts stderr "punk::config::init creating punk shell config dir: [dir]"
puts stderr "(todo)"
}
set configset [dict get $configdata defaults configset]
set config [dict get $configdata defaults config]
set startupfile [file join $config_home $configset $config.toml]
if {![file exists $startupfile]} {
puts stderr "punk::config::init creating punk shell config file: $config for configset: $configset"
puts stderr "(todo)"
}
#unset -nocomplain vars
#todo
set running [tcl::dict::create]
set running [tcl::dict::merge $running $startup]
dict set configdata running [tcl::dict::merge $running [dict get $configdata startup]]
}
init
#todo
proc Apply {config} {
variable configdata
puts stderr "punk::config::Apply partially implemented"
set configname [string map {-config ""} $config]
if {$configname in {startup running}} {
upvar ::punk::config::$configname applyconfig
set applyconfig [dict get $configdata $configname]
if {[dict exists $applyconfig auto_noexec]} {
set auto [dict get $applyconfig auto_noexec]
@ -315,67 +418,128 @@ tcl::namespace::eval punk::config {
}
return "apply done"
}
Apply startup
#todo - consider how to divide up settings, categories, 'devices', decks etc
proc get_running_global {varname} {
variable running
variable configdata
set running [dict get $configdata running]
if {[dict exists $running $varname]} {
return [dict get $running $varname]
}
error "No such global configuration item '$varname' found in running config"
}
proc get_startup_global {varname} {
variable startup
variable configdata
set startup [dict get $configdata startup]
if {[dict exists $startup $varname]} {
return [dict get $startup $varname]
}
error "No such global configuration item '$varname' found in startup config"
}
proc get {whichconfig {globfor *}} {
variable startup
variable running
lappend PUNKARGS [list {
@id -id ::punk::config::get
@cmd -name punk::config::get -help\
"Get configuration values from a config.
Accepts globs eg XDG*"
@leaders -min 1 -max 1
whichconfig -type string -choices {config startup-configuration running-configuration}
@values -min 0 -max -1
globkey -type string -default * -optional 1 -multiple 1
}]
proc get {args} {
set argd [punk::args::parse $args withid ::punk::config::get]
lassign [dict values $argd] leaders opts values received solos
set whichconfig [dict get $leaders whichconfig]
set globs [dict get $values globkey] ;#list
variable configdata
switch -- $whichconfig {
config - startup - startup-config - startup-configuration {
config - startup-configuration {
#review 'config' ??
#show *startup* config - different behaviour may be confusing to those used to router startup and running configs
set configdata $startup
set configrecords [dict get $configdata startup]
}
running - running-config - running-configuration {
set configdata $running
running-configuration {
set configrecords [dict get $configdata running]
}
default {
error "Unknown config name '$whichconfig' - try startup or running"
}
}
if {$globfor eq "*"} {
return $configdata
if {"*" in $globs} {
return $configrecords
} else {
set keys [dict keys $configdata [string tolower $globfor]]
set keys [list]
foreach g $globs {
lappend keys {*}[dict keys $configrecords [string tolower $g]] ;#review tolower?
}
set filtered [dict create]
foreach k $keys {
dict set filtered $k [dict get $configdata $k]
dict set filtered $k [dict get $configrecords $k]
}
return $filtered
}
}
proc configure {args} {
set argdef {
lappend PUNKARGS [list {
@id -id ::punk::config::configure
@cmd -name punk::config::configure -help\
"UNIMPLEMENTED"
@values -min 1 -max 1
whichconfig -type string -choices {startup running stop}
"Get/set configuration values from a config"
@leaders -min 1 -max 1
whichconfig -type string -choices {defaults startup-configuration running-configuration}
@values -min 0 -max 2
key -type string -optional 1
newvalue -optional 1
}]
proc configure {args} {
set argd [punk::args::parse $args withid ::punk::config::configure]
lassign [dict values $argd] leaders opts values received solos
set whichconfig [dict get $argd leaders whichconfig]
variable configdata
if {"running" ni [dict keys $configdata]} {
init
Apply startup
}
set argd [punk::args::get_dict $argdef $args]
return "unimplemented - $argd"
switch -- $whichconfig {
defaults {
set configrecords [dict get $configdata defaults]
}
startup-configuration {
set configrecords [dict get $configdata startup]
}
running-configuration {
set configrecords [dict get $configdata running]
}
}
if {![dict exists $received key]} {
return $configrecords
}
set key [dict get $values key]
if {![dict exists $received newvalue]} {
return [dict get $configrecords $key]
}
error "setting value not implemented"
}
proc show {whichconfig {globfor *}} {
lappend PUNKARGS [list {
@dynamic
@id -id ::punk::config::show
@cmd -name punk::config::get -help\
"Display configuration values from a config.
Accepts globs eg XDG*"
@leaders -min 1 -max 1
}\
{${[punk::args::resolved_def -types leaders ::punk::config::get]}}\
"@values -min 0 -max -1"\
{${[punk::args::resolved_def -types values ::punk::config::get]}}\
]
proc show {args} {
#todo - tables for console
set configdata [punk::config::get $whichconfig $globfor]
return [punk::lib::showdict $configdata]
set configrecords [punk::config::get {*}$args]
return [punk::lib::showdict $configrecords]
}
@ -459,27 +623,35 @@ tcl::namespace::eval punk::config {
::tcl::namespace::eval punk::config {
#todo - something better - 'previous' rather than reverting to startup
proc channelcolors {{onoff {}}} {
variable running
variable startup
variable configdata
#variable running
#variable startup
if {![string length $onoff]} {
return [list stdout [dict get $running color_stdout] stderr [dict get $running color_stderr]]
return [list stdout [dict get $configdata running color_stdout] stderr [dict get $configdata running color_stderr]]
} else {
if {![string is boolean $onoff]} {
error "channelcolors: invalid value $onoff - expected boolean: true|false|on|off|1|0|yes|no"
}
if {$onoff} {
dict set running color_stdout [dict get $startup color_stdout]
dict set running color_stderr [dict get $startup color_stderr]
dict set configdata running color_stdout [dict get $startup color_stdout]
dict set configdata running color_stderr [dict get $startup color_stderr]
} else {
dict set running color_stdout ""
dict set running color_stderr ""
dict set configdata running color_stdout ""
dict set configdata running color_stderr ""
}
}
return [list stdout [dict get $running color_stdout] stderr [dict get $running color_stderr]]
return [list stdout [dict get $configdata running color_stdout] stderr [dict get $configdata running color_stderr]]
}
}
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::config
}
package provide punk::config [tcl::namespace::eval punk::config {
variable version
set version 0.1

6
src/bootsupport/modules/punk/mod-0.1.tm

@ -33,8 +33,7 @@ namespace eval punk::mod::cli {
return $basehelp
}
proc getraw {appname} {
upvar ::punk::config::running running_config
set app_folders [dict get $running_config apps]
set app_folders [punk::config::configure running apps]
#todo search each app folder
set bases [::list]
set versions [::list]
@ -86,8 +85,7 @@ namespace eval punk::mod::cli {
}
proc list {{glob *}} {
upvar ::punk::config::running running_config
set apps_folder [dict get $running_config apps]
set apps_folder [punk::config::configure running apps]
if {[file exists $apps_folder]} {
if {[file exists $apps_folder/$glob]} {
#tailcall source $apps_folder/$glob/main.tcl

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

@ -376,6 +376,8 @@ tcl::namespace::eval punk::ns {
#and a namespace can exist with leading colon - but is even worse - as default Tcl commands will misreport e.g namespace current within namespace eval
#The view is taken that a namespace with leading/trailing colons is so error-prone that even introspection is unreliable so we will rule that out.
#
#nsprefix is *somewhat* like 'namespace parent' execept that it is string based - ie no requirement for the namespaces to actually exist
# - this is an important usecase even if the handling of 'unwise' command names isn't so critical.
proc nsprefix {{nspath ""}} {
#normalize the common case of ::::
set nspath [string map {:::: ::} $nspath]
@ -394,6 +396,8 @@ tcl::namespace::eval punk::ns {
#namespace tail which handles :::cmd ::x:::y ::x:::/y etc in a specific manner for string processing
#review - consider making -strict raise an error for unexpected sequences such as :::: or any situation with more than 2 colons together.
#This is only necessary in the context of requirement to browse namespaces with 'unwisely' named commands
#For most purposes 'namespace tail' is fine.
proc nstail {nspath args} {
#normalize the common case of ::::
set nspath [string map {:::: ::} $nspath]
@ -2018,7 +2022,7 @@ tcl::namespace::eval punk::ns {
}
proc arginfo {args} {
lassign [dict values [punk::args::parse $args withid ::punk::ns::arginfo]] leaders opts values received
set nscaller [uplevel 1 [list ::namespace current]]
#review - setting this afterwards is an architecture smell - we should be able to override the default in the dynamic part
#todo - enable retrieving by id just the record_opts part - so we can treat as a dict directly, as well as easily apply it as a different flag name.
if {![dict exists $received -scheme]} {
@ -2086,10 +2090,12 @@ tcl::namespace::eval punk::ns {
#set numvals [expr {[llength $queryargs]+1}]
##puts "THROWBACK: namespace eval :: [list punk::ns::arginfo {*}[lrange $args 0 end-$numvals] $querycommand {*}$queryargs]"
#return [namespace eval :: [list punk::ns::arginfo {*}[lrange $args 0 end-$numvals] $querycommand {*}$queryargs]]
if {$nscaller ne "::"} {
return [namespace eval :: [list punk::ns::arginfo {*}$opts $querycommand {*}$queryargs]]
}
#set origin $querycommand
#set resolved $querycommand
set origin $querycommand
set resolved $querycommand
}
}

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

@ -175,13 +175,13 @@ tcl::namespace::eval punk::repl::codethread {
set outstack [list]
set errstack [list]
upvar ::punk::config::running running_config
if {[string length [dict get $running_config color_stdout_repl]] && [interp eval code punk::console::colour]} {
set config_running [::punk::config::configure running]
if {[string length [dict get $config_running color_stdout_repl]] && [interp eval code punk::console::colour]} {
lappend outstack [interp eval code [list ::shellfilter::stack add stdout ansiwrap -settings [list -colour [dict get $running_config color_stdout_repl]]]]
}
lappend outstack [interp eval code [list ::shellfilter::stack add stdout tee_to_var -settings {-varname ::punk::repl::codethread::output_stdout}]]
if {[string length [dict get $running_config color_stderr_repl]] && [interp eval code punk::console::colour]} {
if {[string length [dict get $config_running color_stderr_repl]] && [interp eval code punk::console::colour]} {
lappend errstack [interp eval code [list ::shellfilter::stack add stderr ansiwrap -settings [list -colour [dict get $running_config color_stderr_repl]]]]
# #lappend errstack [shellfilter::stack::add stderr ansiwrap -settings [list -colour cyan]]
}

127
src/bootsupport/modules/shellfilter-0.1.9.tm

@ -674,6 +674,9 @@ namespace eval shellfilter::chan {
#todo - track when in sixel,iterm,kitty graphics data - can be very large
method Trackcodes {chunk} {
#note - caller can use 2 resets in a single unit to temporarily reset to no sgr (override ansiwrap filter)
#e.g [a+ reset reset] (<esc><lb>0;0m vs <esc><lb>0;m)
#puts stdout "===[ansistring VIEW -lf 1 $o_buffered]"
set buf $o_buffered$chunk
set emit ""
@ -686,12 +689,29 @@ namespace eval shellfilter::chan {
#process all pt/code pairs except for trailing pt
foreach {pt code} [lrange $parts 0 end-1] {
#puts "<==[ansistring VIEW -lf 1 $pt]==>"
if {( ![llength $o_codestack] || ([llength $o_codestack] == 1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]))} {
switch -- [llength $o_codestack] {
0 {
append emit $o_do_colour$pt$o_do_normal
}
1 {
if {[punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]} {
append emit $o_do_colour$pt$o_do_normal
#append emit $pt
set o_codestack [list]
} else {
append emit $pt
#append emit [lindex $o_codestack 0]$pt
append emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$pt
}
}
default {
append emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$pt
}
}
#if {( ![llength $o_codestack] || ([llength $o_codestack] == 1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]))} {
# append emit $o_do_colour$pt$o_do_normal
# #append emit $pt
#} else {
# append emit $pt
#}
set c1c2 [tcl::string::range $code 0 1]
set leadernorm [tcl::string::range [tcl::string::map [list\
@ -740,11 +760,28 @@ namespace eval shellfilter::chan {
#puts stdout "=-=[ansistring VIEWCODES $o_buffered]"
} else {
#puts [a+ yellow]???[ansistring VIEW "'$o_buffered'<+>'$trailing_pt'"]???[a]
if {![llength $o_codestack] || ([llength $o_codestack] ==1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]])} {
switch -- [llength $o_codestack] {
0 {
append emit $o_do_colour$trailing_pt$o_do_normal
}
1 {
if {[punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]} {
append emit $o_do_colour$trailing_pt$o_do_normal
set o_codestack [list]
} else {
append emit $trailing_pt
#append emit [lindex $o_codestack 0]$trailing_pt
append emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$trailing_pt
}
}
default {
append emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$trailing_pt
}
}
#if {![llength $o_codestack] || ([llength $o_codestack] ==1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]])} {
# append emit $o_do_colour$trailing_pt$o_do_normal
#} else {
# append emit $trailing_pt
#}
#the previous o_buffered formed the data we emitted - nothing new to buffer because we emitted all parts including the trailing plaintext
set o_buffered ""
}
@ -759,11 +796,14 @@ namespace eval shellfilter::chan {
#puts "-->esc but no detect"
#no complete ansi codes - but at least one esc is present
if {[string last \x1b $buf] == [string length $buf]-1} {
#only esc is last char in buf
if {[string index $buf end] eq "\x1b" && [string first \x1b $buf] == [string length $buf]-1} {
#string index in first part of && clause to avoid some unneeded scans of whole string for this test
#we can't use 'string last' - as we need to know only esc is last char in buf
#puts ">>trailing-esc<<"
set o_buffered \x1b
set emit [string range $buf 0 end-1]
set emit $o_do_colour[string range $buf 0 end-1]$o_do_normal
#set emit [string range $buf 0 end-1]
set buf ""
} else {
set emit_anyway 0
#todo - ensure non-ansi escapes in middle of chunks don't lead to ever growing buffer
@ -774,8 +814,10 @@ namespace eval shellfilter::chan {
if {$st_partial_len < 1001} {
append o_buffered $chunk
set emit ""
set buf ""
} else {
set emit_anyway 1
set o_buffered ""
}
} else {
set possible_code_len [expr {[string length $buf] - [string last \x1b $buf]}] ;#length of possible code
@ -783,6 +825,7 @@ namespace eval shellfilter::chan {
set open_sequence_detected [punk::ansi::ta::detect_open $buf]
if {$possible_code_len > 10 && !$open_sequence_detected} {
set emit_anyway 1
set o_buffered ""
} else {
#could be composite sequence with params - allow some reasonable max sequence length
#todo - configurable max sequence length
@ -790,26 +833,49 @@ namespace eval shellfilter::chan {
# - allow some headroom for redundant codes when the caller didn't merge.
if {$possible_code_len < 101} {
append o_buffered $chunk
set buf ""
set emit ""
} else {
#allow a little more grace if we at least have an opening ansi sequence of any type..
if {$open_sequence_detected && $possible_code_len < 151} {
append o_buffered $chunk
set buf ""
set emit ""
} else {
set emit_anyway 1
set o_buffered ""
}
}
}
}
if {$emit_anyway} {
#assert: any time emit_anyway == 1 buf already contains all of previous o_buffered and o_buffered has been cleared.
#looked ansi-like - but we've given enough length without detecting close..
#treat as possible plain text with some esc or unrecognised ansi sequence
if {( ![llength $o_codestack] || ([llength $o_codestack] == 1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]))} {
switch -- [llength $o_codestack] {
0 {
set emit $o_do_colour$buf$o_do_normal
}
1 {
if {[punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]} {
set emit $o_do_colour$buf$o_do_normal
set o_codestack [list]
} else {
set emit $buf
#set emit [lindex $o_codestack 0]$buf
set emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$buf
}
}
default {
#set emit [punk::ansi::codetype::sgr_merge_singles $o_codestack]$buf
set emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$buf
}
}
#if {( ![llength $o_codestack] || ([llength $o_codestack] == 1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]))} {
# set emit $o_do_colour$buf$o_do_normal
#} else {
# set emit $buf
#}
}
}
}
@ -817,12 +883,24 @@ namespace eval shellfilter::chan {
#no esc
#puts stdout [a+ yellow]...[a]
#test!
if {( ![llength $o_codestack] || ([llength $o_codestack] == 1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]))} {
switch -- [llength $o_codestack] {
0 {
set emit $o_do_colour$buf$o_do_normal
}
1 {
if {[punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]} {
set emit $o_do_colour$buf$o_do_normal
set o_codestack [list]
} else {
set emit $buf
#set emit [lindex $o_codestack 0]$buf
set emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$buf
}
}
default {
#set emit [punk::ansi::codetype::sgr_merge_singles $o_codestack]$buf
set emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$buf
}
}
#set emit $buf
set o_buffered ""
}
return [dict create emit $emit stacksize [llength $o_codestack]]
@ -856,13 +934,22 @@ namespace eval shellfilter::chan {
set instring [tcl::encoding::convertfrom $o_enc $bytes]
set streaminfo [my Trackcodes $instring]
set emit [dict get $streaminfo emit]
if {[dict get $streaminfo stacksize] == 0} {
#no ansi on the stack - we can wrap
#review
set outstring "$o_do_colour$emit$o_do_normal"
} else {
#review - wrapping already done in Trackcodes
#if {[dict get $streaminfo stacksize] == 0} {
# #no ansi on the stack - we can wrap
# #review
# set outstring "$o_do_colour$emit$o_do_normal"
#} else {
#}
#if {[llength $o_codestack]} {
# set outstring [punk::ansi::codetype::sgr_merge_singles $o_codestack]$emit
#} else {
# set outstring $emit
#}
set outstring $emit
}
#puts stdout "decoded >>>[ansistring VIEWCODES $outstring]<<<"
#puts stdout "re-encoded>>>[ansistring VIEW [tcl::encoding::convertto $o_enc $outstring]]<<<"
return [tcl::encoding::convertto $o_enc $outstring]
@ -2260,7 +2347,7 @@ namespace eval shellfilter {
#
if {!$is_script} {
set experiment 0
if $experiment {
if {$experiment} {
try {
set results [exec {*}$commandlist]
set exitinfo [list exitcode 0]

BIN
src/bootsupport/modules/test/tomlish-1.1.5.tm

Binary file not shown.

2
src/bootsupport/modules/textblock-0.1.3.tm

@ -4301,7 +4301,7 @@ tcl::namespace::eval textblock {
if {[dict get $opts -frame]} {
#set output [textblock::frame -ansiborder [a+ {*}$fc Black web-cornflowerblue] -type heavy -title "[a+ {*}$fc Black] Periodic Table " [$t print]]
#set output [textblock::frame -ansiborder [a+ {*}$fc Black term-deepskyblue2] -type heavy -title "[a+ {*}$fc Black] Periodic Table " [$t print]]
set output [textblock::frame -checkargs 0 -ansiborder [a+ {*}$fc Black cyan] -type heavy -title "[a+ {*}$fc Black] Periodic Table " [$t print]]
set output [textblock::frame -checkargs 0 -ansiborder [a+ {*}$fc Black cyan] -type heavy -title "[a+ {*}$fc Black] Periodic Table [a]" [$t print]]
} else {
set output [$t print]
}

1246
src/bootsupport/modules/tomlish-1.1.6.tm

File diff suppressed because it is too large Load Diff

67
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/aliascore-0.1.0.tm

@ -103,7 +103,9 @@ tcl::namespace::eval punk::aliascore {
#use absolute ns ie must be prefixed with ::
#single element commands are imported if source command already exists, otherwise aliased. multi element commands are aliased
#functions must be in export list of their source namespace
#functions should generally be covered by one of the export patterns of their source namespace
# - if they are not - e.g (separately loaded ensemble command ?)
# the aliascore::init will temporarily extend the exports list to do the import, and then reset the exports to how they were.
set aliases [tcl::dict::create\
val ::punk::pipe::val\
aliases ::punk::lib::aliases\
@ -122,8 +124,8 @@ tcl::namespace::eval punk::aliascore {
stripansi ::punk::ansi::ansistrip\
ansiwrap ::punk::ansi::ansiwrap\
colour ::punk::console::colour\
ansi ::punk::console::ansi\
color ::punk::console::colour\
ansi ::punk::console::ansi\
a? ::punk::console::code_a?\
A? {::punk::console::code_a? forcecolor}\
a+ ::punk::console::code_a+\
@ -132,6 +134,7 @@ tcl::namespace::eval punk::aliascore {
A {::punk::console::code_a forcecolour}\
smcup ::punk::console::enable_alt_screen\
rmcup ::punk::console::disable_alt_screen\
config ::punk::config\
]
#*** !doctools
@ -153,6 +156,35 @@ tcl::namespace::eval punk::aliascore {
# return "ok"
#}
proc _is_exported {ns cmd} {
set exports [::tcl::namespace::eval $ns [list namespace export]]
set is_exported 0
foreach p $exports {
if {[string match $p $cmd]} {
set is_exported 1
break
}
}
return $is_exported
}
#_nsprefix accepts entire command - not just an existing namespace for which we want the parent
proc _nsprefix {{nspath {}}} {
#maintenance: from punk::ns::nsprefix - (without unnecessary nstail)
#normalize the common case of ::::
set nspath [string map {:::: ::} $nspath]
set rawprefix [string range $nspath 0 end-[string length [namespace tail $nspath]]]
if {$rawprefix eq "::"} {
return $rawprefix
} else {
if {[string match *:: $rawprefix]} {
return [string range $rawprefix 0 end-2]
} else {
return $rawprefix
}
}
}
#todo - options as to whether we should raise an error if collisions found, undo aliases etc?
proc init {args} {
set defaults {-force 0}
@ -195,6 +227,7 @@ tcl::namespace::eval punk::aliascore {
error "punk::aliascore::init declined to create any aliases or imports because -force == 0 and conflicts found:$conflicts"
}
set failed [list]
set tempns ::temp_[info cmdcount] ;#temp ns for renames
dict for {a cmd} $aliases {
#puts "aliascore $a -> $cmd"
@ -206,16 +239,36 @@ tcl::namespace::eval punk::aliascore {
} else {
if {[tcl::info::commands $cmd] ne ""} {
#todo - ensure exported? noclobber?
if {[tcl::namespace::tail $a] eq [tcl::namespace::tail $cmd]} {
set container_ns [_nsprefix $cmd]
set cmdtail [tcl::namespace::tail $cmd]
set was_exported 1 ;#assumption
if {![_is_exported $container_ns $cmdtail]} {
set was_exported 0
set existing_exports [tcl::namespace::eval $container_ns [list ::namespace export]]
tcl::namespace::eval $container_ns [list ::namespace export $cmdtail]
}
if {[tcl::namespace::tail $a] eq $cmdtail} {
#puts stderr "importing $cmd"
tcl::namespace::eval :: [list namespace import $cmd]
try {
tcl::namespace::eval :: [list ::namespace import $cmd]
} trap {} {emsg eopts} {
lappend failed [list alias $a target $cmd errormsg $emsg]
}
} else {
#target command name differs from exported name
#e.g stripansi -> punk::ansi::ansistrip
#import and rename
#puts stderr "importing $cmd (with rename to ::$a)"
tcl::namespace::eval $tempns [list namespace import $cmd]
catch {rename ${tempns}::[namespace tail $cmd] ::$a}
try {
tcl::namespace::eval $tempns [list ::namespace import $cmd]
} trap {} {emsg eopst} {
lappend failed [list alias $a target $cmd errormsg $emsg]
}
catch {rename ${tempns}::$cmdtail ::$a}
}
#restore original exports
if {!$was_exported} {
tcl::namespace::eval $container_ns [list ::namespace export -clear {*}$existing_exports]
}
} else {
interp alias {} $a {} {*}$cmd
@ -223,7 +276,7 @@ tcl::namespace::eval punk::aliascore {
}
}
#tcl::namespace::delete $tempns
return [dict create aliases [dict keys $aliases] unchanged $ignore_aliases changed $conflicts]
return [dict create aliases [dict keys $aliases] existing $existing ignored $ignore_aliases changed $conflicts failed $failed]
}

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

@ -3357,9 +3357,20 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
lappend PUNKARGS [list {
@id -id ::punk::ansi::ansiwrap
@cmd -name punk::ansi::ansiwrap -help\
"Wrap a string with ANSI codes from
{Wrap a string with ANSI codes from
supplied codelist(s) followed by trailing
ANSI reset.
ANSI reset. The wrapping is done such that
after every reset in the supplied text, the
default goes back to the supplied codelist.
e.g1 in the following
ansiwrap red bold "rrr[a+ green]ggg[a]rrr"
both strings rrr will be red & bold
e.g2 bolding and underlining specific text whilst dimming the rest
ansiwrap dim [string map [list test [ansiwrap bold underline test]] "A test string"]
e.g3 reverse render a complex ansi substring
ansiwrap reverse [textblock::periodic]
Codes are numbers or strings as indicated
in the output of the colour information
@ -3372,41 +3383,172 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
For finer control use the a+ and a
functions eg
set x \"[a+ red]text [a+ bold]etc[a]\"
"
set x "[a+ red]text [a+ bold]etc[a]"
}
@leaders -min 0 -max -1
codelist -multiple 1 -default {} -type list -help\
"ANSI names/ints as understood by 'a?'
(Not actual ANSI as output by a+)
These can be supplied individually or
as a list or lists"
@opts
-rawansi -type ansi -default ""
-resetcodes -type list -default {reset}
-rawresets -type ansi -default ""
-fullcodemerge -type boolean -default 0 -help\
"experimental"
-overridecodes -type list -default {}
@values -min 1 -max 1
text -type string -help\
"String to wrap with ANSI (SGR)"
}]
#proc ansiwrap {codes text} {
# return [a {*}$codes]$text[a]
#}
proc ansiwrap2 {args} {
set argd [punk::args::parse $args withid ::punk::ansi::ansiwrap]
set codelists [dict get $argd leaders codelist]
set text [dict get $argd values text]
set codes [concat {*}$codelists] ;#flatten
return [a {*}$codes]$text[a]
}
proc ansiwrap {args} {
if {[llength $args] < 1} {
#minimal args parsing - unhappy path only
#throw to args::parse to get friendly error/usage display
punk::args::parse $args withid ::punk::ansi::ansiwrap
return
}
#we know there are no valid codes that start with -
if {[lsearch [lrange $args 0 end-1] -*] == -1} {
#no opts
set text [lindex $args end]
set codelists [lrange $args 0 end-1]
set R [a] ;#plain ansi reset
set rawansi ""
set rawresets ""
set fullmerge 0
set overrides ""
} else {
set argd [punk::args::parse $args withid ::punk::ansi::ansiwrap]
lassign [dict values $argd] leaders opts values received solos
set codelists [dict get $leaders codelist]
set text [dict get $values text]
set rawansi [dict get $opts -rawansi]
set R [a+ {*}[dict get $opts -resetcodes]]
set rawresets [dict get $opts -rawresets]
set fullmerge [dict get $opts -fullcodemerge]
set overrides [punk::ansi::ta::get_codes_single [a+ {*}[dict get $opts -overridecodes]]]
}
#note that the result of any sgr_merge or sgr_merge_singles is not necessarily a single Ansi escape sequence.
#there can be SGR unmergeables (due to enhanced underlines) as well as non SGR codes
set codes [concat {*}$codelists] ;#flatten
return [a {*}$codes]$text[a]
set base [a+ {*}$codes]
if {$rawansi ne ""} {
set rawcodes [punk::ansi::ta::get_codes_single $rawansi] ;#caller may have supplied as [a+ xxx][a+ yyy]
if {$fullmerge} {
set base [punk::ansi::codetype::sgr_merge [list $base {*}$rawcodes]]
} else {
set base [punk::ansi::codetype::sgr_merge_singles [list $base {*}$rawcodes]]
}
}
if {$rawresets ne ""} {
set rawresetcodes [punk::ansi::ta::get_codes_single $rawresets]
if {$fullmerge} {
set R [punk::ansi::codetype::sgr_merge [list $R {*}$rawresetcodes]]
} else {
set R [punk::ansi::codetype::sgr_merge_singles [list $R {*}$rawresetcodes]]
}
}
set codestack [list]
if {[punk::ansi::ta::detect $text]} {
set emit ""
set parts [punk::ansi::ta::split_codes $text]
foreach {pt code} $parts {
switch -- [llength $codestack] {
0 {
append emit $base$pt$R
}
1 {
if {[punk::ansi::codetype::is_sgr_reset [lindex $codestack 0]]} {
append emit $base$pt$R
set codestack [list]
} else {
#append emit [lindex $o_codestack 0]$pt
if {$fullmerge} {
append emit [punk::ansi::codetype::sgr_merge [list $base {*}$codestack {*}$overrides]]$pt$R
} else {
append emit [punk::ansi::codetype::sgr_merge_singles [list $base {*}$codestack {*}$overrides]]$pt$R
}
}
}
default {
if {$fullmerge} {
append emit [punk::ansi::codetype::sgr_merge [list $base {*}$codestack {*}$overrides]]$pt$R
} else {
append emit [punk::ansi::codetype::sgr_merge_singles [list $base {*}$codestack {*}$overrides]]$pt$R
}
}
}
#parts ends on a pt - last code always empty string
if {$code ne ""} {
set c1c2 [tcl::string::range $code 0 1]
set leadernorm [tcl::string::range [tcl::string::map [list\
\x1b\[ 7CSI\
\x9b 8CSI\
\x1b\( 7GFX\
] $c1c2] 0 3]
switch -- $leadernorm {
7CSI - 8CSI {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set codestack [list "\x1b\[m"]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set codestack [list $code]
} elseif {[punk::ansi::codetype::is_sgr $code]} {
#todo - make caching is_sgr method
set dup_posns [lsearch -all -exact $codestack $code]
set codestack [lremove $codestack {*}$dup_posns]
lappend codestack $code
} else {
}
}
7GFX {
switch -- [tcl::string::index $code 2] {
"0" {
set o_gx_state on
}
"B" {
set o_gx_state off
}
}
}
default {
#other ansi codes
}
}
append emit $code
}
}
return $emit$R
} else {
return $base$text$R
}
}
proc ansiwrap_naive {codes text} {
return [a_ {*}$codes]$text[a]
}
#a silly trick... temporary? probably - todo - tests and work on sgr_merge + sgr_merge_singles before relying on this
#when we use sgr_merge_singles on a 'single' containing a non SGR code e.g <esc>[5h (inverse) it puts this code at the end of the list
#furthermore - it carries any SGR codes along with it (Can/should we rely on this behaviour??? probably not) REVIEW
#P% ansistring VIEW $s1
#- ␛[31m␛[?5h
#P% ansistring VIEW [punk::ansi::codetype::sgr_merge_singles [list $s1 [a+ cyan]]]
#- ␛[36m␛[31m␛[?5h
#P% ansistring VIEW [punk::ansi::codetype::sgr_merge [list $s1 [a+ cyan]]]
#- ␛[36m␛[?5h
#we can use this trick to override background and/or foreground colours using ansiwrap - which uses sgr_merge_singles
#Note - this trick is not composable - e.g ansioverride Red [ansiioverride Green [textblock::periodic]] doesn't work as expected.
proc ansioverride2 {args} {
set text [lindex $args end]
set codes [lrange $args 0 end-1]
ansiwrap {*}$codes -rawansi [punk::ansi::enable_inverse] -rawresets [punk::ansi::disable_inverse] $text
}
proc ansireverse {text} {
ansioverride2 normal reverse $text
}
proc get_code_name {code} {
#*** !doctools
@ -4491,6 +4633,77 @@ tcl::namespace::eval punk::ansi {
return 0
}
}
#e.g has_any_effective $str bg fg
proc has_any_effective {str args} {
set singlecodes [punk::ansi::ta::get_codes_single $str]
set mergeinfo [punk::ansi::codetype::sgr_merge_singles $singlecodes -info 1]
foreach t $args {
switch -- $t {
sgr - unmergeable - othercodes {
if {[dict get $mergeinfo $t] ne ""} {
return 1
}
}
intensity - italic - underline - underextended - blink - reverse - hide - strike - font - gothic - doubleunderline
- proportional - frame_or_circle - ideogram_underline - ideogram_doubleunderline - ideogram_clear - overline - underlinecolour - superscript - subscript
- nosupersub - fg - bg {
if {[dict get $mergeinfo codestate $t] ne ""} {
return 1
}
}
bold {
if {[dict get $mergeinfo codestate intensity] eq "1"} {
return 1
}
}
dim {
if {[dict get $mergeinfo codestate intensity] eq "2"} {
return 1
}
}
default {
error "punk::ansi::ta::has_any_effective invalid type '$t' specified"
}
}
}
return 0
}
proc has_all_effective {str args} {
set singlecodes [punk::ansi::ta::get_codes_single $str]
set mergeinfo [punk::ansi::codetype::sgr_merge_singles $singlecodes -info 1]
foreach t $args {
switch -- $t {
sgr - unmergeable - othercodes {
if {[dict get $mergeinfo $t] eq ""} {
return 0
}
}
intensity - italic - underline - underextended - blink - reverse - hide - strike - font - gothic - doubleunderline
- proportional - frame_or_circle - ideogram_underline - ideogram_doubleunderline - ideogram_clear - overline - underlinecolour - superscript - subscript
- nosupersub - fg - bg {
if {[dict get $mergeinfo codestate $t] eq ""} {
return 0
}
}
bold {
if {[dict get $mergeinfo codestate intensity] ne "1"} {
return 0
}
}
dim {
if {[dict get $mergeinfo codestate intensity] ne "2"} {
return 0
}
}
default {
error "punk::ansi::ta::has_any_effective invalid type '$t' specified"
}
}
}
return 1
}
proc is_gx {code} {
#g0 {(?:\x1b\(0)(?:(?!\x1b\(B).)*\x1b\(B}
#g1 {(?:\x1b\)0)(?:(?!\x1b\)B).)*\x1b\)B}
@ -4513,6 +4726,7 @@ tcl::namespace::eval punk::ansi {
set codestate_empty [tcl::dict::create]
tcl::dict::set codestate_empty rst "" ;#0 (or empty)
tcl::dict::set codestate_empty intensity "" ;#1 bold, 2 dim, 22 normal
tcl::dict::set codestate_empty shadowed "" ;
tcl::dict::set codestate_empty italic "" ;#3 on 23 off
tcl::dict::set codestate_empty underline "" ;#4 on 24 off

31
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/args-0.1.4.tm

@ -3226,7 +3226,36 @@ tcl::namespace::eval punk::args {
form1: parse $arglist ?-flag val?... withid $id
form2: parse $arglist ?-flag val?... withdef $def ?$def?
see punk::args::define"
see punk::args::define
Returns a dict of information regarding the parsed arguments
example of basic usage for single option only:
punk::args::define {
@id -id ::myns::myfunc
@cmd -name myns::myfunc
@leaders -min 0 -max 0
@opts
-configfile -type existingfile
#type none makes it a solo flag
-verbose -type none
@values -min 0 -max 0
}
proc myfunc {args} {
set argd [punk::args::parse $args withid ::myns::myfunc]
lassign [dict values $argd] leaders opts values received solos
if {[dict exists $received] -configfile} {
puts \"have option for existing file [dict get $opts -configfile]\"
}
}
The leaders, opts, values keys in the parse result dict are proper dicts.
The received key is dict-like but can have repeated keys for arguments than can
accept multiples. The value for each received element is the ordinal position.
The solos key refers to a list of solo flags received (those specified with
-type none). This is generally only useful to assist in passing arguments on
to another procedure which also requires solos, because the opts dict contains
solo flags with a 1 value or a list of 1's if it was a solo with -multiple true
specified.
"
@form -form {withid withdef}
@leaders -min 1 -max 1
arglist -type list -optional 0 -help\

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

@ -1,23 +1,109 @@
tcl::namespace::eval punk::config {
variable loaded
variable startup ;#include env overrides
variable running
variable configdata [dict create] ;#key on config names. At least default, startup, running
#variable startup ;#include env overrides
#variable running
variable punk_env_vars
variable other_env_vars
variable vars
namespace export {[a-z]*}
namespace ensemble create
namespace eval punk {namespace export config}
proc _homedir {} {
if {[info exists ::env(HOME)]} {
set home [file normalize $::env(HOME)]
} else {
#not available on 8.6? ok will error out here.
set home [file tildeexpand ~]
}
return $home
}
lappend PUNKARGS [list {
@id -id ::punk::config::dir
@cmd -name punk::config::dir -help\
"Get the path for the default config folder
Config files are in toml format.
The XDG_CONFIG_HOME env var is the preferred
choice of location.
A folder under the user's home directory,
at .config/punk/shell is chosen if
XDG_CONFIG_HOME is not configured.
"
@leaders -min 0 -max 0
@opts
-quiet -type none -help\
"Suppress warning given when the folder does
not yet exist"
@values -min 0 -max 0
}]
proc dir {args} {
if {"-quiet" in $args} {
set be_quiet [dict exists $received -quiet]
}
set was_noisy 0
set config_home [punk::config::configure running xdg_config_home]
set config_dir [file join $config_home punk shell]
if {!$be_quiet && ![file exists $config_dir]} {
set msg "punk::shell data storage folder at $config_dir does not yet exist."
puts stderr $msg
set was_noisy 1
}
if {!$be_quiet && $was_noisy} {
puts stderr "punk::config::dir - call with -quiet option to suppress these messages"
}
return $config_dir
#if {[info exists ::env(XDG_CONFIG_HOME)]} {
# set config_home $::env(XDG_CONFIG_HOME)
#} else {
# set config_home [file join [_homedir] .config]
# if {!$be_quiet} {
# puts stderr "Environment variable XDG_CONFIG_HOME does not exist - consider setting it if $config_home is not a suitable location"
# set was_noisy 1
# }
#}
#if {!$be_quiet && ![file exists $config_home]} {
# #parent folder for 'punk' config dir doesn't exist
# set msg "configuration location (XDG_CONFIG_HOME or ~/.config) $config_home does not yet exist"
# append msg \n " - please create it and/or set XDG_CONFIG_HOME env var."
# puts stderr $msg
# set was_noisy 1
#}
#set config_dir [file join $config_home punk shell]
#if {!$be_quiet && ![file exists $config_dir]} {
# set msg "punk::shell data storage folder at $config_dir does not yet exist."
# append msg \n " It will be created if api_context_save is called without specifying an alternate location."
# puts stderr $msg
# set was_noisy 1
#}
#if {!$be_quiet && $was_noisy} {
# puts stderr "punk::config::dir - call with -quiet option to suppress these messages"
#}
#return [file join $configdir config.toml]
}
#todo - XDG_DATA_HOME etc
#https://specifications.freedesktop.org/basedir-spec/latest/
# see also: http://hiphish.github.io/blog/2020/08/30/dotfiles-were-a-mistake/
proc init {} {
variable defaults
variable startup
variable running
variable configdata
#variable defaults
#variable startup
#variable running
variable punk_env_vars
variable punk_env_vars_config
variable other_env_vars
@ -108,12 +194,14 @@ tcl::namespace::eval punk::config {
#we have a choice of LOCALAPPDATA vs APPDATA (local to machine vs potentially roaming/redirected in a corporate environment)
#using the roaming location should not impact users who aren't using a domain controller but is potentially much more convenient for those who do.
if {[info exists ::env(APPDATA)]} {
#Typical existing/default value for env(APPDATA) on windows is c:\Users\<username>\AppData\Roaming
set default_xdg_config_home $::env(APPDATA)
set default_xdg_data_home $::env(APPDATA)
}
#The xdg_cache_home should be kept local
if {[info exists ::env(LOCALAPPDATA)]} {
#Typical existing/default value for env(APPDATA) on windows is c:\Users\<username>\AppData\Local
set default_xdg_data_home $::env(LOCALAPPDATA)
set default_xdg_cache_home $::env(LOCALAPPDATA)
set default_xdg_state_home $::env(LOCALAPPDATA)
}
@ -133,10 +221,10 @@ tcl::namespace::eval punk::config {
}
}
set defaults [dict create\
dict set configdata defaults [dict create\
apps $default_apps\
config ""\
configset ".punkshell"\
config "startup"\
configset "main"\
scriptlib $default_scriptlib\
color_stdout $default_color_stdout\
color_stdout_repl $default_color_stdout_repl\
@ -160,7 +248,7 @@ tcl::namespace::eval punk::config {
posh_themes_path ""\
]
set startup $defaults
dict set configdata startup [dict get $configdata defaults]
#load values from saved config file - $xdg_config_home/punk/punk.config ?
#typically we want env vars to override the stored config - as env vars conventionally used on some commandlines.
#that's possibly ok for the PUNK_ vars
@ -219,9 +307,9 @@ tcl::namespace::eval punk::config {
lappend final $p
}
}
tcl::dict::set startup $varname $final
tcl::dict::set configdata startup $varname $final
} else {
tcl::dict::set startup $varname $f
tcl::dict::set configdata startup $varname $f
}
}
}
@ -273,29 +361,44 @@ tcl::namespace::eval punk::config {
lappend final $p
}
}
tcl::dict::set startup $varname $final
tcl::dict::set configdata startup $varname $final
} else {
tcl::dict::set startup $varname $f
tcl::dict::set configdata startup $varname $f
}
}
}
}
set config_home [dict get $configdata startup xdg_config_home]
if {![file exists $config_home]} {
puts stderr "punk::config::init creating punk shell config dir: [dir]"
puts stderr "(todo)"
}
set configset [dict get $configdata defaults configset]
set config [dict get $configdata defaults config]
set startupfile [file join $config_home $configset $config.toml]
if {![file exists $startupfile]} {
puts stderr "punk::config::init creating punk shell config file: $config for configset: $configset"
puts stderr "(todo)"
}
#unset -nocomplain vars
#todo
set running [tcl::dict::create]
set running [tcl::dict::merge $running $startup]
dict set configdata running [tcl::dict::merge $running [dict get $configdata startup]]
}
init
#todo
proc Apply {config} {
variable configdata
puts stderr "punk::config::Apply partially implemented"
set configname [string map {-config ""} $config]
if {$configname in {startup running}} {
upvar ::punk::config::$configname applyconfig
set applyconfig [dict get $configdata $configname]
if {[dict exists $applyconfig auto_noexec]} {
set auto [dict get $applyconfig auto_noexec]
@ -315,67 +418,128 @@ tcl::namespace::eval punk::config {
}
return "apply done"
}
Apply startup
#todo - consider how to divide up settings, categories, 'devices', decks etc
proc get_running_global {varname} {
variable running
variable configdata
set running [dict get $configdata running]
if {[dict exists $running $varname]} {
return [dict get $running $varname]
}
error "No such global configuration item '$varname' found in running config"
}
proc get_startup_global {varname} {
variable startup
variable configdata
set startup [dict get $configdata startup]
if {[dict exists $startup $varname]} {
return [dict get $startup $varname]
}
error "No such global configuration item '$varname' found in startup config"
}
proc get {whichconfig {globfor *}} {
variable startup
variable running
lappend PUNKARGS [list {
@id -id ::punk::config::get
@cmd -name punk::config::get -help\
"Get configuration values from a config.
Accepts globs eg XDG*"
@leaders -min 1 -max 1
whichconfig -type string -choices {config startup-configuration running-configuration}
@values -min 0 -max -1
globkey -type string -default * -optional 1 -multiple 1
}]
proc get {args} {
set argd [punk::args::parse $args withid ::punk::config::get]
lassign [dict values $argd] leaders opts values received solos
set whichconfig [dict get $leaders whichconfig]
set globs [dict get $values globkey] ;#list
variable configdata
switch -- $whichconfig {
config - startup - startup-config - startup-configuration {
config - startup-configuration {
#review 'config' ??
#show *startup* config - different behaviour may be confusing to those used to router startup and running configs
set configdata $startup
set configrecords [dict get $configdata startup]
}
running - running-config - running-configuration {
set configdata $running
running-configuration {
set configrecords [dict get $configdata running]
}
default {
error "Unknown config name '$whichconfig' - try startup or running"
}
}
if {$globfor eq "*"} {
return $configdata
if {"*" in $globs} {
return $configrecords
} else {
set keys [dict keys $configdata [string tolower $globfor]]
set keys [list]
foreach g $globs {
lappend keys {*}[dict keys $configrecords [string tolower $g]] ;#review tolower?
}
set filtered [dict create]
foreach k $keys {
dict set filtered $k [dict get $configdata $k]
dict set filtered $k [dict get $configrecords $k]
}
return $filtered
}
}
proc configure {args} {
set argdef {
lappend PUNKARGS [list {
@id -id ::punk::config::configure
@cmd -name punk::config::configure -help\
"UNIMPLEMENTED"
@values -min 1 -max 1
whichconfig -type string -choices {startup running stop}
"Get/set configuration values from a config"
@leaders -min 1 -max 1
whichconfig -type string -choices {defaults startup-configuration running-configuration}
@values -min 0 -max 2
key -type string -optional 1
newvalue -optional 1
}]
proc configure {args} {
set argd [punk::args::parse $args withid ::punk::config::configure]
lassign [dict values $argd] leaders opts values received solos
set whichconfig [dict get $argd leaders whichconfig]
variable configdata
if {"running" ni [dict keys $configdata]} {
init
Apply startup
}
set argd [punk::args::get_dict $argdef $args]
return "unimplemented - $argd"
switch -- $whichconfig {
defaults {
set configrecords [dict get $configdata defaults]
}
startup-configuration {
set configrecords [dict get $configdata startup]
}
running-configuration {
set configrecords [dict get $configdata running]
}
}
if {![dict exists $received key]} {
return $configrecords
}
set key [dict get $values key]
if {![dict exists $received newvalue]} {
return [dict get $configrecords $key]
}
error "setting value not implemented"
}
proc show {whichconfig {globfor *}} {
lappend PUNKARGS [list {
@dynamic
@id -id ::punk::config::show
@cmd -name punk::config::get -help\
"Display configuration values from a config.
Accepts globs eg XDG*"
@leaders -min 1 -max 1
}\
{${[punk::args::resolved_def -types leaders ::punk::config::get]}}\
"@values -min 0 -max -1"\
{${[punk::args::resolved_def -types values ::punk::config::get]}}\
]
proc show {args} {
#todo - tables for console
set configdata [punk::config::get $whichconfig $globfor]
return [punk::lib::showdict $configdata]
set configrecords [punk::config::get {*}$args]
return [punk::lib::showdict $configrecords]
}
@ -459,27 +623,35 @@ tcl::namespace::eval punk::config {
::tcl::namespace::eval punk::config {
#todo - something better - 'previous' rather than reverting to startup
proc channelcolors {{onoff {}}} {
variable running
variable startup
variable configdata
#variable running
#variable startup
if {![string length $onoff]} {
return [list stdout [dict get $running color_stdout] stderr [dict get $running color_stderr]]
return [list stdout [dict get $configdata running color_stdout] stderr [dict get $configdata running color_stderr]]
} else {
if {![string is boolean $onoff]} {
error "channelcolors: invalid value $onoff - expected boolean: true|false|on|off|1|0|yes|no"
}
if {$onoff} {
dict set running color_stdout [dict get $startup color_stdout]
dict set running color_stderr [dict get $startup color_stderr]
dict set configdata running color_stdout [dict get $startup color_stdout]
dict set configdata running color_stderr [dict get $startup color_stderr]
} else {
dict set running color_stdout ""
dict set running color_stderr ""
dict set configdata running color_stdout ""
dict set configdata running color_stderr ""
}
}
return [list stdout [dict get $running color_stdout] stderr [dict get $running color_stderr]]
return [list stdout [dict get $configdata running color_stdout] stderr [dict get $configdata running color_stderr]]
}
}
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::config
}
package provide punk::config [tcl::namespace::eval punk::config {
variable version
set version 0.1

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

@ -33,8 +33,7 @@ namespace eval punk::mod::cli {
return $basehelp
}
proc getraw {appname} {
upvar ::punk::config::running running_config
set app_folders [dict get $running_config apps]
set app_folders [punk::config::configure running apps]
#todo search each app folder
set bases [::list]
set versions [::list]
@ -86,8 +85,7 @@ namespace eval punk::mod::cli {
}
proc list {{glob *}} {
upvar ::punk::config::running running_config
set apps_folder [dict get $running_config apps]
set apps_folder [punk::config::configure running apps]
if {[file exists $apps_folder]} {
if {[file exists $apps_folder/$glob]} {
#tailcall source $apps_folder/$glob/main.tcl

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

@ -376,6 +376,8 @@ tcl::namespace::eval punk::ns {
#and a namespace can exist with leading colon - but is even worse - as default Tcl commands will misreport e.g namespace current within namespace eval
#The view is taken that a namespace with leading/trailing colons is so error-prone that even introspection is unreliable so we will rule that out.
#
#nsprefix is *somewhat* like 'namespace parent' execept that it is string based - ie no requirement for the namespaces to actually exist
# - this is an important usecase even if the handling of 'unwise' command names isn't so critical.
proc nsprefix {{nspath ""}} {
#normalize the common case of ::::
set nspath [string map {:::: ::} $nspath]
@ -394,6 +396,8 @@ tcl::namespace::eval punk::ns {
#namespace tail which handles :::cmd ::x:::y ::x:::/y etc in a specific manner for string processing
#review - consider making -strict raise an error for unexpected sequences such as :::: or any situation with more than 2 colons together.
#This is only necessary in the context of requirement to browse namespaces with 'unwisely' named commands
#For most purposes 'namespace tail' is fine.
proc nstail {nspath args} {
#normalize the common case of ::::
set nspath [string map {:::: ::} $nspath]
@ -2018,7 +2022,7 @@ tcl::namespace::eval punk::ns {
}
proc arginfo {args} {
lassign [dict values [punk::args::parse $args withid ::punk::ns::arginfo]] leaders opts values received
set nscaller [uplevel 1 [list ::namespace current]]
#review - setting this afterwards is an architecture smell - we should be able to override the default in the dynamic part
#todo - enable retrieving by id just the record_opts part - so we can treat as a dict directly, as well as easily apply it as a different flag name.
if {![dict exists $received -scheme]} {
@ -2086,10 +2090,12 @@ tcl::namespace::eval punk::ns {
#set numvals [expr {[llength $queryargs]+1}]
##puts "THROWBACK: namespace eval :: [list punk::ns::arginfo {*}[lrange $args 0 end-$numvals] $querycommand {*}$queryargs]"
#return [namespace eval :: [list punk::ns::arginfo {*}[lrange $args 0 end-$numvals] $querycommand {*}$queryargs]]
if {$nscaller ne "::"} {
return [namespace eval :: [list punk::ns::arginfo {*}$opts $querycommand {*}$queryargs]]
}
#set origin $querycommand
#set resolved $querycommand
set origin $querycommand
set resolved $querycommand
}
}

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

@ -175,13 +175,13 @@ tcl::namespace::eval punk::repl::codethread {
set outstack [list]
set errstack [list]
upvar ::punk::config::running running_config
if {[string length [dict get $running_config color_stdout_repl]] && [interp eval code punk::console::colour]} {
set config_running [::punk::config::configure running]
if {[string length [dict get $config_running color_stdout_repl]] && [interp eval code punk::console::colour]} {
lappend outstack [interp eval code [list ::shellfilter::stack add stdout ansiwrap -settings [list -colour [dict get $running_config color_stdout_repl]]]]
}
lappend outstack [interp eval code [list ::shellfilter::stack add stdout tee_to_var -settings {-varname ::punk::repl::codethread::output_stdout}]]
if {[string length [dict get $running_config color_stderr_repl]] && [interp eval code punk::console::colour]} {
if {[string length [dict get $config_running color_stderr_repl]] && [interp eval code punk::console::colour]} {
lappend errstack [interp eval code [list ::shellfilter::stack add stderr ansiwrap -settings [list -colour [dict get $running_config color_stderr_repl]]]]
# #lappend errstack [shellfilter::stack::add stderr ansiwrap -settings [list -colour cyan]]
}

127
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/shellfilter-0.1.9.tm

@ -674,6 +674,9 @@ namespace eval shellfilter::chan {
#todo - track when in sixel,iterm,kitty graphics data - can be very large
method Trackcodes {chunk} {
#note - caller can use 2 resets in a single unit to temporarily reset to no sgr (override ansiwrap filter)
#e.g [a+ reset reset] (<esc><lb>0;0m vs <esc><lb>0;m)
#puts stdout "===[ansistring VIEW -lf 1 $o_buffered]"
set buf $o_buffered$chunk
set emit ""
@ -686,12 +689,29 @@ namespace eval shellfilter::chan {
#process all pt/code pairs except for trailing pt
foreach {pt code} [lrange $parts 0 end-1] {
#puts "<==[ansistring VIEW -lf 1 $pt]==>"
if {( ![llength $o_codestack] || ([llength $o_codestack] == 1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]))} {
switch -- [llength $o_codestack] {
0 {
append emit $o_do_colour$pt$o_do_normal
}
1 {
if {[punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]} {
append emit $o_do_colour$pt$o_do_normal
#append emit $pt
set o_codestack [list]
} else {
append emit $pt
#append emit [lindex $o_codestack 0]$pt
append emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$pt
}
}
default {
append emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$pt
}
}
#if {( ![llength $o_codestack] || ([llength $o_codestack] == 1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]))} {
# append emit $o_do_colour$pt$o_do_normal
# #append emit $pt
#} else {
# append emit $pt
#}
set c1c2 [tcl::string::range $code 0 1]
set leadernorm [tcl::string::range [tcl::string::map [list\
@ -740,11 +760,28 @@ namespace eval shellfilter::chan {
#puts stdout "=-=[ansistring VIEWCODES $o_buffered]"
} else {
#puts [a+ yellow]???[ansistring VIEW "'$o_buffered'<+>'$trailing_pt'"]???[a]
if {![llength $o_codestack] || ([llength $o_codestack] ==1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]])} {
switch -- [llength $o_codestack] {
0 {
append emit $o_do_colour$trailing_pt$o_do_normal
}
1 {
if {[punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]} {
append emit $o_do_colour$trailing_pt$o_do_normal
set o_codestack [list]
} else {
append emit $trailing_pt
#append emit [lindex $o_codestack 0]$trailing_pt
append emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$trailing_pt
}
}
default {
append emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$trailing_pt
}
}
#if {![llength $o_codestack] || ([llength $o_codestack] ==1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]])} {
# append emit $o_do_colour$trailing_pt$o_do_normal
#} else {
# append emit $trailing_pt
#}
#the previous o_buffered formed the data we emitted - nothing new to buffer because we emitted all parts including the trailing plaintext
set o_buffered ""
}
@ -759,11 +796,14 @@ namespace eval shellfilter::chan {
#puts "-->esc but no detect"
#no complete ansi codes - but at least one esc is present
if {[string last \x1b $buf] == [string length $buf]-1} {
#only esc is last char in buf
if {[string index $buf end] eq "\x1b" && [string first \x1b $buf] == [string length $buf]-1} {
#string index in first part of && clause to avoid some unneeded scans of whole string for this test
#we can't use 'string last' - as we need to know only esc is last char in buf
#puts ">>trailing-esc<<"
set o_buffered \x1b
set emit [string range $buf 0 end-1]
set emit $o_do_colour[string range $buf 0 end-1]$o_do_normal
#set emit [string range $buf 0 end-1]
set buf ""
} else {
set emit_anyway 0
#todo - ensure non-ansi escapes in middle of chunks don't lead to ever growing buffer
@ -774,8 +814,10 @@ namespace eval shellfilter::chan {
if {$st_partial_len < 1001} {
append o_buffered $chunk
set emit ""
set buf ""
} else {
set emit_anyway 1
set o_buffered ""
}
} else {
set possible_code_len [expr {[string length $buf] - [string last \x1b $buf]}] ;#length of possible code
@ -783,6 +825,7 @@ namespace eval shellfilter::chan {
set open_sequence_detected [punk::ansi::ta::detect_open $buf]
if {$possible_code_len > 10 && !$open_sequence_detected} {
set emit_anyway 1
set o_buffered ""
} else {
#could be composite sequence with params - allow some reasonable max sequence length
#todo - configurable max sequence length
@ -790,26 +833,49 @@ namespace eval shellfilter::chan {
# - allow some headroom for redundant codes when the caller didn't merge.
if {$possible_code_len < 101} {
append o_buffered $chunk
set buf ""
set emit ""
} else {
#allow a little more grace if we at least have an opening ansi sequence of any type..
if {$open_sequence_detected && $possible_code_len < 151} {
append o_buffered $chunk
set buf ""
set emit ""
} else {
set emit_anyway 1
set o_buffered ""
}
}
}
}
if {$emit_anyway} {
#assert: any time emit_anyway == 1 buf already contains all of previous o_buffered and o_buffered has been cleared.
#looked ansi-like - but we've given enough length without detecting close..
#treat as possible plain text with some esc or unrecognised ansi sequence
if {( ![llength $o_codestack] || ([llength $o_codestack] == 1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]))} {
switch -- [llength $o_codestack] {
0 {
set emit $o_do_colour$buf$o_do_normal
}
1 {
if {[punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]} {
set emit $o_do_colour$buf$o_do_normal
set o_codestack [list]
} else {
set emit $buf
#set emit [lindex $o_codestack 0]$buf
set emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$buf
}
}
default {
#set emit [punk::ansi::codetype::sgr_merge_singles $o_codestack]$buf
set emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$buf
}
}
#if {( ![llength $o_codestack] || ([llength $o_codestack] == 1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]))} {
# set emit $o_do_colour$buf$o_do_normal
#} else {
# set emit $buf
#}
}
}
}
@ -817,12 +883,24 @@ namespace eval shellfilter::chan {
#no esc
#puts stdout [a+ yellow]...[a]
#test!
if {( ![llength $o_codestack] || ([llength $o_codestack] == 1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]))} {
switch -- [llength $o_codestack] {
0 {
set emit $o_do_colour$buf$o_do_normal
}
1 {
if {[punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]} {
set emit $o_do_colour$buf$o_do_normal
set o_codestack [list]
} else {
set emit $buf
#set emit [lindex $o_codestack 0]$buf
set emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$buf
}
}
default {
#set emit [punk::ansi::codetype::sgr_merge_singles $o_codestack]$buf
set emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$buf
}
}
#set emit $buf
set o_buffered ""
}
return [dict create emit $emit stacksize [llength $o_codestack]]
@ -856,13 +934,22 @@ namespace eval shellfilter::chan {
set instring [tcl::encoding::convertfrom $o_enc $bytes]
set streaminfo [my Trackcodes $instring]
set emit [dict get $streaminfo emit]
if {[dict get $streaminfo stacksize] == 0} {
#no ansi on the stack - we can wrap
#review
set outstring "$o_do_colour$emit$o_do_normal"
} else {
#review - wrapping already done in Trackcodes
#if {[dict get $streaminfo stacksize] == 0} {
# #no ansi on the stack - we can wrap
# #review
# set outstring "$o_do_colour$emit$o_do_normal"
#} else {
#}
#if {[llength $o_codestack]} {
# set outstring [punk::ansi::codetype::sgr_merge_singles $o_codestack]$emit
#} else {
# set outstring $emit
#}
set outstring $emit
}
#puts stdout "decoded >>>[ansistring VIEWCODES $outstring]<<<"
#puts stdout "re-encoded>>>[ansistring VIEW [tcl::encoding::convertto $o_enc $outstring]]<<<"
return [tcl::encoding::convertto $o_enc $outstring]
@ -2260,7 +2347,7 @@ namespace eval shellfilter {
#
if {!$is_script} {
set experiment 0
if $experiment {
if {$experiment} {
try {
set results [exec {*}$commandlist]
set exitinfo [list exitcode 0]

BIN
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/test/tomlish-1.1.5.tm

Binary file not shown.

2
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/textblock-0.1.3.tm

@ -4301,7 +4301,7 @@ tcl::namespace::eval textblock {
if {[dict get $opts -frame]} {
#set output [textblock::frame -ansiborder [a+ {*}$fc Black web-cornflowerblue] -type heavy -title "[a+ {*}$fc Black] Periodic Table " [$t print]]
#set output [textblock::frame -ansiborder [a+ {*}$fc Black term-deepskyblue2] -type heavy -title "[a+ {*}$fc Black] Periodic Table " [$t print]]
set output [textblock::frame -checkargs 0 -ansiborder [a+ {*}$fc Black cyan] -type heavy -title "[a+ {*}$fc Black] Periodic Table " [$t print]]
set output [textblock::frame -checkargs 0 -ansiborder [a+ {*}$fc Black cyan] -type heavy -title "[a+ {*}$fc Black] Periodic Table [a]" [$t print]]
} else {
set output [$t print]
}

1246
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/tomlish-1.1.6.tm

File diff suppressed because it is too large Load Diff

67
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/aliascore-0.1.0.tm

@ -103,7 +103,9 @@ tcl::namespace::eval punk::aliascore {
#use absolute ns ie must be prefixed with ::
#single element commands are imported if source command already exists, otherwise aliased. multi element commands are aliased
#functions must be in export list of their source namespace
#functions should generally be covered by one of the export patterns of their source namespace
# - if they are not - e.g (separately loaded ensemble command ?)
# the aliascore::init will temporarily extend the exports list to do the import, and then reset the exports to how they were.
set aliases [tcl::dict::create\
val ::punk::pipe::val\
aliases ::punk::lib::aliases\
@ -122,8 +124,8 @@ tcl::namespace::eval punk::aliascore {
stripansi ::punk::ansi::ansistrip\
ansiwrap ::punk::ansi::ansiwrap\
colour ::punk::console::colour\
ansi ::punk::console::ansi\
color ::punk::console::colour\
ansi ::punk::console::ansi\
a? ::punk::console::code_a?\
A? {::punk::console::code_a? forcecolor}\
a+ ::punk::console::code_a+\
@ -132,6 +134,7 @@ tcl::namespace::eval punk::aliascore {
A {::punk::console::code_a forcecolour}\
smcup ::punk::console::enable_alt_screen\
rmcup ::punk::console::disable_alt_screen\
config ::punk::config\
]
#*** !doctools
@ -153,6 +156,35 @@ tcl::namespace::eval punk::aliascore {
# return "ok"
#}
proc _is_exported {ns cmd} {
set exports [::tcl::namespace::eval $ns [list namespace export]]
set is_exported 0
foreach p $exports {
if {[string match $p $cmd]} {
set is_exported 1
break
}
}
return $is_exported
}
#_nsprefix accepts entire command - not just an existing namespace for which we want the parent
proc _nsprefix {{nspath {}}} {
#maintenance: from punk::ns::nsprefix - (without unnecessary nstail)
#normalize the common case of ::::
set nspath [string map {:::: ::} $nspath]
set rawprefix [string range $nspath 0 end-[string length [namespace tail $nspath]]]
if {$rawprefix eq "::"} {
return $rawprefix
} else {
if {[string match *:: $rawprefix]} {
return [string range $rawprefix 0 end-2]
} else {
return $rawprefix
}
}
}
#todo - options as to whether we should raise an error if collisions found, undo aliases etc?
proc init {args} {
set defaults {-force 0}
@ -195,6 +227,7 @@ tcl::namespace::eval punk::aliascore {
error "punk::aliascore::init declined to create any aliases or imports because -force == 0 and conflicts found:$conflicts"
}
set failed [list]
set tempns ::temp_[info cmdcount] ;#temp ns for renames
dict for {a cmd} $aliases {
#puts "aliascore $a -> $cmd"
@ -206,16 +239,36 @@ tcl::namespace::eval punk::aliascore {
} else {
if {[tcl::info::commands $cmd] ne ""} {
#todo - ensure exported? noclobber?
if {[tcl::namespace::tail $a] eq [tcl::namespace::tail $cmd]} {
set container_ns [_nsprefix $cmd]
set cmdtail [tcl::namespace::tail $cmd]
set was_exported 1 ;#assumption
if {![_is_exported $container_ns $cmdtail]} {
set was_exported 0
set existing_exports [tcl::namespace::eval $container_ns [list ::namespace export]]
tcl::namespace::eval $container_ns [list ::namespace export $cmdtail]
}
if {[tcl::namespace::tail $a] eq $cmdtail} {
#puts stderr "importing $cmd"
tcl::namespace::eval :: [list namespace import $cmd]
try {
tcl::namespace::eval :: [list ::namespace import $cmd]
} trap {} {emsg eopts} {
lappend failed [list alias $a target $cmd errormsg $emsg]
}
} else {
#target command name differs from exported name
#e.g stripansi -> punk::ansi::ansistrip
#import and rename
#puts stderr "importing $cmd (with rename to ::$a)"
tcl::namespace::eval $tempns [list namespace import $cmd]
catch {rename ${tempns}::[namespace tail $cmd] ::$a}
try {
tcl::namespace::eval $tempns [list ::namespace import $cmd]
} trap {} {emsg eopst} {
lappend failed [list alias $a target $cmd errormsg $emsg]
}
catch {rename ${tempns}::$cmdtail ::$a}
}
#restore original exports
if {!$was_exported} {
tcl::namespace::eval $container_ns [list ::namespace export -clear {*}$existing_exports]
}
} else {
interp alias {} $a {} {*}$cmd
@ -223,7 +276,7 @@ tcl::namespace::eval punk::aliascore {
}
}
#tcl::namespace::delete $tempns
return [dict create aliases [dict keys $aliases] unchanged $ignore_aliases changed $conflicts]
return [dict create aliases [dict keys $aliases] existing $existing ignored $ignore_aliases changed $conflicts failed $failed]
}

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

@ -3357,9 +3357,20 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
lappend PUNKARGS [list {
@id -id ::punk::ansi::ansiwrap
@cmd -name punk::ansi::ansiwrap -help\
"Wrap a string with ANSI codes from
{Wrap a string with ANSI codes from
supplied codelist(s) followed by trailing
ANSI reset.
ANSI reset. The wrapping is done such that
after every reset in the supplied text, the
default goes back to the supplied codelist.
e.g1 in the following
ansiwrap red bold "rrr[a+ green]ggg[a]rrr"
both strings rrr will be red & bold
e.g2 bolding and underlining specific text whilst dimming the rest
ansiwrap dim [string map [list test [ansiwrap bold underline test]] "A test string"]
e.g3 reverse render a complex ansi substring
ansiwrap reverse [textblock::periodic]
Codes are numbers or strings as indicated
in the output of the colour information
@ -3372,41 +3383,172 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
For finer control use the a+ and a
functions eg
set x \"[a+ red]text [a+ bold]etc[a]\"
"
set x "[a+ red]text [a+ bold]etc[a]"
}
@leaders -min 0 -max -1
codelist -multiple 1 -default {} -type list -help\
"ANSI names/ints as understood by 'a?'
(Not actual ANSI as output by a+)
These can be supplied individually or
as a list or lists"
@opts
-rawansi -type ansi -default ""
-resetcodes -type list -default {reset}
-rawresets -type ansi -default ""
-fullcodemerge -type boolean -default 0 -help\
"experimental"
-overridecodes -type list -default {}
@values -min 1 -max 1
text -type string -help\
"String to wrap with ANSI (SGR)"
}]
#proc ansiwrap {codes text} {
# return [a {*}$codes]$text[a]
#}
proc ansiwrap2 {args} {
set argd [punk::args::parse $args withid ::punk::ansi::ansiwrap]
set codelists [dict get $argd leaders codelist]
set text [dict get $argd values text]
set codes [concat {*}$codelists] ;#flatten
return [a {*}$codes]$text[a]
}
proc ansiwrap {args} {
if {[llength $args] < 1} {
#minimal args parsing - unhappy path only
#throw to args::parse to get friendly error/usage display
punk::args::parse $args withid ::punk::ansi::ansiwrap
return
}
#we know there are no valid codes that start with -
if {[lsearch [lrange $args 0 end-1] -*] == -1} {
#no opts
set text [lindex $args end]
set codelists [lrange $args 0 end-1]
set R [a] ;#plain ansi reset
set rawansi ""
set rawresets ""
set fullmerge 0
set overrides ""
} else {
set argd [punk::args::parse $args withid ::punk::ansi::ansiwrap]
lassign [dict values $argd] leaders opts values received solos
set codelists [dict get $leaders codelist]
set text [dict get $values text]
set rawansi [dict get $opts -rawansi]
set R [a+ {*}[dict get $opts -resetcodes]]
set rawresets [dict get $opts -rawresets]
set fullmerge [dict get $opts -fullcodemerge]
set overrides [punk::ansi::ta::get_codes_single [a+ {*}[dict get $opts -overridecodes]]]
}
#note that the result of any sgr_merge or sgr_merge_singles is not necessarily a single Ansi escape sequence.
#there can be SGR unmergeables (due to enhanced underlines) as well as non SGR codes
set codes [concat {*}$codelists] ;#flatten
return [a {*}$codes]$text[a]
set base [a+ {*}$codes]
if {$rawansi ne ""} {
set rawcodes [punk::ansi::ta::get_codes_single $rawansi] ;#caller may have supplied as [a+ xxx][a+ yyy]
if {$fullmerge} {
set base [punk::ansi::codetype::sgr_merge [list $base {*}$rawcodes]]
} else {
set base [punk::ansi::codetype::sgr_merge_singles [list $base {*}$rawcodes]]
}
}
if {$rawresets ne ""} {
set rawresetcodes [punk::ansi::ta::get_codes_single $rawresets]
if {$fullmerge} {
set R [punk::ansi::codetype::sgr_merge [list $R {*}$rawresetcodes]]
} else {
set R [punk::ansi::codetype::sgr_merge_singles [list $R {*}$rawresetcodes]]
}
}
set codestack [list]
if {[punk::ansi::ta::detect $text]} {
set emit ""
set parts [punk::ansi::ta::split_codes $text]
foreach {pt code} $parts {
switch -- [llength $codestack] {
0 {
append emit $base$pt$R
}
1 {
if {[punk::ansi::codetype::is_sgr_reset [lindex $codestack 0]]} {
append emit $base$pt$R
set codestack [list]
} else {
#append emit [lindex $o_codestack 0]$pt
if {$fullmerge} {
append emit [punk::ansi::codetype::sgr_merge [list $base {*}$codestack {*}$overrides]]$pt$R
} else {
append emit [punk::ansi::codetype::sgr_merge_singles [list $base {*}$codestack {*}$overrides]]$pt$R
}
}
}
default {
if {$fullmerge} {
append emit [punk::ansi::codetype::sgr_merge [list $base {*}$codestack {*}$overrides]]$pt$R
} else {
append emit [punk::ansi::codetype::sgr_merge_singles [list $base {*}$codestack {*}$overrides]]$pt$R
}
}
}
#parts ends on a pt - last code always empty string
if {$code ne ""} {
set c1c2 [tcl::string::range $code 0 1]
set leadernorm [tcl::string::range [tcl::string::map [list\
\x1b\[ 7CSI\
\x9b 8CSI\
\x1b\( 7GFX\
] $c1c2] 0 3]
switch -- $leadernorm {
7CSI - 8CSI {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set codestack [list "\x1b\[m"]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set codestack [list $code]
} elseif {[punk::ansi::codetype::is_sgr $code]} {
#todo - make caching is_sgr method
set dup_posns [lsearch -all -exact $codestack $code]
set codestack [lremove $codestack {*}$dup_posns]
lappend codestack $code
} else {
}
}
7GFX {
switch -- [tcl::string::index $code 2] {
"0" {
set o_gx_state on
}
"B" {
set o_gx_state off
}
}
}
default {
#other ansi codes
}
}
append emit $code
}
}
return $emit$R
} else {
return $base$text$R
}
}
proc ansiwrap_naive {codes text} {
return [a_ {*}$codes]$text[a]
}
#a silly trick... temporary? probably - todo - tests and work on sgr_merge + sgr_merge_singles before relying on this
#when we use sgr_merge_singles on a 'single' containing a non SGR code e.g <esc>[5h (inverse) it puts this code at the end of the list
#furthermore - it carries any SGR codes along with it (Can/should we rely on this behaviour??? probably not) REVIEW
#P% ansistring VIEW $s1
#- ␛[31m␛[?5h
#P% ansistring VIEW [punk::ansi::codetype::sgr_merge_singles [list $s1 [a+ cyan]]]
#- ␛[36m␛[31m␛[?5h
#P% ansistring VIEW [punk::ansi::codetype::sgr_merge [list $s1 [a+ cyan]]]
#- ␛[36m␛[?5h
#we can use this trick to override background and/or foreground colours using ansiwrap - which uses sgr_merge_singles
#Note - this trick is not composable - e.g ansioverride Red [ansiioverride Green [textblock::periodic]] doesn't work as expected.
proc ansioverride2 {args} {
set text [lindex $args end]
set codes [lrange $args 0 end-1]
ansiwrap {*}$codes -rawansi [punk::ansi::enable_inverse] -rawresets [punk::ansi::disable_inverse] $text
}
proc ansireverse {text} {
ansioverride2 normal reverse $text
}
proc get_code_name {code} {
#*** !doctools
@ -4491,6 +4633,77 @@ tcl::namespace::eval punk::ansi {
return 0
}
}
#e.g has_any_effective $str bg fg
proc has_any_effective {str args} {
set singlecodes [punk::ansi::ta::get_codes_single $str]
set mergeinfo [punk::ansi::codetype::sgr_merge_singles $singlecodes -info 1]
foreach t $args {
switch -- $t {
sgr - unmergeable - othercodes {
if {[dict get $mergeinfo $t] ne ""} {
return 1
}
}
intensity - italic - underline - underextended - blink - reverse - hide - strike - font - gothic - doubleunderline
- proportional - frame_or_circle - ideogram_underline - ideogram_doubleunderline - ideogram_clear - overline - underlinecolour - superscript - subscript
- nosupersub - fg - bg {
if {[dict get $mergeinfo codestate $t] ne ""} {
return 1
}
}
bold {
if {[dict get $mergeinfo codestate intensity] eq "1"} {
return 1
}
}
dim {
if {[dict get $mergeinfo codestate intensity] eq "2"} {
return 1
}
}
default {
error "punk::ansi::ta::has_any_effective invalid type '$t' specified"
}
}
}
return 0
}
proc has_all_effective {str args} {
set singlecodes [punk::ansi::ta::get_codes_single $str]
set mergeinfo [punk::ansi::codetype::sgr_merge_singles $singlecodes -info 1]
foreach t $args {
switch -- $t {
sgr - unmergeable - othercodes {
if {[dict get $mergeinfo $t] eq ""} {
return 0
}
}
intensity - italic - underline - underextended - blink - reverse - hide - strike - font - gothic - doubleunderline
- proportional - frame_or_circle - ideogram_underline - ideogram_doubleunderline - ideogram_clear - overline - underlinecolour - superscript - subscript
- nosupersub - fg - bg {
if {[dict get $mergeinfo codestate $t] eq ""} {
return 0
}
}
bold {
if {[dict get $mergeinfo codestate intensity] ne "1"} {
return 0
}
}
dim {
if {[dict get $mergeinfo codestate intensity] ne "2"} {
return 0
}
}
default {
error "punk::ansi::ta::has_any_effective invalid type '$t' specified"
}
}
}
return 1
}
proc is_gx {code} {
#g0 {(?:\x1b\(0)(?:(?!\x1b\(B).)*\x1b\(B}
#g1 {(?:\x1b\)0)(?:(?!\x1b\)B).)*\x1b\)B}
@ -4513,6 +4726,7 @@ tcl::namespace::eval punk::ansi {
set codestate_empty [tcl::dict::create]
tcl::dict::set codestate_empty rst "" ;#0 (or empty)
tcl::dict::set codestate_empty intensity "" ;#1 bold, 2 dim, 22 normal
tcl::dict::set codestate_empty shadowed "" ;
tcl::dict::set codestate_empty italic "" ;#3 on 23 off
tcl::dict::set codestate_empty underline "" ;#4 on 24 off

31
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/args-0.1.4.tm

@ -3226,7 +3226,36 @@ tcl::namespace::eval punk::args {
form1: parse $arglist ?-flag val?... withid $id
form2: parse $arglist ?-flag val?... withdef $def ?$def?
see punk::args::define"
see punk::args::define
Returns a dict of information regarding the parsed arguments
example of basic usage for single option only:
punk::args::define {
@id -id ::myns::myfunc
@cmd -name myns::myfunc
@leaders -min 0 -max 0
@opts
-configfile -type existingfile
#type none makes it a solo flag
-verbose -type none
@values -min 0 -max 0
}
proc myfunc {args} {
set argd [punk::args::parse $args withid ::myns::myfunc]
lassign [dict values $argd] leaders opts values received solos
if {[dict exists $received] -configfile} {
puts \"have option for existing file [dict get $opts -configfile]\"
}
}
The leaders, opts, values keys in the parse result dict are proper dicts.
The received key is dict-like but can have repeated keys for arguments than can
accept multiples. The value for each received element is the ordinal position.
The solos key refers to a list of solo flags received (those specified with
-type none). This is generally only useful to assist in passing arguments on
to another procedure which also requires solos, because the opts dict contains
solo flags with a 1 value or a list of 1's if it was a solo with -multiple true
specified.
"
@form -form {withid withdef}
@leaders -min 1 -max 1
arglist -type list -optional 0 -help\

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

@ -1,23 +1,109 @@
tcl::namespace::eval punk::config {
variable loaded
variable startup ;#include env overrides
variable running
variable configdata [dict create] ;#key on config names. At least default, startup, running
#variable startup ;#include env overrides
#variable running
variable punk_env_vars
variable other_env_vars
variable vars
namespace export {[a-z]*}
namespace ensemble create
namespace eval punk {namespace export config}
proc _homedir {} {
if {[info exists ::env(HOME)]} {
set home [file normalize $::env(HOME)]
} else {
#not available on 8.6? ok will error out here.
set home [file tildeexpand ~]
}
return $home
}
lappend PUNKARGS [list {
@id -id ::punk::config::dir
@cmd -name punk::config::dir -help\
"Get the path for the default config folder
Config files are in toml format.
The XDG_CONFIG_HOME env var is the preferred
choice of location.
A folder under the user's home directory,
at .config/punk/shell is chosen if
XDG_CONFIG_HOME is not configured.
"
@leaders -min 0 -max 0
@opts
-quiet -type none -help\
"Suppress warning given when the folder does
not yet exist"
@values -min 0 -max 0
}]
proc dir {args} {
if {"-quiet" in $args} {
set be_quiet [dict exists $received -quiet]
}
set was_noisy 0
set config_home [punk::config::configure running xdg_config_home]
set config_dir [file join $config_home punk shell]
if {!$be_quiet && ![file exists $config_dir]} {
set msg "punk::shell data storage folder at $config_dir does not yet exist."
puts stderr $msg
set was_noisy 1
}
if {!$be_quiet && $was_noisy} {
puts stderr "punk::config::dir - call with -quiet option to suppress these messages"
}
return $config_dir
#if {[info exists ::env(XDG_CONFIG_HOME)]} {
# set config_home $::env(XDG_CONFIG_HOME)
#} else {
# set config_home [file join [_homedir] .config]
# if {!$be_quiet} {
# puts stderr "Environment variable XDG_CONFIG_HOME does not exist - consider setting it if $config_home is not a suitable location"
# set was_noisy 1
# }
#}
#if {!$be_quiet && ![file exists $config_home]} {
# #parent folder for 'punk' config dir doesn't exist
# set msg "configuration location (XDG_CONFIG_HOME or ~/.config) $config_home does not yet exist"
# append msg \n " - please create it and/or set XDG_CONFIG_HOME env var."
# puts stderr $msg
# set was_noisy 1
#}
#set config_dir [file join $config_home punk shell]
#if {!$be_quiet && ![file exists $config_dir]} {
# set msg "punk::shell data storage folder at $config_dir does not yet exist."
# append msg \n " It will be created if api_context_save is called without specifying an alternate location."
# puts stderr $msg
# set was_noisy 1
#}
#if {!$be_quiet && $was_noisy} {
# puts stderr "punk::config::dir - call with -quiet option to suppress these messages"
#}
#return [file join $configdir config.toml]
}
#todo - XDG_DATA_HOME etc
#https://specifications.freedesktop.org/basedir-spec/latest/
# see also: http://hiphish.github.io/blog/2020/08/30/dotfiles-were-a-mistake/
proc init {} {
variable defaults
variable startup
variable running
variable configdata
#variable defaults
#variable startup
#variable running
variable punk_env_vars
variable punk_env_vars_config
variable other_env_vars
@ -108,12 +194,14 @@ tcl::namespace::eval punk::config {
#we have a choice of LOCALAPPDATA vs APPDATA (local to machine vs potentially roaming/redirected in a corporate environment)
#using the roaming location should not impact users who aren't using a domain controller but is potentially much more convenient for those who do.
if {[info exists ::env(APPDATA)]} {
#Typical existing/default value for env(APPDATA) on windows is c:\Users\<username>\AppData\Roaming
set default_xdg_config_home $::env(APPDATA)
set default_xdg_data_home $::env(APPDATA)
}
#The xdg_cache_home should be kept local
if {[info exists ::env(LOCALAPPDATA)]} {
#Typical existing/default value for env(APPDATA) on windows is c:\Users\<username>\AppData\Local
set default_xdg_data_home $::env(LOCALAPPDATA)
set default_xdg_cache_home $::env(LOCALAPPDATA)
set default_xdg_state_home $::env(LOCALAPPDATA)
}
@ -133,10 +221,10 @@ tcl::namespace::eval punk::config {
}
}
set defaults [dict create\
dict set configdata defaults [dict create\
apps $default_apps\
config ""\
configset ".punkshell"\
config "startup"\
configset "main"\
scriptlib $default_scriptlib\
color_stdout $default_color_stdout\
color_stdout_repl $default_color_stdout_repl\
@ -160,7 +248,7 @@ tcl::namespace::eval punk::config {
posh_themes_path ""\
]
set startup $defaults
dict set configdata startup [dict get $configdata defaults]
#load values from saved config file - $xdg_config_home/punk/punk.config ?
#typically we want env vars to override the stored config - as env vars conventionally used on some commandlines.
#that's possibly ok for the PUNK_ vars
@ -219,9 +307,9 @@ tcl::namespace::eval punk::config {
lappend final $p
}
}
tcl::dict::set startup $varname $final
tcl::dict::set configdata startup $varname $final
} else {
tcl::dict::set startup $varname $f
tcl::dict::set configdata startup $varname $f
}
}
}
@ -273,29 +361,44 @@ tcl::namespace::eval punk::config {
lappend final $p
}
}
tcl::dict::set startup $varname $final
tcl::dict::set configdata startup $varname $final
} else {
tcl::dict::set startup $varname $f
tcl::dict::set configdata startup $varname $f
}
}
}
}
set config_home [dict get $configdata startup xdg_config_home]
if {![file exists $config_home]} {
puts stderr "punk::config::init creating punk shell config dir: [dir]"
puts stderr "(todo)"
}
set configset [dict get $configdata defaults configset]
set config [dict get $configdata defaults config]
set startupfile [file join $config_home $configset $config.toml]
if {![file exists $startupfile]} {
puts stderr "punk::config::init creating punk shell config file: $config for configset: $configset"
puts stderr "(todo)"
}
#unset -nocomplain vars
#todo
set running [tcl::dict::create]
set running [tcl::dict::merge $running $startup]
dict set configdata running [tcl::dict::merge $running [dict get $configdata startup]]
}
init
#todo
proc Apply {config} {
variable configdata
puts stderr "punk::config::Apply partially implemented"
set configname [string map {-config ""} $config]
if {$configname in {startup running}} {
upvar ::punk::config::$configname applyconfig
set applyconfig [dict get $configdata $configname]
if {[dict exists $applyconfig auto_noexec]} {
set auto [dict get $applyconfig auto_noexec]
@ -315,67 +418,128 @@ tcl::namespace::eval punk::config {
}
return "apply done"
}
Apply startup
#todo - consider how to divide up settings, categories, 'devices', decks etc
proc get_running_global {varname} {
variable running
variable configdata
set running [dict get $configdata running]
if {[dict exists $running $varname]} {
return [dict get $running $varname]
}
error "No such global configuration item '$varname' found in running config"
}
proc get_startup_global {varname} {
variable startup
variable configdata
set startup [dict get $configdata startup]
if {[dict exists $startup $varname]} {
return [dict get $startup $varname]
}
error "No such global configuration item '$varname' found in startup config"
}
proc get {whichconfig {globfor *}} {
variable startup
variable running
lappend PUNKARGS [list {
@id -id ::punk::config::get
@cmd -name punk::config::get -help\
"Get configuration values from a config.
Accepts globs eg XDG*"
@leaders -min 1 -max 1
whichconfig -type string -choices {config startup-configuration running-configuration}
@values -min 0 -max -1
globkey -type string -default * -optional 1 -multiple 1
}]
proc get {args} {
set argd [punk::args::parse $args withid ::punk::config::get]
lassign [dict values $argd] leaders opts values received solos
set whichconfig [dict get $leaders whichconfig]
set globs [dict get $values globkey] ;#list
variable configdata
switch -- $whichconfig {
config - startup - startup-config - startup-configuration {
config - startup-configuration {
#review 'config' ??
#show *startup* config - different behaviour may be confusing to those used to router startup and running configs
set configdata $startup
set configrecords [dict get $configdata startup]
}
running - running-config - running-configuration {
set configdata $running
running-configuration {
set configrecords [dict get $configdata running]
}
default {
error "Unknown config name '$whichconfig' - try startup or running"
}
}
if {$globfor eq "*"} {
return $configdata
if {"*" in $globs} {
return $configrecords
} else {
set keys [dict keys $configdata [string tolower $globfor]]
set keys [list]
foreach g $globs {
lappend keys {*}[dict keys $configrecords [string tolower $g]] ;#review tolower?
}
set filtered [dict create]
foreach k $keys {
dict set filtered $k [dict get $configdata $k]
dict set filtered $k [dict get $configrecords $k]
}
return $filtered
}
}
proc configure {args} {
set argdef {
lappend PUNKARGS [list {
@id -id ::punk::config::configure
@cmd -name punk::config::configure -help\
"UNIMPLEMENTED"
@values -min 1 -max 1
whichconfig -type string -choices {startup running stop}
"Get/set configuration values from a config"
@leaders -min 1 -max 1
whichconfig -type string -choices {defaults startup-configuration running-configuration}
@values -min 0 -max 2
key -type string -optional 1
newvalue -optional 1
}]
proc configure {args} {
set argd [punk::args::parse $args withid ::punk::config::configure]
lassign [dict values $argd] leaders opts values received solos
set whichconfig [dict get $argd leaders whichconfig]
variable configdata
if {"running" ni [dict keys $configdata]} {
init
Apply startup
}
set argd [punk::args::get_dict $argdef $args]
return "unimplemented - $argd"
switch -- $whichconfig {
defaults {
set configrecords [dict get $configdata defaults]
}
startup-configuration {
set configrecords [dict get $configdata startup]
}
running-configuration {
set configrecords [dict get $configdata running]
}
}
if {![dict exists $received key]} {
return $configrecords
}
set key [dict get $values key]
if {![dict exists $received newvalue]} {
return [dict get $configrecords $key]
}
error "setting value not implemented"
}
proc show {whichconfig {globfor *}} {
lappend PUNKARGS [list {
@dynamic
@id -id ::punk::config::show
@cmd -name punk::config::get -help\
"Display configuration values from a config.
Accepts globs eg XDG*"
@leaders -min 1 -max 1
}\
{${[punk::args::resolved_def -types leaders ::punk::config::get]}}\
"@values -min 0 -max -1"\
{${[punk::args::resolved_def -types values ::punk::config::get]}}\
]
proc show {args} {
#todo - tables for console
set configdata [punk::config::get $whichconfig $globfor]
return [punk::lib::showdict $configdata]
set configrecords [punk::config::get {*}$args]
return [punk::lib::showdict $configrecords]
}
@ -459,27 +623,35 @@ tcl::namespace::eval punk::config {
::tcl::namespace::eval punk::config {
#todo - something better - 'previous' rather than reverting to startup
proc channelcolors {{onoff {}}} {
variable running
variable startup
variable configdata
#variable running
#variable startup
if {![string length $onoff]} {
return [list stdout [dict get $running color_stdout] stderr [dict get $running color_stderr]]
return [list stdout [dict get $configdata running color_stdout] stderr [dict get $configdata running color_stderr]]
} else {
if {![string is boolean $onoff]} {
error "channelcolors: invalid value $onoff - expected boolean: true|false|on|off|1|0|yes|no"
}
if {$onoff} {
dict set running color_stdout [dict get $startup color_stdout]
dict set running color_stderr [dict get $startup color_stderr]
dict set configdata running color_stdout [dict get $startup color_stdout]
dict set configdata running color_stderr [dict get $startup color_stderr]
} else {
dict set running color_stdout ""
dict set running color_stderr ""
dict set configdata running color_stdout ""
dict set configdata running color_stderr ""
}
}
return [list stdout [dict get $running color_stdout] stderr [dict get $running color_stderr]]
return [list stdout [dict get $configdata running color_stdout] stderr [dict get $configdata running color_stderr]]
}
}
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::config
}
package provide punk::config [tcl::namespace::eval punk::config {
variable version
set version 0.1

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

@ -33,8 +33,7 @@ namespace eval punk::mod::cli {
return $basehelp
}
proc getraw {appname} {
upvar ::punk::config::running running_config
set app_folders [dict get $running_config apps]
set app_folders [punk::config::configure running apps]
#todo search each app folder
set bases [::list]
set versions [::list]
@ -86,8 +85,7 @@ namespace eval punk::mod::cli {
}
proc list {{glob *}} {
upvar ::punk::config::running running_config
set apps_folder [dict get $running_config apps]
set apps_folder [punk::config::configure running apps]
if {[file exists $apps_folder]} {
if {[file exists $apps_folder/$glob]} {
#tailcall source $apps_folder/$glob/main.tcl

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

@ -376,6 +376,8 @@ tcl::namespace::eval punk::ns {
#and a namespace can exist with leading colon - but is even worse - as default Tcl commands will misreport e.g namespace current within namespace eval
#The view is taken that a namespace with leading/trailing colons is so error-prone that even introspection is unreliable so we will rule that out.
#
#nsprefix is *somewhat* like 'namespace parent' execept that it is string based - ie no requirement for the namespaces to actually exist
# - this is an important usecase even if the handling of 'unwise' command names isn't so critical.
proc nsprefix {{nspath ""}} {
#normalize the common case of ::::
set nspath [string map {:::: ::} $nspath]
@ -394,6 +396,8 @@ tcl::namespace::eval punk::ns {
#namespace tail which handles :::cmd ::x:::y ::x:::/y etc in a specific manner for string processing
#review - consider making -strict raise an error for unexpected sequences such as :::: or any situation with more than 2 colons together.
#This is only necessary in the context of requirement to browse namespaces with 'unwisely' named commands
#For most purposes 'namespace tail' is fine.
proc nstail {nspath args} {
#normalize the common case of ::::
set nspath [string map {:::: ::} $nspath]
@ -2018,7 +2022,7 @@ tcl::namespace::eval punk::ns {
}
proc arginfo {args} {
lassign [dict values [punk::args::parse $args withid ::punk::ns::arginfo]] leaders opts values received
set nscaller [uplevel 1 [list ::namespace current]]
#review - setting this afterwards is an architecture smell - we should be able to override the default in the dynamic part
#todo - enable retrieving by id just the record_opts part - so we can treat as a dict directly, as well as easily apply it as a different flag name.
if {![dict exists $received -scheme]} {
@ -2086,10 +2090,12 @@ tcl::namespace::eval punk::ns {
#set numvals [expr {[llength $queryargs]+1}]
##puts "THROWBACK: namespace eval :: [list punk::ns::arginfo {*}[lrange $args 0 end-$numvals] $querycommand {*}$queryargs]"
#return [namespace eval :: [list punk::ns::arginfo {*}[lrange $args 0 end-$numvals] $querycommand {*}$queryargs]]
if {$nscaller ne "::"} {
return [namespace eval :: [list punk::ns::arginfo {*}$opts $querycommand {*}$queryargs]]
}
#set origin $querycommand
#set resolved $querycommand
set origin $querycommand
set resolved $querycommand
}
}

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

@ -175,13 +175,13 @@ tcl::namespace::eval punk::repl::codethread {
set outstack [list]
set errstack [list]
upvar ::punk::config::running running_config
if {[string length [dict get $running_config color_stdout_repl]] && [interp eval code punk::console::colour]} {
set config_running [::punk::config::configure running]
if {[string length [dict get $config_running color_stdout_repl]] && [interp eval code punk::console::colour]} {
lappend outstack [interp eval code [list ::shellfilter::stack add stdout ansiwrap -settings [list -colour [dict get $running_config color_stdout_repl]]]]
}
lappend outstack [interp eval code [list ::shellfilter::stack add stdout tee_to_var -settings {-varname ::punk::repl::codethread::output_stdout}]]
if {[string length [dict get $running_config color_stderr_repl]] && [interp eval code punk::console::colour]} {
if {[string length [dict get $config_running color_stderr_repl]] && [interp eval code punk::console::colour]} {
lappend errstack [interp eval code [list ::shellfilter::stack add stderr ansiwrap -settings [list -colour [dict get $running_config color_stderr_repl]]]]
# #lappend errstack [shellfilter::stack::add stderr ansiwrap -settings [list -colour cyan]]
}

127
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/shellfilter-0.1.9.tm

@ -674,6 +674,9 @@ namespace eval shellfilter::chan {
#todo - track when in sixel,iterm,kitty graphics data - can be very large
method Trackcodes {chunk} {
#note - caller can use 2 resets in a single unit to temporarily reset to no sgr (override ansiwrap filter)
#e.g [a+ reset reset] (<esc><lb>0;0m vs <esc><lb>0;m)
#puts stdout "===[ansistring VIEW -lf 1 $o_buffered]"
set buf $o_buffered$chunk
set emit ""
@ -686,12 +689,29 @@ namespace eval shellfilter::chan {
#process all pt/code pairs except for trailing pt
foreach {pt code} [lrange $parts 0 end-1] {
#puts "<==[ansistring VIEW -lf 1 $pt]==>"
if {( ![llength $o_codestack] || ([llength $o_codestack] == 1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]))} {
switch -- [llength $o_codestack] {
0 {
append emit $o_do_colour$pt$o_do_normal
}
1 {
if {[punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]} {
append emit $o_do_colour$pt$o_do_normal
#append emit $pt
set o_codestack [list]
} else {
append emit $pt
#append emit [lindex $o_codestack 0]$pt
append emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$pt
}
}
default {
append emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$pt
}
}
#if {( ![llength $o_codestack] || ([llength $o_codestack] == 1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]))} {
# append emit $o_do_colour$pt$o_do_normal
# #append emit $pt
#} else {
# append emit $pt
#}
set c1c2 [tcl::string::range $code 0 1]
set leadernorm [tcl::string::range [tcl::string::map [list\
@ -740,11 +760,28 @@ namespace eval shellfilter::chan {
#puts stdout "=-=[ansistring VIEWCODES $o_buffered]"
} else {
#puts [a+ yellow]???[ansistring VIEW "'$o_buffered'<+>'$trailing_pt'"]???[a]
if {![llength $o_codestack] || ([llength $o_codestack] ==1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]])} {
switch -- [llength $o_codestack] {
0 {
append emit $o_do_colour$trailing_pt$o_do_normal
}
1 {
if {[punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]} {
append emit $o_do_colour$trailing_pt$o_do_normal
set o_codestack [list]
} else {
append emit $trailing_pt
#append emit [lindex $o_codestack 0]$trailing_pt
append emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$trailing_pt
}
}
default {
append emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$trailing_pt
}
}
#if {![llength $o_codestack] || ([llength $o_codestack] ==1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]])} {
# append emit $o_do_colour$trailing_pt$o_do_normal
#} else {
# append emit $trailing_pt
#}
#the previous o_buffered formed the data we emitted - nothing new to buffer because we emitted all parts including the trailing plaintext
set o_buffered ""
}
@ -759,11 +796,14 @@ namespace eval shellfilter::chan {
#puts "-->esc but no detect"
#no complete ansi codes - but at least one esc is present
if {[string last \x1b $buf] == [string length $buf]-1} {
#only esc is last char in buf
if {[string index $buf end] eq "\x1b" && [string first \x1b $buf] == [string length $buf]-1} {
#string index in first part of && clause to avoid some unneeded scans of whole string for this test
#we can't use 'string last' - as we need to know only esc is last char in buf
#puts ">>trailing-esc<<"
set o_buffered \x1b
set emit [string range $buf 0 end-1]
set emit $o_do_colour[string range $buf 0 end-1]$o_do_normal
#set emit [string range $buf 0 end-1]
set buf ""
} else {
set emit_anyway 0
#todo - ensure non-ansi escapes in middle of chunks don't lead to ever growing buffer
@ -774,8 +814,10 @@ namespace eval shellfilter::chan {
if {$st_partial_len < 1001} {
append o_buffered $chunk
set emit ""
set buf ""
} else {
set emit_anyway 1
set o_buffered ""
}
} else {
set possible_code_len [expr {[string length $buf] - [string last \x1b $buf]}] ;#length of possible code
@ -783,6 +825,7 @@ namespace eval shellfilter::chan {
set open_sequence_detected [punk::ansi::ta::detect_open $buf]
if {$possible_code_len > 10 && !$open_sequence_detected} {
set emit_anyway 1
set o_buffered ""
} else {
#could be composite sequence with params - allow some reasonable max sequence length
#todo - configurable max sequence length
@ -790,26 +833,49 @@ namespace eval shellfilter::chan {
# - allow some headroom for redundant codes when the caller didn't merge.
if {$possible_code_len < 101} {
append o_buffered $chunk
set buf ""
set emit ""
} else {
#allow a little more grace if we at least have an opening ansi sequence of any type..
if {$open_sequence_detected && $possible_code_len < 151} {
append o_buffered $chunk
set buf ""
set emit ""
} else {
set emit_anyway 1
set o_buffered ""
}
}
}
}
if {$emit_anyway} {
#assert: any time emit_anyway == 1 buf already contains all of previous o_buffered and o_buffered has been cleared.
#looked ansi-like - but we've given enough length without detecting close..
#treat as possible plain text with some esc or unrecognised ansi sequence
if {( ![llength $o_codestack] || ([llength $o_codestack] == 1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]))} {
switch -- [llength $o_codestack] {
0 {
set emit $o_do_colour$buf$o_do_normal
}
1 {
if {[punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]} {
set emit $o_do_colour$buf$o_do_normal
set o_codestack [list]
} else {
set emit $buf
#set emit [lindex $o_codestack 0]$buf
set emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$buf
}
}
default {
#set emit [punk::ansi::codetype::sgr_merge_singles $o_codestack]$buf
set emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$buf
}
}
#if {( ![llength $o_codestack] || ([llength $o_codestack] == 1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]))} {
# set emit $o_do_colour$buf$o_do_normal
#} else {
# set emit $buf
#}
}
}
}
@ -817,12 +883,24 @@ namespace eval shellfilter::chan {
#no esc
#puts stdout [a+ yellow]...[a]
#test!
if {( ![llength $o_codestack] || ([llength $o_codestack] == 1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]))} {
switch -- [llength $o_codestack] {
0 {
set emit $o_do_colour$buf$o_do_normal
}
1 {
if {[punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]} {
set emit $o_do_colour$buf$o_do_normal
set o_codestack [list]
} else {
set emit $buf
#set emit [lindex $o_codestack 0]$buf
set emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$buf
}
}
default {
#set emit [punk::ansi::codetype::sgr_merge_singles $o_codestack]$buf
set emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$buf
}
}
#set emit $buf
set o_buffered ""
}
return [dict create emit $emit stacksize [llength $o_codestack]]
@ -856,13 +934,22 @@ namespace eval shellfilter::chan {
set instring [tcl::encoding::convertfrom $o_enc $bytes]
set streaminfo [my Trackcodes $instring]
set emit [dict get $streaminfo emit]
if {[dict get $streaminfo stacksize] == 0} {
#no ansi on the stack - we can wrap
#review
set outstring "$o_do_colour$emit$o_do_normal"
} else {
#review - wrapping already done in Trackcodes
#if {[dict get $streaminfo stacksize] == 0} {
# #no ansi on the stack - we can wrap
# #review
# set outstring "$o_do_colour$emit$o_do_normal"
#} else {
#}
#if {[llength $o_codestack]} {
# set outstring [punk::ansi::codetype::sgr_merge_singles $o_codestack]$emit
#} else {
# set outstring $emit
#}
set outstring $emit
}
#puts stdout "decoded >>>[ansistring VIEWCODES $outstring]<<<"
#puts stdout "re-encoded>>>[ansistring VIEW [tcl::encoding::convertto $o_enc $outstring]]<<<"
return [tcl::encoding::convertto $o_enc $outstring]
@ -2260,7 +2347,7 @@ namespace eval shellfilter {
#
if {!$is_script} {
set experiment 0
if $experiment {
if {$experiment} {
try {
set results [exec {*}$commandlist]
set exitinfo [list exitcode 0]

BIN
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/test/tomlish-1.1.5.tm

Binary file not shown.

2
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/textblock-0.1.3.tm

@ -4301,7 +4301,7 @@ tcl::namespace::eval textblock {
if {[dict get $opts -frame]} {
#set output [textblock::frame -ansiborder [a+ {*}$fc Black web-cornflowerblue] -type heavy -title "[a+ {*}$fc Black] Periodic Table " [$t print]]
#set output [textblock::frame -ansiborder [a+ {*}$fc Black term-deepskyblue2] -type heavy -title "[a+ {*}$fc Black] Periodic Table " [$t print]]
set output [textblock::frame -checkargs 0 -ansiborder [a+ {*}$fc Black cyan] -type heavy -title "[a+ {*}$fc Black] Periodic Table " [$t print]]
set output [textblock::frame -checkargs 0 -ansiborder [a+ {*}$fc Black cyan] -type heavy -title "[a+ {*}$fc Black] Periodic Table [a]" [$t print]]
} else {
set output [$t print]
}

1246
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/tomlish-1.1.6.tm

File diff suppressed because it is too large Load Diff

31
src/vfs/_vfscommon.vfs/modules/dictn-0.1.1.tm → src/vfs/_vfscommon.vfs/modules/dictn-0.1.2.tm

@ -7,7 +7,7 @@
# (C) 2023
#
# @@ Meta Begin
# Application dictn 0.1.1
# Application dictn 0.1.2
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
@ -74,15 +74,17 @@ proc ::dictn::get {dictval {path {}}} {
return [dict get $dictval {*}$path]
}
proc ::dictn::getdef {dictval path default} {
if {[info commands ::tcl::dict::getdef] ne ""} {
#tcl 9+
proc ::dictn::getdef {dictval path default} {
return [dict getdef $dictval {*}$path $default]
}
}
proc ::dictn::getwithdefault {dictval path default} {
proc ::dictn::getwithdefault {dictval path default} {
return [dict getdef $dictval {*}$path $default]
}
}
if {[info commands ::tcl::dict::getdef] ne ""} {
proc ::dictn::incr {dictvar path {increment {}} } {
if {$increment eq ""} {
::set increment 1
@ -101,6 +103,21 @@ if {[info commands ::tcl::dict::getdef] ne ""} {
}
}
} else {
#tcl < 9
proc ::dictn::getdef {dictval path default} {
if {[tcl::dict::exists $dictval {*}$path]} {
return [tcl::dict::get $dictval {*}$path]
} else {
return $default
}
}
proc ::dictn::getwithdefault {dictval path default} {
if {[tcl::dict::exists $dictval {*}$path]} {
return [tcl::dict::get $dictval {*}$path]
} else {
return $default
}
}
proc ::dictn::incr {dictvar path {increment {}} } {
if {$increment eq ""} {
::set increment 1
@ -344,6 +361,6 @@ proc ::dictn::with {dictvar path args} {
## Ready
package provide dictn [namespace eval dictn {
variable version
::set version 0.1.1
::set version 0.1.2
}]
return

704
src/vfs/_vfscommon.vfs/modules/modpod-0.1.3.tm

@ -0,0 +1,704 @@
# -*- tcl -*-
# Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from <pkg>-buildversion.txt
#
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem.
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# (C) 2024
#
# @@ Meta Begin
# Application modpod 0.1.3
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[manpage_begin modpod_module_modpod 0 0.1.3]
#[copyright "2024"]
#[titledesc {Module API}] [comment {-- Name section and table of contents description --}]
#[moddesc {-}] [comment {-- Description at end of page heading --}]
#[require modpod]
#[keywords module]
#[description]
#[para] -
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section Overview]
#[para] overview of modpod
#[subsection Concepts]
#[para] -
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[subsection dependencies]
#[para] packages used by modpod
#[list_begin itemized]
package require Tcl 8.6-
package require struct::set ;#review
package require punk::lib
package require punk::args
#*** !doctools
#[item] [package {Tcl 8.6-}]
# #package require frobz
# #*** !doctools
# #[item] [package {frobz}]
#*** !doctools
#[list_end]
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section API]
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# oo::class namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval modpod::class {
#*** !doctools
#[subsection {Namespace modpod::class}]
#[para] class definitions
if {[info commands [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 ---}]
}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Base namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval modpod {
namespace export {[a-z]*}; # Convention: export all lowercase
variable connected
if {![info exists connected(to)]} {
set connected(to) list
}
variable modpodscript
set modpodscript [info script]
if {[string tolower [file extension $modpodscript]] eq ".tcl"} {
set connected(self) [file dirname $modpodscript]
} else {
#expecting a .tm
set connected(self) $modpodscript
}
variable loadables [info sharedlibextension]
variable sourceables {.tcl .tk} ;# .tm ?
#*** !doctools
#[subsection {Namespace modpod}]
#[para] Core API functions for modpod
#[list_begin definitions]
#proc sample1 {p1 args} {
# #*** !doctools
# #[call [fun sample1] [arg p1] [opt {?option value...?}]]
# #[para]Description of sample1
# return "ok"
#}
#old tar connect mechanism - review - not needed?
proc connect {args} {
puts stderr "modpod::connect--->>$args"
set argd [punk::args::get_dict {
@id -id ::modpod::connect
-type -default ""
@values -min 1 -max 1
path -type string -minsize 1 -help "path to .tm file or toplevel .tcl script within #modpod-<pkg>-<ver> folder (unwrapped modpod)"
} $args]
catch {
punk::lib::showdict $argd ;#heavy dependencies
}
set opt_path [dict get $argd values path]
variable connected
set original_connectpath $opt_path
set modpodpath [modpod::system::normalize $opt_path] ;#
if {$modpodpath in $connected(to)} {
return [dict create ok ALREADY_CONNECTED]
}
lappend connected(to) $modpodpath
set connected(connectpath,$opt_path) $original_connectpath
set is_sourced [expr {[file normalize $modpodpath] eq [file normalize [info script]]}]
set connected(location,$modpodpath) [file dirname $modpodpath]
set connected(startdata,$modpodpath) -1
set connected(type,$modpodpath) [dict get $argd opts -type]
set connected(fh,$modpodpath) ""
if {[string range [file tail $modpodpath] 0 7] eq "#modpod-"} {
set connected(type,$modpodpath) "unwrapped"
lassign [::split [file tail [file dirname $modpodpath]] -] connected(package,$modpodpath) connected(version,$modpodpath)
set this_pkg_tm_folder [file dirname [file dirname $modpodpath]]
} else {
#connect to .tm but may still be unwrapped version available
lassign [::split [file rootname [file tail $modpodath]] -] connected(package,$modpodpath) connected(version,$modpodpath)
set this_pkg_tm_folder [file dirname $modpodpath]
if {$connected(type,$modpodpath) ne "unwrapped"} {
#Not directly connected to unwrapped version - but may still be redirected there
set unwrappedFolder [file join $connected(location,$modpodpath) #modpod-$connected(package,$modpodpath)-$connected(version,$modpodpath)]
if {[file exists $unwrappedFolder]} {
#folder with exact version-match must exist for redirect to 'unwrapped'
set con(type,$modpodpath) "modpod-redirecting"
}
}
}
set unwrapped_tm_file [file join $this_pkg_tm_folder] "[set connected(package,$modpodpath)]-[set connected(version,$modpodpath)].tm"
set connected(tmfile,$modpodpath)
set tail_segments [list]
set lcase_tmfile_segments [string tolower [file split $this_pkg_tm_folder]]
set lcase_modulepaths [string tolower [tcl::tm::list]]
foreach lc_mpath $lcase_modulepaths {
set mpath_segments [file split $lc_mpath]
if {[llength [struct::set intersect $lcase_tmfile_segments $mpath_segments]] == [llength $mpath_segments]} {
set tail_segments [lrange [file split $this_pkg_tm_folder] [llength $mpath_segments] end]
break
}
}
if {[llength $tail_segments]} {
set connected(fullpackage,$modpodpath) [join [concat $tail_segments [set connected(package,$modpodpath)]] ::] ;#full name of package as used in package require
} else {
set connected(fullpackage,$modpodpath) [set connected(package,$modpodpath)]
}
switch -exact -- $connected(type,$modpodpath) {
"modpod-redirecting" {
#redirect to the unwrapped version
set loadscript_name [file join $unwrappedFolder #modpod-loadscript-$con(package,$modpod).tcl]
}
"unwrapped" {
if {[info commands ::thread::id] ne ""} {
set from [pid],[thread::id]
} else {
set from [pid]
}
#::modpod::Puts stderr "$from-> Package $connected(package,$modpodpath)-$connected(version,$modpodpath) is using unwrapped version: $modpodpath"
return [list ok ""]
}
default {
#autodetect .tm - zip/tar ?
#todo - use vfs ?
#connect to tarball - start at 1st header
set connected(startdata,$modpodpath) 0
set fh [open $modpodpath r]
set connected(fh,$modpodpath) $fh
fconfigure $fh -encoding iso8859-1 -translation binary -eofchar {}
if {$connected(startdata,$modpodpath) >= 0} {
#verify we have a valid tar header
if {![catch {::modpod::system::tar::readHeader [red $fh 512]}]} {
seek $fh $connected(startdata,$modpodpath) start
return [list ok $fh]
} else {
#error "cannot verify tar header"
}
}
lpop connected(to) end
set connected(startdata,$modpodpath) -1
unset connected(fh,$modpodpath)
catch {close $fh}
return [dict create err {Does not appear to be a valid modpod}]
}
}
}
proc disconnect {{modpod ""}} {
variable connected
if {![llength $connected(to)]} {
return 0
}
if {$modpod eq ""} {
puts stderr "modpod::disconnect WARNING: modpod not explicitly specified. Disconnecting last connected: [lindex $connected(to) end]"
set modpod [lindex $connected(to) end]
}
if {[set posn [lsearch $connected(to) $modpod]] == -1} {
puts stderr "modpod::disconnect WARNING: disconnect called when not connected: $modpod"
return 0
}
if {[string length $connected(fh,$modpod)]} {
close $connected(fh,$modpod)
}
array unset connected *,$modpod
set connected(to) [lreplace $connected(to) $posn $posn]
return 1
}
proc get {args} {
set argd [punk::args::get_dict {
-from -default "" -help "path to pod"
*values -min 1 -max 1
filename
} $args]
set frompod [dict get $argd opts -from]
set filename [dict get $argd values filename]
variable connected
#//review
set modpod [::modpod::system::connect_if_not $frompod]
set fh $connected(fh,$modpod)
if {$connected(type,$modpod) eq "unwrapped"} {
#for unwrapped connection - $connected(location) already points to the #modpod-pkg-ver folder
if {[string range $filename 0 0 eq "/"]} {
#absolute path (?)
set path [file join $connected(location,$modpod) .. [string trim $filename /]]
} else {
#relative path - use #modpod-xxx as base
set path [file join $connected(location,$modpod) $filename]
}
set fd [open $path r]
#utf-8?
#fconfigure $fd -encoding iso8859-1 -translation binary
return [list ok [lindex [list [read $fd] [close $fd]] 0]]
} else {
#read from vfs
puts stderr "get $filename from wrapped pod '$frompod' not implemented"
}
}
#*** !doctools
#[list_end] [comment {--- end definitions namespace modpod ---}]
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Secondary API namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval modpod::lib {
namespace export {[a-z]*}; # Convention: export all lowercase
namespace path [namespace parent]
#*** !doctools
#[subsection {Namespace modpod::lib}]
#[para] Secondary functions that are part of the API
#[list_begin definitions]
#proc utility1 {p1 args} {
# #*** !doctools
# #[call lib::[fun utility1] [arg p1] [opt {?option value...?}]]
# #[para]Description of utility1
# return 1
#}
proc is_valid_tm_version {versionpart} {
#Needs to be suitable for use with Tcl's 'package vcompare'
if {![catch [list package vcompare $versionparts $versionparts]]} {
return 1
} else {
return 0
}
}
#zipfile is a pure zip at this point - ie no script/exe header
proc make_zip_modpod {args} {
set argd [punk::args::get_dict {
@id -id ::modpod::lib::make_zip_modpod
-offsettype -default "archive" -choices {archive file} -help\
"Whether zip offsets are relative to start of file or start of zip-data within the file.
'archive' relative offsets are easier to work with (for writing/updating) in tools such as 7zip,peazip,
but other tools may be easier with 'file' relative offsets. (e.g info-zip,pkzip)
info-zip's 'zip -A' can sometimes convert archive-relative to file-relative.
-offsettype archive is equivalent to plain 'cat prefixfile zipfile > modulefile'"
@values -min 2 -max 2
zipfile -type path -minsize 1 -help "path to plain zip file with subfolder #modpod-packagename-version containing .tm, data files and/or binaries"
outfile -type path -minsize 1 -help "path to output file. Name should be of the form packagename-version.tm"
} $args]
set zipfile [dict get $argd values zipfile]
set outfile [dict get $argd values outfile]
set opt_offsettype [dict get $argd opts -offsettype]
set mount_stub [string map [list %offsettype% $opt_offsettype] {
#zip file with Tcl loader prepended. Requires either builtin zipfs, or vfs::zip to mount while zipped.
#Alternatively unzip so that extracted #modpod-package-version folder is in same folder as .tm file.
#generated using: modpod::lib::make_zip_modpod -offsettype %offsettype% <zipfile> <tmfile>
if {[catch {file normalize [info script]} modfile]} {
error "modpod zip stub error. Unable to determine module path. (possible safe interp restrictions?)"
}
if {$modfile eq "" || ![file exists $modfile]} {
error "modpod zip stub error. Unable to determine module path"
}
set moddir [file dirname $modfile]
set mod_and_ver [file rootname [file tail $modfile]]
lassign [split $mod_and_ver -] moduletail version
if {[file exists $moddir/#modpod-$mod_and_ver.tm]} {
source $moddir/#modpod-$mod_and_ver/$mod_and_ver.tm
} else {
#determine module namespace so we can mount appropriately
proc intersect {A B} {
if {[llength $A] == 0} {return {}}
if {[llength $B] == 0} {return {}}
if {[llength $B] > [llength $A]} {
set res $A
set A $B
set B $res
}
set res {}
foreach x $A {set ($x) {}}
foreach x $B {
if {[info exists ($x)]} {
lappend res $x
}
}
return $res
}
set lcase_tmfile_segments [string tolower [file split $moddir]]
set lcase_modulepaths [string tolower [tcl::tm::list]]
foreach lc_mpath $lcase_modulepaths {
set mpath_segments [file split $lc_mpath]
if {[llength [intersect $lcase_tmfile_segments $mpath_segments]] == [llength $mpath_segments]} {
set tail_segments [lrange [file split $moddir] [llength $mpath_segments] end] ;#use properly cased tail
break
}
}
if {[llength $tail_segments]} {
set fullpackage [join [concat $tail_segments $moduletail] ::] ;#full name of package as used in package require
set mount_at #modpod/[file join {*}$tail_segments]/#mounted-modpod-$mod_and_ver
} else {
set fullpackage $moduletail
set mount_at #modpod/#mounted-modpod-$mod_and_ver
}
if {[info commands tcl::zipfs::mount] ne ""} {
#argument order changed to be consistent with vfs::zip::Mount etc
#early versions: zipfs::Mount mountpoint zipname
#since 2023-09: zipfs::Mount zipname mountpoint
#don't use 'file exists' when testing mountpoints. (some versions at least give massive delays on windows platform for non-existance)
#This is presumably related to // being interpreted as a network path
set mountpoints [dict keys [tcl::zipfs::mount]]
if {"//zipfs:/$mount_at" ni $mountpoints} {
#despite API change tcl::zipfs package version was unfortunately not updated - so we don't know argument order without trying it
if {[catch {
#tcl::zipfs::mount $modfile //zipfs:/#mounted-modpod-$mod_and_ver ;#extremely slow if this is a wrong guess (artifact of aforementioned file exists issue ?)
#puts "tcl::zipfs::mount $modfile $mount_at"
tcl::zipfs::mount $modfile $mount_at
} errM]} {
#try old api
if {![catch {tcl::zipfs::mount //zipfs:/$mount_at $modfile}]} {
puts stderr "modpod stub>>> tcl::zipfs::mount <file> <mountpoint> failed.\nbut old api: tcl::zipfs::mount <mountpoint> <file> succeeded\n tcl::zipfs::mount //zipfs://$mount_at $modfile"
puts stderr "Consider upgrading tcl runtime to one with fixed zipfs API"
}
}
if {![file exists //zipfs:/$mount_at/#modpod-$mod_and_ver/$mod_and_ver.tm]} {
puts stderr "modpod stub>>> mount at //zipfs:/$mount_at/#modpod-$mod_and_ver/$mod_and_ver.tm failed\n zipfs mounts: [zipfs mount]"
#tcl::zipfs::unmount //zipfs:/$mount_at
error "Unable to find $mod_and_ver.tm in $modfile for module $fullpackage"
}
}
# #modpod-$mod_and_ver subdirectory always present in the archive so it can be conveniently extracted and run in that form
source //zipfs:/$mount_at/#modpod-$mod_and_ver/$mod_and_ver.tm
} else {
#fallback to slower vfs::zip
#NB. We don't create the intermediate dirs - but the mount still works
if {![file exists $moddir/$mount_at]} {
if {[catch {package require vfs::zip} errM]} {
set msg "Unable to load vfs::zip package to mount module $mod_and_ver (and zipfs not available either)"
append msg \n "If neither zipfs or vfs::zip are available - the module can still be loaded by manually unzipping the file $modfile in place."
append msg \n "The unzipped data will all be contained in a folder named #modpod-$mod_and_ver in the same parent folder as $modfile"
error $msg
} else {
set fd [vfs::zip::Mount $modfile $moddir/$mount_at]
if {![file exists $moddir/$mount_at/#modpod-$mod_and_ver/$mod_and_ver.tm]} {
vfs::zip::Unmount $fd $moddir/$mount_at
error "Unable to find $mod_and_ver.tm in $modfile for module $fullpackage"
}
}
}
source $moddir/$mount_at/#modpod-$mod_and_ver/$mod_and_ver.tm
}
}
#zipped data follows
}]
#todo - test if supplied zipfile has #modpod-loadcript.tcl or some other script/executable before even creating?
append mount_stub \x1A
modpod::system::make_mountable_zip $zipfile $outfile $mount_stub $opt_offsettype
}
#*** !doctools
#[list_end] [comment {--- end definitions namespace modpod::lib ---}]
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section Internal]
namespace eval modpod::system {
#*** !doctools
#[subsection {Namespace modpod::system}]
#[para] Internal functions that are not part of the API
#deflate,store only supported
#zipfile here is plain zip - no script/exe prefix part.
proc make_mountable_zip {zipfile outfile mount_stub {offsettype "archive"}} {
set inzip [open $zipfile r]
fconfigure $inzip -encoding iso8859-1 -translation binary
set out [open $outfile w+]
fconfigure $out -encoding iso8859-1 -translation binary
puts -nonewline $out $mount_stub
set stuboffset [tell $out]
lappend report "stub size: $stuboffset"
fcopy $inzip $out
close $inzip
set size [tell $out]
lappend report "modpod::system::make_mountable_zip"
lappend report "tmfile : [file tail $outfile]"
lappend report "output size : $size"
lappend report "offsettype : $offsettype"
if {$offsettype eq "file"} {
#make zip offsets relative to start of whole file including prepended script.
#same offset structure as Tcl's older 'zipfs mkimg' as at 2024-10
#2025 - zipfs mkimg fixed to use 'archive' offset.
#not editable by 7z,nanazip,peazip
#we aren't adding any new files/folders so we can edit the offsets in place
#Now seek in $out to find the end of directory signature:
#The structure itself is 24 bytes Long, followed by a maximum of 64Kbytes text
if {$size < 65559} {
set tailsearch_start 0
} else {
set tailsearch_start [expr {$size - 65559}]
}
seek $out $tailsearch_start
set data [read $out]
#EOCD - End of Central Directory record
#PK\5\6
set start_of_end [string last "\x50\x4b\x05\x06" $data]
#set start_of_end [expr {$start_of_end + $seek}]
#incr start_of_end $seek
set filerelative_eocd_posn [expr {$start_of_end + $tailsearch_start}]
lappend report "kitfile-relative START-OF-EOCD: $filerelative_eocd_posn"
seek $out $filerelative_eocd_posn
set end_of_ctrl_dir [read $out]
binary scan $end_of_ctrl_dir issssiis eocd(signature) eocd(disknbr) eocd(ctrldirdisk) \
eocd(numondisk) eocd(totalnum) eocd(dirsize) eocd(diroffset) eocd(comment_len)
lappend report "End of central directory: [array get eocd]"
seek $out [expr {$filerelative_eocd_posn+16}]
#adjust offset of start of central directory by the length of our sfx stub
puts -nonewline $out [binary format i [expr {$eocd(diroffset) + $stuboffset}]]
flush $out
seek $out $filerelative_eocd_posn
set end_of_ctrl_dir [read $out]
binary scan $end_of_ctrl_dir issssiis eocd(signature) eocd(disknbr) eocd(ctrldirdisk) \
eocd(numondisk) eocd(totalnum) eocd(dirsize) eocd(diroffset) eocd(comment_len)
# 0x06054b50 - end of central dir signature
puts stderr "$end_of_ctrl_dir"
puts stderr "comment_len: $eocd(comment_len)"
puts stderr "eocd sig: $eocd(signature) [punk::lib::dec2hex $eocd(signature)]"
lappend report "New dir offset: $eocd(diroffset)"
lappend report "Adjusting $eocd(totalnum) zip file items."
catch {
punk::lib::showdict -roottype list -chan stderr $report ;#heavy dependencies
}
seek $out $eocd(diroffset)
for {set i 0} {$i <$eocd(totalnum)} {incr i} {
set current_file [tell $out]
set fileheader [read $out 46]
puts --------------
puts [ansistring VIEW -lf 1 $fileheader]
puts --------------
#binary scan $fileheader is2sss2ii2s3ssii x(sig) x(version) x(flags) x(method) \
# x(date) x(crc32) x(sizes) x(lengths) x(diskno) x(iattr) x(eattr) x(offset)
binary scan $fileheader ic4sss2ii2s3ssii x(sig) x(version) x(flags) x(method) \
x(date) x(crc32) x(sizes) x(lengths) x(diskno) x(iattr) x(eattr) x(offset)
set ::last_header $fileheader
puts "sig: $x(sig) (hex: [punk::lib::dec2hex $x(sig)])"
puts "ver: $x(version)"
puts "method: $x(method)"
#PK\1\2
#33639248 dec = 0x02014b50 - central directory file header signature
if { $x(sig) != 33639248 } {
error "modpod::system::make_mountable_zip Bad file header signature at item $i: dec:$x(sig) hex:[punk::lib::dec2hex $x(sig)]"
}
foreach size $x(lengths) var {filename extrafield comment} {
if { $size > 0 } {
set x($var) [read $out $size]
} else {
set x($var) ""
}
}
set next_file [tell $out]
lappend report "file $i: $x(offset) $x(sizes) $x(filename)"
seek $out [expr {$current_file+42}]
puts -nonewline $out [binary format i [expr {$x(offset)+$stuboffset}]]
#verify:
flush $out
seek $out $current_file
set fileheader [read $out 46]
lappend report "old $x(offset) + $stuboffset"
binary scan $fileheader is2sss2ii2s3ssii x(sig) x(version) x(flags) x(method) \
x(date) x(crc32) x(sizes) x(lengths) x(diskno) x(iattr) x(eattr) x(offset)
lappend report "new $x(offset)"
seek $out $next_file
}
}
close $out
#pdict/showdict reuire punk & textlib - ie lots of dependencies
#don't fall over just because of that
catch {
punk::lib::showdict -roottype list -chan stderr $report
}
#puts [join $report \n]
return
}
proc connect_if_not {{podpath ""}} {
upvar ::modpod::connected connected
set podpath [::modpod::system::normalize $podpath]
set docon 0
if {![llength $connected(to)]} {
if {![string length $podpath]} {
error "modpod::system::connect_if_not - Not connected to a modpod file, and no podpath specified"
} else {
set docon 1
}
} else {
if {![string length $podpath]} {
set podpath [lindex $connected(to) end]
puts stderr "modpod::system::connect_if_not WARNING: using last connected modpod:$podpath for operation\n -podpath not explicitly specified during operation: [info level -1]"
} else {
if {$podpath ni $connected(to)} {
set docon 1
}
}
}
if {$docon} {
if {[lindex [modpod::connect $podpath]] 0] ne "ok"} {
error "modpod::system::connect_if_not error. file $podpath does not seem to be a valid modpod"
} else {
return $podpath
}
}
#we were already connected
return $podpath
}
proc myversion {} {
upvar ::modpod::connected connected
set script [info script]
if {![string length $script]} {
error "No result from \[info script\] - modpod::system::myversion should only be called from within a loading modpod"
}
set fname [file tail [file rootname [file normalize $script]]]
set scriptdir [file dirname $script]
if {![string match "#modpod-*" $fname]} {
lassign [lrange [split $fname -] end-1 end] _pkgname version
} else {
lassign [scan [file tail [file rootname $script]] {#modpod-loadscript-%[a-z]-%s}] _pkgname version
if {![string length $version]} {
#try again on the name of the containing folder
lassign [scan [file tail $scriptdir] {#modpod-%[a-z]-%s}] _pkgname version
#todo - proper walk up the directory tree
if {![string length $version]} {
#try again on the grandparent folder (this is a standard depth for sourced .tcl files in a modpod)
lassign [scan [file tail [file dirname $scriptdir]] {#modpod-%[a-z]-%s}] _pkgname version
}
}
}
#tarjar::Log debug "'myversion' determined version for [info script]: $version"
return $version
}
proc myname {} {
upvar ::modpod::connected connected
set script [info script]
if {![string length $script]} {
error "No result from \[info script\] - modpod::system::myname should only be called from within a loading modpod"
}
return $connected(fullpackage,$script)
}
proc myfullname {} {
upvar ::modpod::connected connected
set script [info script]
#set script [::tarjar::normalize $script]
set script [file normalize $script]
if {![string length $script]} {
error "No result from \[info script\] - modpod::system::myfullname should only be called from within a loading tarjar"
}
return $::tarjar::connected(fullpackage,$script)
}
proc normalize {path} {
#newer versions of Tcl don't do tilde sub
#Tcl's 'file normalize' seems to do some unfortunate tilde substitution on windows.. (at least for relative paths)
# we take the assumption here that if Tcl's tilde substitution is required - it should be done before the path is provided to this function.
set matilda "<_tarjar_tilde_placeholder_>" ;#token that is *unlikely* to occur in the wild, and is somewhat self describing in case it somehow ..escapes..
set path [string map [list ~ $matilda] $path] ;#give our tildes to matilda to look after
set path [file normalize $path]
#set path [string tolower $path] ;#must do this after file normalize
return [string map [list $matilda ~] $path] ;#get our tildes back.
}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
package provide modpod [namespace eval modpod {
variable pkg modpod
variable version
set version 0.1.3
}]
return
#*** !doctools
#[manpage_end]

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

@ -5192,7 +5192,7 @@ namespace eval punk {
#lappend idlist_stderr [shellfilter::stack::add stderr ansiwrap -settings {-colour {red bold}}]
#lappend idlist_stdout [shellfilter::stack::add stdout tee_to_var -action float -settings {-varname ::shellrun::runout}]
if {[dict get $::punk::config::running auto_exec_mechanism] eq "experimental"} {
if {[punk::config::configure running auto_exec_mechanism] eq "experimental"} {
#TODO - something cross-platform that allows us to maintain a separate console(s) with an additional set of IO channels to drive it
#not a trivial task
@ -5993,8 +5993,7 @@ namespace eval punk {
proc scriptlibpath {{shortname {}} args} {
upvar ::punk::config::running running_config
set scriptlib [dict get $running_config scriptlib]
set scriptlib [punk::config::configure running scriptlib]
if {[string match "lib::*" $shortname]} {
set relpath [string map [list "lib::" "" "::" "/"] $shortname]
set relpath [string trimleft $relpath "/"]

67
src/vfs/_vfscommon.vfs/modules/punk/aliascore-0.1.0.tm

@ -103,7 +103,9 @@ tcl::namespace::eval punk::aliascore {
#use absolute ns ie must be prefixed with ::
#single element commands are imported if source command already exists, otherwise aliased. multi element commands are aliased
#functions must be in export list of their source namespace
#functions should generally be covered by one of the export patterns of their source namespace
# - if they are not - e.g (separately loaded ensemble command ?)
# the aliascore::init will temporarily extend the exports list to do the import, and then reset the exports to how they were.
set aliases [tcl::dict::create\
val ::punk::pipe::val\
aliases ::punk::lib::aliases\
@ -122,8 +124,8 @@ tcl::namespace::eval punk::aliascore {
stripansi ::punk::ansi::ansistrip\
ansiwrap ::punk::ansi::ansiwrap\
colour ::punk::console::colour\
ansi ::punk::console::ansi\
color ::punk::console::colour\
ansi ::punk::console::ansi\
a? ::punk::console::code_a?\
A? {::punk::console::code_a? forcecolor}\
a+ ::punk::console::code_a+\
@ -132,6 +134,7 @@ tcl::namespace::eval punk::aliascore {
A {::punk::console::code_a forcecolour}\
smcup ::punk::console::enable_alt_screen\
rmcup ::punk::console::disable_alt_screen\
config ::punk::config\
]
#*** !doctools
@ -153,6 +156,35 @@ tcl::namespace::eval punk::aliascore {
# return "ok"
#}
proc _is_exported {ns cmd} {
set exports [::tcl::namespace::eval $ns [list namespace export]]
set is_exported 0
foreach p $exports {
if {[string match $p $cmd]} {
set is_exported 1
break
}
}
return $is_exported
}
#_nsprefix accepts entire command - not just an existing namespace for which we want the parent
proc _nsprefix {{nspath {}}} {
#maintenance: from punk::ns::nsprefix - (without unnecessary nstail)
#normalize the common case of ::::
set nspath [string map {:::: ::} $nspath]
set rawprefix [string range $nspath 0 end-[string length [namespace tail $nspath]]]
if {$rawprefix eq "::"} {
return $rawprefix
} else {
if {[string match *:: $rawprefix]} {
return [string range $rawprefix 0 end-2]
} else {
return $rawprefix
}
}
}
#todo - options as to whether we should raise an error if collisions found, undo aliases etc?
proc init {args} {
set defaults {-force 0}
@ -195,6 +227,7 @@ tcl::namespace::eval punk::aliascore {
error "punk::aliascore::init declined to create any aliases or imports because -force == 0 and conflicts found:$conflicts"
}
set failed [list]
set tempns ::temp_[info cmdcount] ;#temp ns for renames
dict for {a cmd} $aliases {
#puts "aliascore $a -> $cmd"
@ -206,16 +239,36 @@ tcl::namespace::eval punk::aliascore {
} else {
if {[tcl::info::commands $cmd] ne ""} {
#todo - ensure exported? noclobber?
if {[tcl::namespace::tail $a] eq [tcl::namespace::tail $cmd]} {
set container_ns [_nsprefix $cmd]
set cmdtail [tcl::namespace::tail $cmd]
set was_exported 1 ;#assumption
if {![_is_exported $container_ns $cmdtail]} {
set was_exported 0
set existing_exports [tcl::namespace::eval $container_ns [list ::namespace export]]
tcl::namespace::eval $container_ns [list ::namespace export $cmdtail]
}
if {[tcl::namespace::tail $a] eq $cmdtail} {
#puts stderr "importing $cmd"
tcl::namespace::eval :: [list namespace import $cmd]
try {
tcl::namespace::eval :: [list ::namespace import $cmd]
} trap {} {emsg eopts} {
lappend failed [list alias $a target $cmd errormsg $emsg]
}
} else {
#target command name differs from exported name
#e.g stripansi -> punk::ansi::ansistrip
#import and rename
#puts stderr "importing $cmd (with rename to ::$a)"
tcl::namespace::eval $tempns [list namespace import $cmd]
catch {rename ${tempns}::[namespace tail $cmd] ::$a}
try {
tcl::namespace::eval $tempns [list ::namespace import $cmd]
} trap {} {emsg eopst} {
lappend failed [list alias $a target $cmd errormsg $emsg]
}
catch {rename ${tempns}::$cmdtail ::$a}
}
#restore original exports
if {!$was_exported} {
tcl::namespace::eval $container_ns [list ::namespace export -clear {*}$existing_exports]
}
} else {
interp alias {} $a {} {*}$cmd
@ -223,7 +276,7 @@ tcl::namespace::eval punk::aliascore {
}
}
#tcl::namespace::delete $tempns
return [dict create aliases [dict keys $aliases] unchanged $ignore_aliases changed $conflicts]
return [dict create aliases [dict keys $aliases] existing $existing ignored $ignore_aliases changed $conflicts failed $failed]
}

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

@ -3357,9 +3357,20 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
lappend PUNKARGS [list {
@id -id ::punk::ansi::ansiwrap
@cmd -name punk::ansi::ansiwrap -help\
"Wrap a string with ANSI codes from
{Wrap a string with ANSI codes from
supplied codelist(s) followed by trailing
ANSI reset.
ANSI reset. The wrapping is done such that
after every reset in the supplied text, the
default goes back to the supplied codelist.
e.g1 in the following
ansiwrap red bold "rrr[a+ green]ggg[a]rrr"
both strings rrr will be red & bold
e.g2 bolding and underlining specific text whilst dimming the rest
ansiwrap dim [string map [list test [ansiwrap bold underline test]] "A test string"]
e.g3 reverse render a complex ansi substring
ansiwrap reverse [textblock::periodic]
Codes are numbers or strings as indicated
in the output of the colour information
@ -3372,41 +3383,172 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
For finer control use the a+ and a
functions eg
set x \"[a+ red]text [a+ bold]etc[a]\"
"
set x "[a+ red]text [a+ bold]etc[a]"
}
@leaders -min 0 -max -1
codelist -multiple 1 -default {} -type list -help\
"ANSI names/ints as understood by 'a?'
(Not actual ANSI as output by a+)
These can be supplied individually or
as a list or lists"
@opts
-rawansi -type ansi -default ""
-resetcodes -type list -default {reset}
-rawresets -type ansi -default ""
-fullcodemerge -type boolean -default 0 -help\
"experimental"
-overridecodes -type list -default {}
@values -min 1 -max 1
text -type string -help\
"String to wrap with ANSI (SGR)"
}]
#proc ansiwrap {codes text} {
# return [a {*}$codes]$text[a]
#}
proc ansiwrap2 {args} {
set argd [punk::args::parse $args withid ::punk::ansi::ansiwrap]
set codelists [dict get $argd leaders codelist]
set text [dict get $argd values text]
set codes [concat {*}$codelists] ;#flatten
return [a {*}$codes]$text[a]
}
proc ansiwrap {args} {
if {[llength $args] < 1} {
#minimal args parsing - unhappy path only
#throw to args::parse to get friendly error/usage display
punk::args::parse $args withid ::punk::ansi::ansiwrap
return
}
#we know there are no valid codes that start with -
if {[lsearch [lrange $args 0 end-1] -*] == -1} {
#no opts
set text [lindex $args end]
set codelists [lrange $args 0 end-1]
set R [a] ;#plain ansi reset
set rawansi ""
set rawresets ""
set fullmerge 0
set overrides ""
} else {
set argd [punk::args::parse $args withid ::punk::ansi::ansiwrap]
lassign [dict values $argd] leaders opts values received solos
set codelists [dict get $leaders codelist]
set text [dict get $values text]
set rawansi [dict get $opts -rawansi]
set R [a+ {*}[dict get $opts -resetcodes]]
set rawresets [dict get $opts -rawresets]
set fullmerge [dict get $opts -fullcodemerge]
set overrides [punk::ansi::ta::get_codes_single [a+ {*}[dict get $opts -overridecodes]]]
}
#note that the result of any sgr_merge or sgr_merge_singles is not necessarily a single Ansi escape sequence.
#there can be SGR unmergeables (due to enhanced underlines) as well as non SGR codes
set codes [concat {*}$codelists] ;#flatten
return [a {*}$codes]$text[a]
set base [a+ {*}$codes]
if {$rawansi ne ""} {
set rawcodes [punk::ansi::ta::get_codes_single $rawansi] ;#caller may have supplied as [a+ xxx][a+ yyy]
if {$fullmerge} {
set base [punk::ansi::codetype::sgr_merge [list $base {*}$rawcodes]]
} else {
set base [punk::ansi::codetype::sgr_merge_singles [list $base {*}$rawcodes]]
}
}
if {$rawresets ne ""} {
set rawresetcodes [punk::ansi::ta::get_codes_single $rawresets]
if {$fullmerge} {
set R [punk::ansi::codetype::sgr_merge [list $R {*}$rawresetcodes]]
} else {
set R [punk::ansi::codetype::sgr_merge_singles [list $R {*}$rawresetcodes]]
}
}
set codestack [list]
if {[punk::ansi::ta::detect $text]} {
set emit ""
set parts [punk::ansi::ta::split_codes $text]
foreach {pt code} $parts {
switch -- [llength $codestack] {
0 {
append emit $base$pt$R
}
1 {
if {[punk::ansi::codetype::is_sgr_reset [lindex $codestack 0]]} {
append emit $base$pt$R
set codestack [list]
} else {
#append emit [lindex $o_codestack 0]$pt
if {$fullmerge} {
append emit [punk::ansi::codetype::sgr_merge [list $base {*}$codestack {*}$overrides]]$pt$R
} else {
append emit [punk::ansi::codetype::sgr_merge_singles [list $base {*}$codestack {*}$overrides]]$pt$R
}
}
}
default {
if {$fullmerge} {
append emit [punk::ansi::codetype::sgr_merge [list $base {*}$codestack {*}$overrides]]$pt$R
} else {
append emit [punk::ansi::codetype::sgr_merge_singles [list $base {*}$codestack {*}$overrides]]$pt$R
}
}
}
#parts ends on a pt - last code always empty string
if {$code ne ""} {
set c1c2 [tcl::string::range $code 0 1]
set leadernorm [tcl::string::range [tcl::string::map [list\
\x1b\[ 7CSI\
\x9b 8CSI\
\x1b\( 7GFX\
] $c1c2] 0 3]
switch -- $leadernorm {
7CSI - 8CSI {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set codestack [list "\x1b\[m"]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set codestack [list $code]
} elseif {[punk::ansi::codetype::is_sgr $code]} {
#todo - make caching is_sgr method
set dup_posns [lsearch -all -exact $codestack $code]
set codestack [lremove $codestack {*}$dup_posns]
lappend codestack $code
} else {
}
}
7GFX {
switch -- [tcl::string::index $code 2] {
"0" {
set o_gx_state on
}
"B" {
set o_gx_state off
}
}
}
default {
#other ansi codes
}
}
append emit $code
}
}
return $emit$R
} else {
return $base$text$R
}
}
proc ansiwrap_naive {codes text} {
return [a_ {*}$codes]$text[a]
}
#a silly trick... temporary? probably - todo - tests and work on sgr_merge + sgr_merge_singles before relying on this
#when we use sgr_merge_singles on a 'single' containing a non SGR code e.g <esc>[5h (inverse) it puts this code at the end of the list
#furthermore - it carries any SGR codes along with it (Can/should we rely on this behaviour??? probably not) REVIEW
#P% ansistring VIEW $s1
#- ␛[31m␛[?5h
#P% ansistring VIEW [punk::ansi::codetype::sgr_merge_singles [list $s1 [a+ cyan]]]
#- ␛[36m␛[31m␛[?5h
#P% ansistring VIEW [punk::ansi::codetype::sgr_merge [list $s1 [a+ cyan]]]
#- ␛[36m␛[?5h
#we can use this trick to override background and/or foreground colours using ansiwrap - which uses sgr_merge_singles
#Note - this trick is not composable - e.g ansioverride Red [ansiioverride Green [textblock::periodic]] doesn't work as expected.
proc ansioverride2 {args} {
set text [lindex $args end]
set codes [lrange $args 0 end-1]
ansiwrap {*}$codes -rawansi [punk::ansi::enable_inverse] -rawresets [punk::ansi::disable_inverse] $text
}
proc ansireverse {text} {
ansioverride2 normal reverse $text
}
proc get_code_name {code} {
#*** !doctools
@ -4491,6 +4633,77 @@ tcl::namespace::eval punk::ansi {
return 0
}
}
#e.g has_any_effective $str bg fg
proc has_any_effective {str args} {
set singlecodes [punk::ansi::ta::get_codes_single $str]
set mergeinfo [punk::ansi::codetype::sgr_merge_singles $singlecodes -info 1]
foreach t $args {
switch -- $t {
sgr - unmergeable - othercodes {
if {[dict get $mergeinfo $t] ne ""} {
return 1
}
}
intensity - italic - underline - underextended - blink - reverse - hide - strike - font - gothic - doubleunderline
- proportional - frame_or_circle - ideogram_underline - ideogram_doubleunderline - ideogram_clear - overline - underlinecolour - superscript - subscript
- nosupersub - fg - bg {
if {[dict get $mergeinfo codestate $t] ne ""} {
return 1
}
}
bold {
if {[dict get $mergeinfo codestate intensity] eq "1"} {
return 1
}
}
dim {
if {[dict get $mergeinfo codestate intensity] eq "2"} {
return 1
}
}
default {
error "punk::ansi::ta::has_any_effective invalid type '$t' specified"
}
}
}
return 0
}
proc has_all_effective {str args} {
set singlecodes [punk::ansi::ta::get_codes_single $str]
set mergeinfo [punk::ansi::codetype::sgr_merge_singles $singlecodes -info 1]
foreach t $args {
switch -- $t {
sgr - unmergeable - othercodes {
if {[dict get $mergeinfo $t] eq ""} {
return 0
}
}
intensity - italic - underline - underextended - blink - reverse - hide - strike - font - gothic - doubleunderline
- proportional - frame_or_circle - ideogram_underline - ideogram_doubleunderline - ideogram_clear - overline - underlinecolour - superscript - subscript
- nosupersub - fg - bg {
if {[dict get $mergeinfo codestate $t] eq ""} {
return 0
}
}
bold {
if {[dict get $mergeinfo codestate intensity] ne "1"} {
return 0
}
}
dim {
if {[dict get $mergeinfo codestate intensity] ne "2"} {
return 0
}
}
default {
error "punk::ansi::ta::has_any_effective invalid type '$t' specified"
}
}
}
return 1
}
proc is_gx {code} {
#g0 {(?:\x1b\(0)(?:(?!\x1b\(B).)*\x1b\(B}
#g1 {(?:\x1b\)0)(?:(?!\x1b\)B).)*\x1b\)B}
@ -4513,6 +4726,7 @@ tcl::namespace::eval punk::ansi {
set codestate_empty [tcl::dict::create]
tcl::dict::set codestate_empty rst "" ;#0 (or empty)
tcl::dict::set codestate_empty intensity "" ;#1 bold, 2 dim, 22 normal
tcl::dict::set codestate_empty shadowed "" ;
tcl::dict::set codestate_empty italic "" ;#3 on 23 off
tcl::dict::set codestate_empty underline "" ;#4 on 24 off

31
src/vfs/_vfscommon.vfs/modules/punk/args-0.1.4.tm

@ -3226,7 +3226,36 @@ tcl::namespace::eval punk::args {
form1: parse $arglist ?-flag val?... withid $id
form2: parse $arglist ?-flag val?... withdef $def ?$def?
see punk::args::define"
see punk::args::define
Returns a dict of information regarding the parsed arguments
example of basic usage for single option only:
punk::args::define {
@id -id ::myns::myfunc
@cmd -name myns::myfunc
@leaders -min 0 -max 0
@opts
-configfile -type existingfile
#type none makes it a solo flag
-verbose -type none
@values -min 0 -max 0
}
proc myfunc {args} {
set argd [punk::args::parse $args withid ::myns::myfunc]
lassign [dict values $argd] leaders opts values received solos
if {[dict exists $received] -configfile} {
puts \"have option for existing file [dict get $opts -configfile]\"
}
}
The leaders, opts, values keys in the parse result dict are proper dicts.
The received key is dict-like but can have repeated keys for arguments than can
accept multiples. The value for each received element is the ordinal position.
The solos key refers to a list of solo flags received (those specified with
-type none). This is generally only useful to assist in passing arguments on
to another procedure which also requires solos, because the opts dict contains
solo flags with a 1 value or a list of 1's if it was a solo with -multiple true
specified.
"
@form -form {withid withdef}
@leaders -min 1 -max 1
arglist -type list -optional 0 -help\

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

@ -750,26 +750,6 @@ tcl::namespace::eval punk::args::tclcore {
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
############################################################################################################################################################
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
punk::args::define {
@id -id ::lrange
@cmd -name "builtin: lrange" -help\
"return one or more adjacent elements from a list.
The new list returned consists of elements first through last, inclusive.
The index values first and last are interpreted the same as index values
for the command 'string index', supporting simple index arithmetic and
indices relative to the end of the list.
e.g lrange {a b c} 0 end-1
"
@values -min 3 -max 3
list -type list -help\
"tcl list as a value"
first -help\
"index expression for first element"
last -help\
"index expression for last element"
} "@doc -name Manpage: -url [manpage_tcl lrange]"
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
@ -802,26 +782,60 @@ tcl::namespace::eval punk::args::tclcore {
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
punk::args::define {
@id -id ::lremove
@cmd -name "builtin: lremove" -help\
"Remove elements from a list by index
lremove returns a new list formed by simultaneously removing zero or
more elements of list at each of the indices given by an arbitrary
number of index arguments. The indices may be in any order and may be
repeated; the element at index will only be removed once. The index
values are interpreted the same as index values for the command
'string index', supporting simple index arithmetic and indices relative
to the end of the list. 0 refers to the first element of the list, and
end refers to the last element of the list."
@id -id ::lindex
@cmd -name "builtin: lindex" -help\
"Retrieve an element from a list
"
@values -min 1 -max -1
list -type list -help\
"tcl list as a value"
index -type indexexpression -multiple 1 -optional 1
index -type indexexpression -multiple 1 -optional 1 -help\
"When no index is supplied or a single index is supplied as an empty list,
the value of the entire list is simply returned.
If a single index is supplied and is a list of indices - this list is used
as a sequence of nested indices.
The command,
lindex $a 1 2 3
or
lindex $l {1 2 3}
is synonymous with
lindex [lindex [lindex $a 1] 2] 3
When presented with a single indes, the lindex command treats list as a Tcl list
and returns the index'th element from it (0 refers to the first element of the
list). In extracting the element, lindex observes the same rules concerning
braces and quotes and backslashes as the Tcl command interpreter; however,
variable substution and command substitution do not occur. If index is negative
or greater than or equal to the number of elements in 'list', then an empty
string is returned. The interpretation of each simple index value is the same
as for the command 'string index', supporting simple index arithmetic and
indices relative to the end of the list.
@seealso -commands {list lappend lassign ledit lindex linsert llength lmap lpop lrange lrepeat lreplace lreverse lsearch lseq lset lsort}
} "@doc -name Manpage: -url [manpage_tcl lremove]"
If additional index arguments are supplied, then each argument is used in turn
to select an element from the previous indexing operation, allowing the script
to select elements from sublists."
} "@doc -name Manpage: -url [manpage_tcl lindex]"
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
punk::args::define {
@id -id ::list
@cmd -name "builtin: list" -help\
"Create a list
This command returns a list comprised of all the args, or an empty string
if no args are specified. Braces and backslashes get added as necessary,
so that the lindex command may be used on the result to re-extract the
original arguments, and also so that eval may be used to execute the
resulting list, with arg1 comprising the command's name and the other args
comprising its arguments. List produces slightly different results than
concat: concat removes one level of grouping before forming the list,
while list works directly from the original arguments."
@values -min 0 -max -1
arg -type any -optional 1 -multiple 1
} "@doc -name Manpage: -url [manpage_tcl list]"
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
punk::args::define {
@id -id ::lpop
@ -842,6 +856,51 @@ tcl::namespace::eval punk::args::tclcore {
previous indexing operation, allowing the script to remove elements
in sublists, similar to lindex and lset."
} "@doc -name Manpage: -url [manpage_tcl lpop]"
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
punk::args::define {
@id -id ::lrange
@cmd -name "builtin: lrange" -help\
"return one or more adjacent elements from a list.
The new list returned consists of elements first through last, inclusive.
The index values first and last are interpreted the same as index values
for the command 'string index', supporting simple index arithmetic and
indices relative to the end of the list.
e.g lrange {a b c} 0 end-1
"
@values -min 3 -max 3
list -type list -help\
"tcl list as a value"
first -help\
"index expression for first element"
last -help\
"index expression for last element"
} "@doc -name Manpage: -url [manpage_tcl lrange]"
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
punk::args::define {
@id -id ::lremove
@cmd -name "builtin: lremove" -help\
"Remove elements from a list by index
lremove returns a new list formed by simultaneously removing zero or
more elements of list at each of the indices given by an arbitrary
number of index arguments. The indices may be in any order and may be
repeated; the element at index will only be removed once. The index
values are interpreted the same as index values for the command
'string index', supporting simple index arithmetic and indices relative
to the end of the list. 0 refers to the first element of the list, and
end refers to the last element of the list."
@values -min 1 -max -1
list -type list -help\
"tcl list as a value"
index -type indexexpression -multiple 1 -optional 1
@seealso -commands {list lappend lassign ledit lindex linsert llength lmap lpop lrange lrepeat lreplace lreverse lsearch lseq lset lsort}
} "@doc -name Manpage: -url [manpage_tcl lremove]"
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
punk::args::define {
@id -id ::lrange

13
src/vfs/_vfscommon.vfs/modules/punk/basictelnet-0.1.0.tm

@ -478,6 +478,13 @@ namespace eval punk::basictelnet {
set tmode [dict get $argd opts -mode]
set mouse [dict get $argd opts -mouse]
if {[info commands ::colour] ne ""} {
#The ansiwrap filter on stdout/stderr slows rendering significantly e.g on max headroom ansi vid at server: 1984.ws
#TODO - just disable the channel filters - not all ansi colour.
set priorcolourstate [::colour]
::colour off
}
#todo - check for vt52 and don't try DEC queries
if {[info commands ::mode] eq ""} {
puts stderr "::mode command for terminal is unavailable - please set line/raw mode manually on the terminal"
@ -540,6 +547,12 @@ namespace eval punk::basictelnet {
vwait ::punk::basictelnet::closed($sock)
unset closed($sock)
chan conf stdin -blocking 1
if {[info commands ::colour] ne ""} {
::colour $priorcolourstate
}
if {[info commands ::mode] ne ""} {
::mode $priormode
}

274
src/vfs/_vfscommon.vfs/modules/punk/config-0.1.tm

@ -1,23 +1,109 @@
tcl::namespace::eval punk::config {
variable loaded
variable startup ;#include env overrides
variable running
variable configdata [dict create] ;#key on config names. At least default, startup, running
#variable startup ;#include env overrides
#variable running
variable punk_env_vars
variable other_env_vars
variable vars
namespace export {[a-z]*}
namespace ensemble create
namespace eval punk {namespace export config}
proc _homedir {} {
if {[info exists ::env(HOME)]} {
set home [file normalize $::env(HOME)]
} else {
#not available on 8.6? ok will error out here.
set home [file tildeexpand ~]
}
return $home
}
lappend PUNKARGS [list {
@id -id ::punk::config::dir
@cmd -name punk::config::dir -help\
"Get the path for the default config folder
Config files are in toml format.
The XDG_CONFIG_HOME env var is the preferred
choice of location.
A folder under the user's home directory,
at .config/punk/shell is chosen if
XDG_CONFIG_HOME is not configured.
"
@leaders -min 0 -max 0
@opts
-quiet -type none -help\
"Suppress warning given when the folder does
not yet exist"
@values -min 0 -max 0
}]
proc dir {args} {
if {"-quiet" in $args} {
set be_quiet [dict exists $received -quiet]
}
set was_noisy 0
set config_home [punk::config::configure running xdg_config_home]
set config_dir [file join $config_home punk shell]
if {!$be_quiet && ![file exists $config_dir]} {
set msg "punk::shell data storage folder at $config_dir does not yet exist."
puts stderr $msg
set was_noisy 1
}
if {!$be_quiet && $was_noisy} {
puts stderr "punk::config::dir - call with -quiet option to suppress these messages"
}
return $config_dir
#if {[info exists ::env(XDG_CONFIG_HOME)]} {
# set config_home $::env(XDG_CONFIG_HOME)
#} else {
# set config_home [file join [_homedir] .config]
# if {!$be_quiet} {
# puts stderr "Environment variable XDG_CONFIG_HOME does not exist - consider setting it if $config_home is not a suitable location"
# set was_noisy 1
# }
#}
#if {!$be_quiet && ![file exists $config_home]} {
# #parent folder for 'punk' config dir doesn't exist
# set msg "configuration location (XDG_CONFIG_HOME or ~/.config) $config_home does not yet exist"
# append msg \n " - please create it and/or set XDG_CONFIG_HOME env var."
# puts stderr $msg
# set was_noisy 1
#}
#set config_dir [file join $config_home punk shell]
#if {!$be_quiet && ![file exists $config_dir]} {
# set msg "punk::shell data storage folder at $config_dir does not yet exist."
# append msg \n " It will be created if api_context_save is called without specifying an alternate location."
# puts stderr $msg
# set was_noisy 1
#}
#if {!$be_quiet && $was_noisy} {
# puts stderr "punk::config::dir - call with -quiet option to suppress these messages"
#}
#return [file join $configdir config.toml]
}
#todo - XDG_DATA_HOME etc
#https://specifications.freedesktop.org/basedir-spec/latest/
# see also: http://hiphish.github.io/blog/2020/08/30/dotfiles-were-a-mistake/
proc init {} {
variable defaults
variable startup
variable running
variable configdata
#variable defaults
#variable startup
#variable running
variable punk_env_vars
variable punk_env_vars_config
variable other_env_vars
@ -108,12 +194,14 @@ tcl::namespace::eval punk::config {
#we have a choice of LOCALAPPDATA vs APPDATA (local to machine vs potentially roaming/redirected in a corporate environment)
#using the roaming location should not impact users who aren't using a domain controller but is potentially much more convenient for those who do.
if {[info exists ::env(APPDATA)]} {
#Typical existing/default value for env(APPDATA) on windows is c:\Users\<username>\AppData\Roaming
set default_xdg_config_home $::env(APPDATA)
set default_xdg_data_home $::env(APPDATA)
}
#The xdg_cache_home should be kept local
if {[info exists ::env(LOCALAPPDATA)]} {
#Typical existing/default value for env(APPDATA) on windows is c:\Users\<username>\AppData\Local
set default_xdg_data_home $::env(LOCALAPPDATA)
set default_xdg_cache_home $::env(LOCALAPPDATA)
set default_xdg_state_home $::env(LOCALAPPDATA)
}
@ -133,10 +221,10 @@ tcl::namespace::eval punk::config {
}
}
set defaults [dict create\
dict set configdata defaults [dict create\
apps $default_apps\
config ""\
configset ".punkshell"\
config "startup"\
configset "main"\
scriptlib $default_scriptlib\
color_stdout $default_color_stdout\
color_stdout_repl $default_color_stdout_repl\
@ -160,7 +248,7 @@ tcl::namespace::eval punk::config {
posh_themes_path ""\
]
set startup $defaults
dict set configdata startup [dict get $configdata defaults]
#load values from saved config file - $xdg_config_home/punk/punk.config ?
#typically we want env vars to override the stored config - as env vars conventionally used on some commandlines.
#that's possibly ok for the PUNK_ vars
@ -219,9 +307,9 @@ tcl::namespace::eval punk::config {
lappend final $p
}
}
tcl::dict::set startup $varname $final
tcl::dict::set configdata startup $varname $final
} else {
tcl::dict::set startup $varname $f
tcl::dict::set configdata startup $varname $f
}
}
}
@ -273,29 +361,46 @@ tcl::namespace::eval punk::config {
lappend final $p
}
}
tcl::dict::set startup $varname $final
tcl::dict::set configdata startup $varname $final
} else {
tcl::dict::set startup $varname $f
tcl::dict::set configdata startup $varname $f
}
}
}
}
set config_home [dict get $configdata startup xdg_config_home]
if {![file exists $config_home]} {
puts stderr "punk::config::init creating punk shell config dir: $config_home"
if {[catch {file mkdir $config_home} errM]} {
puts stderr "punk::config::init failed to create dir at $config_home\n$errM"
}
}
set configset [dict get $configdata defaults configset]
set config [dict get $configdata defaults config]
set startupfile [file join $config_home $configset $config.toml]
if {![file exists $startupfile]} {
puts stderr "punk::config::init creating punk shell config file: $config for configset: $configset"
puts stderr "(todo)"
}
#unset -nocomplain vars
#todo
set running [tcl::dict::create]
set running [tcl::dict::merge $running $startup]
dict set configdata running [tcl::dict::merge $running [dict get $configdata startup]]
}
init
#todo
proc Apply {config} {
variable configdata
puts stderr "punk::config::Apply partially implemented"
set configname [string map {-config ""} $config]
if {$configname in {startup running}} {
upvar ::punk::config::$configname applyconfig
set applyconfig [dict get $configdata $configname]
if {[dict exists $applyconfig auto_noexec]} {
set auto [dict get $applyconfig auto_noexec]
@ -315,67 +420,128 @@ tcl::namespace::eval punk::config {
}
return "apply done"
}
Apply startup
#todo - consider how to divide up settings, categories, 'devices', decks etc
proc get_running_global {varname} {
variable running
variable configdata
set running [dict get $configdata running]
if {[dict exists $running $varname]} {
return [dict get $running $varname]
}
error "No such global configuration item '$varname' found in running config"
}
proc get_startup_global {varname} {
variable startup
variable configdata
set startup [dict get $configdata startup]
if {[dict exists $startup $varname]} {
return [dict get $startup $varname]
}
error "No such global configuration item '$varname' found in startup config"
}
proc get {whichconfig {globfor *}} {
variable startup
variable running
lappend PUNKARGS [list {
@id -id ::punk::config::get
@cmd -name punk::config::get -help\
"Get configuration values from a config.
Accepts globs eg XDG*"
@leaders -min 1 -max 1
whichconfig -type string -choices {config startup-configuration running-configuration}
@values -min 0 -max -1
globkey -type string -default * -optional 1 -multiple 1
}]
proc get {args} {
set argd [punk::args::parse $args withid ::punk::config::get]
lassign [dict values $argd] leaders opts values received solos
set whichconfig [dict get $leaders whichconfig]
set globs [dict get $values globkey] ;#list
variable configdata
switch -- $whichconfig {
config - startup - startup-config - startup-configuration {
config - startup-configuration {
#review 'config' ??
#show *startup* config - different behaviour may be confusing to those used to router startup and running configs
set configdata $startup
set configrecords [dict get $configdata startup]
}
running - running-config - running-configuration {
set configdata $running
running-configuration {
set configrecords [dict get $configdata running]
}
default {
error "Unknown config name '$whichconfig' - try startup or running"
}
}
if {$globfor eq "*"} {
return $configdata
if {"*" in $globs} {
return $configrecords
} else {
set keys [dict keys $configdata [string tolower $globfor]]
set keys [list]
foreach g $globs {
lappend keys {*}[dict keys $configrecords [string tolower $g]] ;#review tolower?
}
set filtered [dict create]
foreach k $keys {
dict set filtered $k [dict get $configdata $k]
dict set filtered $k [dict get $configrecords $k]
}
return $filtered
}
}
proc configure {args} {
set argdef {
lappend PUNKARGS [list {
@id -id ::punk::config::configure
@cmd -name punk::config::configure -help\
"UNIMPLEMENTED"
@values -min 1 -max 1
whichconfig -type string -choices {startup running stop}
"Get/set configuration values from a config"
@leaders -min 1 -max 1
whichconfig -type string -choices {defaults startup-configuration running-configuration}
@values -min 0 -max 2
key -type string -optional 1
newvalue -optional 1
}]
proc configure {args} {
set argd [punk::args::parse $args withid ::punk::config::configure]
lassign [dict values $argd] leaders opts values received solos
set whichconfig [dict get $argd leaders whichconfig]
variable configdata
if {"running" ni [dict keys $configdata]} {
init
Apply startup
}
set argd [punk::args::get_dict $argdef $args]
return "unimplemented - $argd"
switch -- $whichconfig {
defaults {
set configrecords [dict get $configdata defaults]
}
startup-configuration {
set configrecords [dict get $configdata startup]
}
running-configuration {
set configrecords [dict get $configdata running]
}
}
if {![dict exists $received key]} {
return $configrecords
}
set key [dict get $values key]
if {![dict exists $received newvalue]} {
return [dict get $configrecords $key]
}
error "setting value not implemented"
}
proc show {whichconfig {globfor *}} {
lappend PUNKARGS [list {
@dynamic
@id -id ::punk::config::show
@cmd -name punk::config::get -help\
"Display configuration values from a config.
Accepts globs eg XDG*"
@leaders -min 1 -max 1
}\
{${[punk::args::resolved_def -types leaders ::punk::config::get]}}\
"@values -min 0 -max -1"\
{${[punk::args::resolved_def -types values ::punk::config::get]}}\
]
proc show {args} {
#todo - tables for console
set configdata [punk::config::get $whichconfig $globfor]
return [punk::lib::showdict $configdata]
set configrecords [punk::config::get {*}$args]
return [punk::lib::showdict $configrecords]
}
@ -459,27 +625,35 @@ tcl::namespace::eval punk::config {
::tcl::namespace::eval punk::config {
#todo - something better - 'previous' rather than reverting to startup
proc channelcolors {{onoff {}}} {
variable running
variable startup
variable configdata
#variable running
#variable startup
if {![string length $onoff]} {
return [list stdout [dict get $running color_stdout] stderr [dict get $running color_stderr]]
return [list stdout [dict get $configdata running color_stdout] stderr [dict get $configdata running color_stderr]]
} else {
if {![string is boolean $onoff]} {
error "channelcolors: invalid value $onoff - expected boolean: true|false|on|off|1|0|yes|no"
}
if {$onoff} {
dict set running color_stdout [dict get $startup color_stdout]
dict set running color_stderr [dict get $startup color_stderr]
dict set configdata running color_stdout [dict get $startup color_stdout]
dict set configdata running color_stderr [dict get $startup color_stderr]
} else {
dict set running color_stdout ""
dict set running color_stderr ""
dict set configdata running color_stdout ""
dict set configdata running color_stderr ""
}
}
return [list stdout [dict get $running color_stdout] stderr [dict get $running color_stderr]]
return [list stdout [dict get $configdata running color_stdout] stderr [dict get $configdata running color_stderr]]
}
}
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::config
}
package provide punk::config [tcl::namespace::eval punk::config {
variable version
set version 0.1

6
src/vfs/_vfscommon.vfs/modules/punk/mod-0.1.tm

@ -33,8 +33,7 @@ namespace eval punk::mod::cli {
return $basehelp
}
proc getraw {appname} {
upvar ::punk::config::running running_config
set app_folders [dict get $running_config apps]
set app_folders [punk::config::configure running apps]
#todo search each app folder
set bases [::list]
set versions [::list]
@ -86,8 +85,7 @@ namespace eval punk::mod::cli {
}
proc list {{glob *}} {
upvar ::punk::config::running running_config
set apps_folder [dict get $running_config apps]
set apps_folder [punk::config::configure running apps]
if {[file exists $apps_folder]} {
if {[file exists $apps_folder/$glob]} {
#tailcall source $apps_folder/$glob/main.tcl

23
src/vfs/_vfscommon.vfs/modules/punk/netbox-0.1.0.tm

@ -562,10 +562,19 @@ tcl::namespace::eval punk::netbox {
used when an explicit path is not given by
the caller to the api_context load/save
functions. This file is in toml format.
On any platform the XDG_DATA_HOME env var
can be used to override the location, but
on Windows the LOCALAPPDATA env var will
specifiy the location if XDG_DATA_HOME is
not set.
Interfacing with a proper secret store
should be considered as an alternative.
On non Windows platforms:
The XDG_DATA_HOME env var is the preferred
choice of location - considered more secure
than XDG_CONFIG_HOME, although not as good
as a proper secret store.
choice of location - considered slightly more
secure than XDG_CONFIG_HOME.
A folder under the user's home directory,
at .local/share/punk/netbox is chosen if
XDG_DATA_HOME is not configured.
@ -585,6 +594,9 @@ tcl::namespace::eval punk::netbox {
set was_noisy 0
if {[info exists ::env(XDG_DATA_HOME)]} {
set data_home $::env(XDG_DATA_HOME)
} else {
if {$::tcl_platform(platform) eq "windows"} {
set data_home $::env(LOCALAPPDATA)
} else {
set data_home [file join [_homedir] .local share]
if {!$be_quiet} {
@ -592,10 +604,11 @@ tcl::namespace::eval punk::netbox {
set was_noisy 1
}
}
}
if {!$be_quiet && ![file exists $data_home]} {
#parent folder for 'punk' config dir doesn't exist
set msg "configuration location (XDG_DATA_HOME or ~/.local/share) $data_home does not yet exist"
append msg \n " - please create it and/or set XDG_DATA_HOME env var."
set msg "configuration location XDG_DATA_HOME or ~/.local/share (or LOCALAPPDATA on windows) at path '$data_home' does not yet exist"
append msg \n " - please create it and/or set the appropriate env var."
puts stderr $msg
set was_noisy 1
}

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

@ -376,6 +376,8 @@ tcl::namespace::eval punk::ns {
#and a namespace can exist with leading colon - but is even worse - as default Tcl commands will misreport e.g namespace current within namespace eval
#The view is taken that a namespace with leading/trailing colons is so error-prone that even introspection is unreliable so we will rule that out.
#
#nsprefix is *somewhat* like 'namespace parent' execept that it is string based - ie no requirement for the namespaces to actually exist
# - this is an important usecase even if the handling of 'unwise' command names isn't so critical.
proc nsprefix {{nspath ""}} {
#normalize the common case of ::::
set nspath [string map {:::: ::} $nspath]
@ -394,6 +396,8 @@ tcl::namespace::eval punk::ns {
#namespace tail which handles :::cmd ::x:::y ::x:::/y etc in a specific manner for string processing
#review - consider making -strict raise an error for unexpected sequences such as :::: or any situation with more than 2 colons together.
#This is only necessary in the context of requirement to browse namespaces with 'unwisely' named commands
#For most purposes 'namespace tail' is fine.
proc nstail {nspath args} {
#normalize the common case of ::::
set nspath [string map {:::: ::} $nspath]
@ -2018,7 +2022,7 @@ tcl::namespace::eval punk::ns {
}
proc arginfo {args} {
lassign [dict values [punk::args::parse $args withid ::punk::ns::arginfo]] leaders opts values received
set nscaller [uplevel 1 [list ::namespace current]]
#review - setting this afterwards is an architecture smell - we should be able to override the default in the dynamic part
#todo - enable retrieving by id just the record_opts part - so we can treat as a dict directly, as well as easily apply it as a different flag name.
if {![dict exists $received -scheme]} {
@ -2086,10 +2090,12 @@ tcl::namespace::eval punk::ns {
#set numvals [expr {[llength $queryargs]+1}]
##puts "THROWBACK: namespace eval :: [list punk::ns::arginfo {*}[lrange $args 0 end-$numvals] $querycommand {*}$queryargs]"
#return [namespace eval :: [list punk::ns::arginfo {*}[lrange $args 0 end-$numvals] $querycommand {*}$queryargs]]
if {$nscaller ne "::"} {
return [namespace eval :: [list punk::ns::arginfo {*}$opts $querycommand {*}$queryargs]]
}
#set origin $querycommand
#set resolved $querycommand
set origin $querycommand
set resolved $querycommand
}
}

48
src/vfs/_vfscommon.vfs/modules/punk/repl-0.1.1.tm

@ -1722,7 +1722,8 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
# ---
variable reading
variable id_outstack
upvar ::punk::config::running running_config
#upvar ::punk::config::configdata configd
#set running_config [dict get $configd running]
try {
#catch {puts stderr "xx--->[rep $::arglej]"}
@ -2794,21 +2795,28 @@ namespace eval repl {
interp eval code [list apply {docolour {
#adjust channel transform stack
if {!$docolour} {
set s [lindex $::codeinterp::outstack end]
if {$s ne ""} {
shellfilter::stack::remove stdout $s
set stackinfo [dict get [shellfilter::stack item stdout] stack]
set topstack [lindex $stackinfo 0]
if {[string match *::ansiwrap [dict get $topstack -transform]]} {
set sid [dict get $topstack -id]
shellfilter::stack::remove stdout $sid
}
set s [lindex $::codeinterp::errstack end]
if {$s ne ""} {
shellfilter::stack::remove stderr $s
set stackinfo [dict get [shellfilter::stack item stderr] stack]
set topstack [lindex $stackinfo 0]
if {[string match *::ansiwrap [dict get $topstack -transform]]} {
set sid [dict get $topstack -id]
shellfilter::stack::remove stderr $sid
}
} else {
set running_config $::punk::config::running
if {[string length [dict get $running_config color_stdout]]} {
lappend ::codeinterp::outstack [shellfilter::stack::add stdout ansiwrap -settings [list -colour [dict get $running_config color_stdout]]]
set configd $::punk::config::configdata
if {[string length [dict get $configd running color_stdout]]} {
#lappend ::codeinterp::outstack [shellfilter::stack::add stdout ansiwrap -settings [list -colour [dict get $running_config color_stdout]]]
shellfilter::stack::add stdout ansiwrap -settings [list -colour [dict get $configd running color_stdout]]
}
if {[string length [dict get $running_config color_stderr]]} {
lappend ::codeinterp::errstack [shellfilter::stack::add stderr ansiwrap -settings [list -colour [dict get $running_config color_stderr]]]
if {[string length [dict get $configd running color_stderr]]} {
#lappend ::codeinterp::errstack [shellfilter::stack::add stderr ansiwrap -settings [list -colour [dict get $running_config color_stderr]]]
shellfilter::stack::add stderr ansiwrap -settings [list -colour [dict get $configd running color_stderr]]
}
}
@ -3273,12 +3281,12 @@ namespace eval repl {
package require shellfilter ;#requires: shellthread,Thread
apply {running_config {
if {[string length [dict get $running_config color_stderr]] && [punk::console::colour]} {
lappend ::codeinterp::errstack [shellfilter::stack::add stderr ansiwrap -settings [list -colour [dict get $running_config color_stderr]]]
shellfilter::stack::add stderr ansiwrap -settings [list -colour [dict get $running_config color_stderr]]
}
if {[string length [dict get $running_config color_stdout]] && [punk::console::colour]} {
lappend ::codeinterp::outstack [shellfilter::stack::add stdout ansiwrap -settings [list -colour [dict get $running_config color_stdout]]]
shellfilter::stack::add stdout ansiwrap -settings [list -colour [dict get $running_config color_stdout]]
}
}} $::punk::config::running
}} [punk::config::configure running]
} errM]} {
puts stderr "========================"
@ -3352,6 +3360,7 @@ namespace eval repl {
#puts stderr -----
if {[catch {
package require punk::args
package require punk::config
package require punk::ns
#puts stderr "loading natsort"
@ -3360,19 +3369,19 @@ namespace eval repl {
package require natsort
#catch {package require packageTrace}
package require punk
package require punk::args
#package require punk::args
package require punk::args::tclcore
package require shellrun
package require shellfilter
#set running_config $::punk::config::running
apply {running_config {
if {[string length [dict get $running_config color_stderr]] && [punk::console::colour]} {
lappend ::codeinterp::errstack [shellfilter::stack::add stderr ansiwrap -settings [list -colour [dict get $running_config color_stderr]]]
shellfilter::stack::add stderr ansiwrap -settings [list -colour [dict get $running_config color_stderr]]
}
if {[string length [dict get $running_config color_stdout]] && [punk::console::colour]} {
lappend ::codeinterp::outstack [shellfilter::stack::add stdout ansiwrap -settings [list -colour [dict get $running_config color_stdout]]]
shellfilter::stack::add stdout ansiwrap -settings [list -colour [dict get $running_config color_stdout]]
}
}} $::punk::config::running
}} [punk::config::configure running]
package require textblock
} errM]} {
@ -3393,6 +3402,7 @@ namespace eval repl {
code alias quit ::repl::interphelpers::quit
code alias editbuf ::repl::interphelpers::editbuf
code alias colour ::repl::interphelpers::colour
code alias color ::repl::interphelpers::colour
code alias mode ::repl::interphelpers::mode
code alias vt52 ::repl::interphelpers::vt52
#code alias after ::repl::interphelpers::do_after

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

@ -175,13 +175,13 @@ tcl::namespace::eval punk::repl::codethread {
set outstack [list]
set errstack [list]
upvar ::punk::config::running running_config
if {[string length [dict get $running_config color_stdout_repl]] && [interp eval code punk::console::colour]} {
set config_running [::punk::config::configure running]
if {[string length [dict get $config_running color_stdout_repl]] && [interp eval code punk::console::colour]} {
lappend outstack [interp eval code [list ::shellfilter::stack add stdout ansiwrap -settings [list -colour [dict get $running_config color_stdout_repl]]]]
}
lappend outstack [interp eval code [list ::shellfilter::stack add stdout tee_to_var -settings {-varname ::punk::repl::codethread::output_stdout}]]
if {[string length [dict get $running_config color_stderr_repl]] && [interp eval code punk::console::colour]} {
if {[string length [dict get $config_running color_stderr_repl]] && [interp eval code punk::console::colour]} {
lappend errstack [interp eval code [list ::shellfilter::stack add stderr ansiwrap -settings [list -colour [dict get $running_config color_stderr_repl]]]]
# #lappend errstack [shellfilter::stack::add stderr ansiwrap -settings [list -colour cyan]]
}

127
src/vfs/_vfscommon.vfs/modules/shellfilter-0.1.9.tm

@ -674,6 +674,9 @@ namespace eval shellfilter::chan {
#todo - track when in sixel,iterm,kitty graphics data - can be very large
method Trackcodes {chunk} {
#note - caller can use 2 resets in a single unit to temporarily reset to no sgr (override ansiwrap filter)
#e.g [a+ reset reset] (<esc><lb>0;0m vs <esc><lb>0;m)
#puts stdout "===[ansistring VIEW -lf 1 $o_buffered]"
set buf $o_buffered$chunk
set emit ""
@ -686,12 +689,29 @@ namespace eval shellfilter::chan {
#process all pt/code pairs except for trailing pt
foreach {pt code} [lrange $parts 0 end-1] {
#puts "<==[ansistring VIEW -lf 1 $pt]==>"
if {( ![llength $o_codestack] || ([llength $o_codestack] == 1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]))} {
switch -- [llength $o_codestack] {
0 {
append emit $o_do_colour$pt$o_do_normal
}
1 {
if {[punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]} {
append emit $o_do_colour$pt$o_do_normal
#append emit $pt
set o_codestack [list]
} else {
append emit $pt
#append emit [lindex $o_codestack 0]$pt
append emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$pt
}
}
default {
append emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$pt
}
}
#if {( ![llength $o_codestack] || ([llength $o_codestack] == 1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]))} {
# append emit $o_do_colour$pt$o_do_normal
# #append emit $pt
#} else {
# append emit $pt
#}
set c1c2 [tcl::string::range $code 0 1]
set leadernorm [tcl::string::range [tcl::string::map [list\
@ -740,11 +760,28 @@ namespace eval shellfilter::chan {
#puts stdout "=-=[ansistring VIEWCODES $o_buffered]"
} else {
#puts [a+ yellow]???[ansistring VIEW "'$o_buffered'<+>'$trailing_pt'"]???[a]
if {![llength $o_codestack] || ([llength $o_codestack] ==1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]])} {
switch -- [llength $o_codestack] {
0 {
append emit $o_do_colour$trailing_pt$o_do_normal
}
1 {
if {[punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]} {
append emit $o_do_colour$trailing_pt$o_do_normal
set o_codestack [list]
} else {
append emit $trailing_pt
#append emit [lindex $o_codestack 0]$trailing_pt
append emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$trailing_pt
}
}
default {
append emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$trailing_pt
}
}
#if {![llength $o_codestack] || ([llength $o_codestack] ==1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]])} {
# append emit $o_do_colour$trailing_pt$o_do_normal
#} else {
# append emit $trailing_pt
#}
#the previous o_buffered formed the data we emitted - nothing new to buffer because we emitted all parts including the trailing plaintext
set o_buffered ""
}
@ -759,11 +796,14 @@ namespace eval shellfilter::chan {
#puts "-->esc but no detect"
#no complete ansi codes - but at least one esc is present
if {[string last \x1b $buf] == [string length $buf]-1} {
#only esc is last char in buf
if {[string index $buf end] eq "\x1b" && [string first \x1b $buf] == [string length $buf]-1} {
#string index in first part of && clause to avoid some unneeded scans of whole string for this test
#we can't use 'string last' - as we need to know only esc is last char in buf
#puts ">>trailing-esc<<"
set o_buffered \x1b
set emit [string range $buf 0 end-1]
set emit $o_do_colour[string range $buf 0 end-1]$o_do_normal
#set emit [string range $buf 0 end-1]
set buf ""
} else {
set emit_anyway 0
#todo - ensure non-ansi escapes in middle of chunks don't lead to ever growing buffer
@ -774,8 +814,10 @@ namespace eval shellfilter::chan {
if {$st_partial_len < 1001} {
append o_buffered $chunk
set emit ""
set buf ""
} else {
set emit_anyway 1
set o_buffered ""
}
} else {
set possible_code_len [expr {[string length $buf] - [string last \x1b $buf]}] ;#length of possible code
@ -783,6 +825,7 @@ namespace eval shellfilter::chan {
set open_sequence_detected [punk::ansi::ta::detect_open $buf]
if {$possible_code_len > 10 && !$open_sequence_detected} {
set emit_anyway 1
set o_buffered ""
} else {
#could be composite sequence with params - allow some reasonable max sequence length
#todo - configurable max sequence length
@ -790,26 +833,49 @@ namespace eval shellfilter::chan {
# - allow some headroom for redundant codes when the caller didn't merge.
if {$possible_code_len < 101} {
append o_buffered $chunk
set buf ""
set emit ""
} else {
#allow a little more grace if we at least have an opening ansi sequence of any type..
if {$open_sequence_detected && $possible_code_len < 151} {
append o_buffered $chunk
set buf ""
set emit ""
} else {
set emit_anyway 1
set o_buffered ""
}
}
}
}
if {$emit_anyway} {
#assert: any time emit_anyway == 1 buf already contains all of previous o_buffered and o_buffered has been cleared.
#looked ansi-like - but we've given enough length without detecting close..
#treat as possible plain text with some esc or unrecognised ansi sequence
if {( ![llength $o_codestack] || ([llength $o_codestack] == 1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]))} {
switch -- [llength $o_codestack] {
0 {
set emit $o_do_colour$buf$o_do_normal
}
1 {
if {[punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]} {
set emit $o_do_colour$buf$o_do_normal
set o_codestack [list]
} else {
set emit $buf
#set emit [lindex $o_codestack 0]$buf
set emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$buf
}
}
default {
#set emit [punk::ansi::codetype::sgr_merge_singles $o_codestack]$buf
set emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$buf
}
}
#if {( ![llength $o_codestack] || ([llength $o_codestack] == 1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]))} {
# set emit $o_do_colour$buf$o_do_normal
#} else {
# set emit $buf
#}
}
}
}
@ -817,12 +883,24 @@ namespace eval shellfilter::chan {
#no esc
#puts stdout [a+ yellow]...[a]
#test!
if {( ![llength $o_codestack] || ([llength $o_codestack] == 1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]))} {
switch -- [llength $o_codestack] {
0 {
set emit $o_do_colour$buf$o_do_normal
}
1 {
if {[punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]} {
set emit $o_do_colour$buf$o_do_normal
set o_codestack [list]
} else {
set emit $buf
#set emit [lindex $o_codestack 0]$buf
set emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$buf
}
}
default {
#set emit [punk::ansi::codetype::sgr_merge_singles $o_codestack]$buf
set emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$buf
}
}
#set emit $buf
set o_buffered ""
}
return [dict create emit $emit stacksize [llength $o_codestack]]
@ -856,13 +934,22 @@ namespace eval shellfilter::chan {
set instring [tcl::encoding::convertfrom $o_enc $bytes]
set streaminfo [my Trackcodes $instring]
set emit [dict get $streaminfo emit]
if {[dict get $streaminfo stacksize] == 0} {
#no ansi on the stack - we can wrap
#review
set outstring "$o_do_colour$emit$o_do_normal"
} else {
#review - wrapping already done in Trackcodes
#if {[dict get $streaminfo stacksize] == 0} {
# #no ansi on the stack - we can wrap
# #review
# set outstring "$o_do_colour$emit$o_do_normal"
#} else {
#}
#if {[llength $o_codestack]} {
# set outstring [punk::ansi::codetype::sgr_merge_singles $o_codestack]$emit
#} else {
# set outstring $emit
#}
set outstring $emit
}
#puts stdout "decoded >>>[ansistring VIEWCODES $outstring]<<<"
#puts stdout "re-encoded>>>[ansistring VIEW [tcl::encoding::convertto $o_enc $outstring]]<<<"
return [tcl::encoding::convertto $o_enc $outstring]
@ -2260,7 +2347,7 @@ namespace eval shellfilter {
#
if {!$is_script} {
set experiment 0
if $experiment {
if {$experiment} {
try {
set results [exec {*}$commandlist]
set exitinfo [list exitcode 0]

12
src/vfs/_vfscommon.vfs/modules/shellrun-0.1.1.tm

@ -21,12 +21,12 @@ namespace eval shellrun {
#some ugly coupling with punk/punk::config for now
#todo - something better
if {[info exists ::punk::config::running]} {
upvar ::punk::config::running conf
set syslog_stdout [dict get $conf syslog_stdout]
set syslog_stderr [dict get $conf syslog_stderr]
set logfile_stdout [dict get $conf logfile_stdout]
set logfile_stderr [dict get $conf logfile_stderr]
if {[info exists ::punk::config::configdata]} {
set conf_running [punk::config::configure running]
set syslog_stdout [dict get $conf_running syslog_stdout]
set syslog_stderr [dict get $conf_running syslog_stderr]
set logfile_stdout [dict get $conf_running logfile_stdout]
set logfile_stderr [dict get $conf_running logfile_stderr]
} else {
lassign [list "" "" "" ""] syslog_stdout syslog_stderr logfile_stdout logfile_stderr
}

BIN
src/vfs/_vfscommon.vfs/modules/test/tomlish-1.1.5.tm

Binary file not shown.

2
src/vfs/_vfscommon.vfs/modules/textblock-0.1.3.tm

@ -4301,7 +4301,7 @@ tcl::namespace::eval textblock {
if {[dict get $opts -frame]} {
#set output [textblock::frame -ansiborder [a+ {*}$fc Black web-cornflowerblue] -type heavy -title "[a+ {*}$fc Black] Periodic Table " [$t print]]
#set output [textblock::frame -ansiborder [a+ {*}$fc Black term-deepskyblue2] -type heavy -title "[a+ {*}$fc Black] Periodic Table " [$t print]]
set output [textblock::frame -checkargs 0 -ansiborder [a+ {*}$fc Black cyan] -type heavy -title "[a+ {*}$fc Black] Periodic Table " [$t print]]
set output [textblock::frame -checkargs 0 -ansiborder [a+ {*}$fc Black cyan] -type heavy -title "[a+ {*}$fc Black] Periodic Table [a]" [$t print]]
} else {
set output [$t print]
}

1285
src/vfs/_vfscommon.vfs/modules/tomlish-1.1.6.tm

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save