Browse Source

main.tcl dev-os-internal pkIndex and tm package ordering, punk::args tests/fixes, docs and misc

master
Julian Noble 2 months ago
parent
commit
2f787109d8
  1. 293
      src/modules/punk-0.1.tm
  2. 3
      src/modules/punk/ansi/colourmap-999999.0a1.0.tm
  3. 21
      src/modules/punk/args-999999.0a1.0.tm
  4. 4
      src/modules/punk/args/tclcore-999999.0a1.0.tm
  5. 2
      src/modules/punk/args/tkcore-999999.0a1.0.tm
  6. 2
      src/modules/punk/lib-999999.0a1.0.tm
  7. 17
      src/modules/punk/libunknown-0.1.tm
  8. 34
      src/modules/punk/ns-999999.0a1.0.tm
  9. 7
      src/modules/punk/repl-999999.0a1.0.tm
  10. 8
      src/modules/shellrun-0.1.1.tm
  11. 8
      src/modules/test/punk/#modpod-args-999999.0a1.0/args-0.1.5_testsuites/args/opts.test
  12. 578
      src/vfs/_config/punk_main.tcl
  13. 106
      src/vfs/_vfscommon.vfs/lib/app-punk/repl.tcl
  14. 537
      src/vfs/_vfscommon.vfs/modules/punk-0.1.tm
  15. 222
      src/vfs/_vfscommon.vfs/modules/punk/ansi-0.1.1.tm
  16. 3
      src/vfs/_vfscommon.vfs/modules/punk/ansi/colourmap-0.1.0.tm
  17. 3053
      src/vfs/_vfscommon.vfs/modules/punk/args-0.2.tm
  18. 715
      src/vfs/_vfscommon.vfs/modules/punk/args/tclcore-0.1.0.tm
  19. 2
      src/vfs/_vfscommon.vfs/modules/punk/args/tkcore-0.1.0.tm
  20. 4
      src/vfs/_vfscommon.vfs/modules/punk/config-0.1.tm
  21. 176
      src/vfs/_vfscommon.vfs/modules/punk/lib-0.1.2.tm
  22. 121
      src/vfs/_vfscommon.vfs/modules/punk/libunknown-0.1.tm
  23. 79
      src/vfs/_vfscommon.vfs/modules/punk/ns-0.1.0.tm
  24. 53
      src/vfs/_vfscommon.vfs/modules/punk/packagepreference-0.1.0.tm
  25. 3
      src/vfs/_vfscommon.vfs/modules/punk/pipe-1.0.tm
  26. 13
      src/vfs/_vfscommon.vfs/modules/punk/repl-0.1.2.tm
  27. 25
      src/vfs/_vfscommon.vfs/modules/shellfilter-0.2.tm
  28. 8
      src/vfs/_vfscommon.vfs/modules/shellrun-0.1.1.tm
  29. BIN
      src/vfs/_vfscommon.vfs/modules/test/punk/args-0.1.5.tm
  30. 248
      src/vfs/_vfscommon.vfs/modules/textblock-0.1.3.tm

293
src/modules/punk-0.1.tm

@ -576,9 +576,11 @@ namespace eval punk {
@leaders -min 0 -max 0
@opts
-returnlines -type string -typesynopsis matched|all -default matched -choicecolumns 1 -choices {matched all} -choicelabels {
-returnlines -type string -typesynopsis matched|all -default breaksandmatches -choicecolumns 1 -choices {matched all breaksandmatches} -choicelabels {
"matched"\
" Return only lines that matched."
"breaksandmatches"\
" Return configured --break= lines in between non-consecutive matches"
"all"\
" Return all lines.
This has a similar effect to the 'grep' trick of matching on 'pattern|$'
@ -592,18 +594,18 @@ namespace eval punk {
-C|--context= -parsekey "--context" -default 0 -type integer -typesynopsis num -help\
"Print num lines of leading and trailing context surrounding each match."
-A|--after-context= -parsekey "--after-context" -default 0 -type integer -typesynopsis num
--showbreak= -type string -default "-- %c%\U2260" -help\
--break= -type string -default "-- %c%\U2260" -help\
"When returning matched lines and there is a break in consecutive output,
display the break with the given string. %c% is a placeholder for the
number of lines skipped.
Use empty-string for no break display.
grepstr --showbreak= needle $haystacklines
Use empty-string for an empty line as a break display.
grepstr --break= needle $haystacklines
The unix grep utility commonly uses -- for this indicator.
grepstr --showbreak=-- needle $haystacklines
grepstr --break=-- needle $haystacklines
Customisation example:
grepstr -n \"--showbreak=(skipped %c% lines)\" needle $haystacklines
grepstr -n \"--break=(skipped %c% lines)\" needle $haystacklines
"
-ansistrip -type none -help\
"Strip all ansi codes from the input string before processing.
@ -617,7 +619,7 @@ namespace eval punk {
For lines that matched the regex, the line number will be suffixed with a * indicator
with the same highlighting as the matched string(s).
The number of matches in the line immediately follows the *
For lines with no matches the * indicator is present with no highligthing and suffixed
For lines with no matches the * indicator is present with no highlighting and suffixed
with zeros."
-i|--ignore-case -type none -help\
"Perform case insensitive matching."
@ -638,13 +640,13 @@ namespace eval punk {
set data [punk::ansi::ansistrip $data]
}
set highlight [dict get $opts -highlight]
set returnlines [dict get $opts -returnlines]
set opt_returnlines [dict get $opts -returnlines]
set context [dict get $opts --context] ;#int
set beforecontext [dict get $opts --before-context]
set beforecontext [expr {max($beforecontext,$context)}]
set aftercontext [dict get $opts --after-context]
set aftercontext [expr {max($aftercontext,$context)}]
set showbreak [dict get $opts --showbreak]
set break [dict get $opts --break]
set ignorecase [dict exists $received --ignore-case]
if {$ignorecase} {
set nocase "-nocase"
@ -672,9 +674,10 @@ namespace eval punk {
set lines [split $data \n]
set matches [lsearch -all {*}$nocase -regexp $lines $pattern]
set result ""
if {$returnlines eq "all"} {
if {$opt_returnlines eq "all"} {
set returnlines [punk::lib::range 0 [llength $lines]-1]
} else {
#matches|breaksandmatches
set returnlines $matches
}
set max [lindex $returnlines end]
@ -688,7 +691,7 @@ namespace eval punk {
set ln [lindex $lines $lineindex]
set col1 ""
if {$do_linenums} {
set col1 [format %${w1}s [expr {$lineindex+1}]]
set col1 [format "%${w1}s " [expr {$lineindex+1}]]
}
if {$lineindex in $matches} {
set ln [regsub -all {*}$nocase -- $pattern $ln $H&$R] ;#n
@ -710,7 +713,7 @@ namespace eval punk {
#append result "[format %${w1}s [expr {$s+1}]]- " " " $p \n
if {![dict exists $resultlines $s]} {
if {$do_linenums} {
set show "[format %${w1}s [expr {$s+1}]]- $p"
set show "[format "%${w1}s " [expr {$s+1}]]- $p"
} else {
set show $p
}
@ -731,7 +734,7 @@ namespace eval punk {
incr s
if {![dict exists $resultlines $s]} {
if {$do_linenums} {
set show "[format %${w1}s [expr {$s+1}]]- $p"
set show "[format "%${w1}s " [expr {$s+1}]]- $p"
} else {
set show $p
}
@ -746,7 +749,7 @@ namespace eval punk {
set plainlines [split $plain \n]
set lines [split $data \n]
set matches [lsearch -all {*}$nocase -regexp $plainlines $pattern]
if {$returnlines eq "all"} {
if {$opt_returnlines eq "all"} {
set returnlines [punk::lib::range 0 [llength $lines]-1]
} else {
set returnlines $matches
@ -764,7 +767,7 @@ namespace eval punk {
set ln [lindex $lines $lineindex]
set col1 ""
if {$do_linenums} {
set col1 [format %${w1}s [expr {$lineindex+1}]]
set col1 [format "%${w1}s " [expr {$lineindex+1}]]
}
if {$lineindex in $matches} {
set plain_ln [lindex $plainlines $lineindex]
@ -808,7 +811,7 @@ namespace eval punk {
#append result "[format %${w1}s [expr {$s+1}]]- " " " $p \n
if {![dict exists $resultlines $s]} {
if {$do_linenums} {
set show "[format %${w1}s [expr {$s+1}]]- $p"
set show "[format "%${w1}s " [expr {$s+1}]]- $p"
} else {
set show $p
}
@ -824,7 +827,7 @@ namespace eval punk {
incr s
if {![dict exists $resultlines $s]} {
if {$do_linenums} {
set show "[format %${w1}s [expr {$s+1}]]- $p"
set show "[format "%${w1}s " [expr {$s+1}]]- $p"
} else {
set show $p
}
@ -846,18 +849,28 @@ namespace eval punk {
set ordered_resultlines [lsort -integer [dict keys $resultlines]]
set result ""
set i -1
foreach r $ordered_resultlines {
incr i
if {$showbreak ne "" && $r > $i} {
set c [expr {$r - $i}]
append result [string map [list %c% $c] $showbreak] \n
}
append result [dict get $resultlines $r] \n
set i $r
set do_break 0
if {$opt_returnlines eq "breaksandmatches"} {
set do_break 1
}
if {$showbreak ne "" && $i<[llength $lines]-1} {
set c [expr {[llength $lines]-1-$i}]
append result [string map [list %c% $c] $showbreak] \n
if {$do_break} {
foreach r $ordered_resultlines {
incr i
if {$r > $i} {
set c [expr {$r - $i}]
append result [string map [list %c% $c] $break] \n
}
append result [dict get $resultlines $r] \n
set i $r
}
if {$i<[llength $lines]-1} {
set c [expr {[llength $lines]-1-$i}]
append result [string map [list %c% $c] $break] \n
}
} else {
foreach r $ordered_resultlines {
append result [dict get $resultlines $r] \n
}
}
set result [string trimright $result \n]
return $result
@ -7293,74 +7306,210 @@ namespace eval punk {
return $result
}
##dict of lists?
#a
# 1
# 2
#b
# 3
# 4
# ""
# etc
# d
# D
# "ok then"
##dict of dicts
#a
# x
# 1
# y
# 2
#b
# x
# 11
##dict of mixed
#list
# a
# b
# c
#dict
# a
# aa
# b
# bb
#val
# x
#list
# a
# b
# each line has 1 key or value OR part of 1 key or value. ie <=1 key/val per line!
##multiline
#key
# "multi
# line value"
#
#--------------------------------
#a
# 1
# 2
#vs
#a
# 1
# 2
#dict of list-len 2 is equiv to dict of dict with one keyval pair
#--------------------------------
#!!!todo fix - linedict is unfinished and non-functioning
#linedict based on indents
proc linedict {args} {
puts stderr "linedict is experimental and incomplete"
set data [lindex $args 0]
set opts [lrange $args 1 end] ;#todo
set nlsplit [split $data \n]
set rootindent -1
set stepindent -1
#set wordlike_parts [regexp -inline -all {\S+} $lastitem]
set d [dict create]
set keys [list]
set i 1
set firstkeyline "N/A"
set firststepline "N/A"
#first do a partial loop through lines and work out the rootindent and stepindent.
#we could do this in the main loop - but we do it here to remove a small bit of logic from the main loop.
#review - if we ever move to streaming a linedict - we'll need to re-arrange to validating indents as we go anyway.
set linenum 0
set firstkey_line "N/A"
set firstkey_linenum -1
set firststep_line "N/A"
set firststep_linenum -1
set indents_seen [dict create]
foreach ln $nlsplit {
incr linenum
if {![string length [string trim $ln]]} {
incr i
continue
}
set is_rootkey 0
#todo - use info complete to accept keys/values with newlines
regexp {(\s*)(.*)} $ln _ space linedata
puts stderr ">>line:'$ln' [string length $space] $linedata"
set this_indent [string length $space]
if {$rootindent < 0} {
set firstkeyline $ln
set rootindent $this_indent
if {[catch {lindex $linedata 0}]} {
error "bad line value '$linedata' - each line must be a valid Tcl list of length 1. Use quotes or curly braces as necessary"
}
if {$this_indent == $rootindent} {
set is_rootkey 1
if {[llength $linedata] > 1} {
error "bad line value '$linedata' - each line must be a valid Tcl list of length 1. Use quotes or curly braces as necessary"
}
if {$this_indent < $rootindent} {
error "bad root indentation ($this_indent) at line: $i smallest indent was set by first key line: $firstkeyline"
#puts stderr "--linenum:[format %-3s $linenum] line:[format "%-20s" $ln] [format %-4s [string length $space]] $linedata"
set this_indent [string length $space]
if {[dict exists $indents_seen $this_indent]} {
continue
}
if {$is_rootkey} {
dict set d $linedata {}
lappend keys $linedata
if {$rootindent < 0} {
set firstkey_line $ln
set firstkey_linenum $linenum
set rootindent $this_indent
dict set indents_seen $this_indent 1
} elseif {$stepindent < 0} {
if {$this_indent > $rootindent} {
set firststep_line $ln
set firststep_linenum $linenum
set stepindent [expr {$this_indent - $rootindent}]
dict set indents_seen $this_indent 1
} elseif {$this_indent < $rootindent} {
error "bad root indentation ($this_indent) at line: $linenum. Smallest indent was set on linenumber: $firstkey_linenum by first key line: $firstkey_line"
}
#if equal - it's just another root key
} else {
if {$stepindent < 0} {
set stepindent $this_indent
set firststepline $ln
#validate all others
if {$this_indent < $rootindent} {
error "bad root indentation ($this_indent) at line: $linenum. Smallest indent was set on linenumber: $firstkey_linenum by first key line: $firstkey_line"
}
if {$this_indent == $stepindent} {
dict set d [lindex $keys end] $ln
if {($this_indent - $rootindent) % $stepindent != 0} {
error "bad indentation ($this_indent) at linenum: $linenum line:'$ln'. this_indent - rootindent ($this_indent - $rootindent == [expr {$this_indent - $rootindent}]) is not a multiple of the first key indent $stepindent seen on linenumber: $firststep_linenum value:'$firststep_line'"
} else {
if {($this_indent % $stepindent) != 0} {
error "bad indentation ($this_indent) at line: $i not a multiple of the first key indent $step_indent seen on $firststepline"
}
dict set indents_seen $this_indent 1
}
}
}
#todo fix!
#set wordlike_parts [regexp -inline -all {\S+} $lastitem]
set d [dict create]
set keys [list]
set linenum 0 ;#line-numbers 1 based
foreach ln $nlsplit {
incr linenum
if {![string length [string trim $ln]]} {
incr linenum
continue
}
regexp {(\s*)(.*)} $ln _ space linedata
puts stderr ">>linenum:[format %-3s $linenum] line:[format "%-20s " $ln] [format %-4s [string length $space]] $linedata"
set linedata [lindex $linedata 0]
set this_indent [string length $space]
if {$this_indent == $rootindent} {
#is rootkey
dict set d $linedata {}
set keys [list $linedata]
} else {
set ispan [expr {$this_indent - $rootindent}]
set numsteps [expr {$ispan / $stepindent}]
#assert - since validated in initial loop - numsteps is always >= 1
set keydepth [llength $keys]
if {$numsteps > $keydepth + 1} {
#too deep - not tested for in initial loop. ? todo - convert to leading spaces in key/val?
error "bad indentation ($this_indent) at linenum: $linenum line:'$ln'. (too deep) - review"
}
if {$numsteps > ($keydepth - 1)} {
#assert - from above test - must be 1 or 2 deeper
set parentkey [lindex $keys end]
lappend keys [list $parentkey $ln]
set oldval [dict get $d $parentkey]
if {[string length $oldval]} {
set new [dict create $oldval $ln]
set oldval [dict get $d {*}$parentkey]
if {$numsteps - ($keydepth -1) == 1} {
#1 deeper
if {$oldval ne {}} {
lappend keys [list {*}$parentkey $linedata]
dict unset d {*}$parentkey
#dict set d {*}$parentkey $oldval $linedata
dict set d {*}$parentkey $oldval {} ;#convert to key?
dict set d {*}$parentkey $linedata {}
} else {
dict set d {*}$parentkey $linedata
}
} else {
dict set d $parentkey $ln
}
#2 deeper - only ok if there is an existing val
if {$oldval eq {}} {
error "bad indentation ($this_indent) at linenum: $linenum line:'$ln'. (too deep) - review"
}
puts ">>> 2deep d:'$d' oldval:$oldval linedata:$linedata parentkey:$parentkey"
dict unset d {*}$parentkey
dict set d {*}$parentkey $oldval $linedata
lappend keys [list {*}$parentkey $oldval]
}
} elseif {$numsteps < ($keydepth - 1)} {
set diff [expr {$keydepth - 1 - $numsteps}]
set keys [lrange $keys 0 end-$diff]
#now treat as same level
set parentkey [lindex $keys end-1]
lset keys end end $linedata
dict set d {*}$parentkey $linedata {}
} else {
#same level
set parentkey [lindex $keys end-1]
lset keys end end $linedata
dict set d {*}$parentkey $linedata {}
}
}
incr i
#puts ">>keys:$keys"
}
return $d
}
proc dictline {d} {
proc dictline {d {indent 2}} {
puts stderr "unimplemented"
set lines [list]
@ -7603,25 +7752,29 @@ namespace eval punk {
1 {
#val may have ansi - including resets. Pass through ansibase_lines to
if {$ansibase ne ""} {
set displayval [::textblock::ansibase_lines $displayval $ansibase]
#set displayval [::textblock::ansibase_lines $displayval $ansibase]
set displayval [punk::ansi::ansiwrap_raw $ansibase "" "" $displayval]
}
}
2 {
set displayval $ansibase[ansistring VIEW $displayval]
if {$ansibase ne ""} {
set displayval [::textblock::ansibase_lines $displayval $ansibase]
#set displayval [::textblock::ansibase_lines $displayval $ansibase]
set displayval [punk::ansi::ansiwrap_raw $ansibase "" "" $displayval]
}
}
3 {
set displayval $ansibase[ansistring VIEWCODE $displayval]
if {$ansibase ne ""} {
set displayval [::textblock::ansibase_lines $displayval $ansibase]
#set displayval [::textblock::ansibase_lines $displayval $ansibase]
set displayval [punk::ansi::ansiwrap_raw $ansibase "" "" $displayval]
}
}
4 {
set displayval $ansibase[ansistring VIEWSTYLE $displayval]
if {$ansibase ne ""} {
set displayval [::textblock::ansibase_lines $displayval $ansibase]
#set displayval [::textblock::ansibase_lines $displayval $ansibase]
set displayval [punk::ansi::ansiwrap_raw $ansibase "" "" $displayval]
}
}
}

3
src/modules/punk/ansi/colourmap-999999.0a1.0.tm

@ -49,9 +49,6 @@ package require Tcl 8.6-
#*** !doctools
#[item] [package {Tcl 8.6}]
# #package require frobz
# #*** !doctools
# #[item] [package {frobz}]
#*** !doctools
#[list_end]

21
src/modules/punk/args-999999.0a1.0.tm

@ -3142,10 +3142,10 @@ tcl::namespace::eval punk::args {
variable arg_error_CLR_error
array set arg_error_CLR_error {}
proc _argerror_load_colours {} {
proc _argerror_load_colours {{forcereload 0}} {
variable arg_error_CLR
#todo - option for reload/retry?
if {[array size arg_error_CLR] > 0} {
if {!$forcereload && [array size arg_error_CLR] > 0} {
return
}
@ -3159,6 +3159,7 @@ tcl::namespace::eval punk::args {
}
}
#array set arg_error_CLR {}
set arg_error_CLR(testsinglecolour) [a+ yellow] ;#A single SGR colour to test current colour on|off state (empty string vs some result - used to determine if forcereload required)
set arg_error_CLR(errormsg) [a+ brightred]
set arg_error_CLR(title) ""
set arg_error_CLR(check) [a+ brightgreen]
@ -3247,7 +3248,18 @@ tcl::namespace::eval punk::args {
error "arg_error already running - error in arg_error?\n triggering errmsg: $msg"
}
_argerror_load_colours
#set arg_error_CLR(testsinglecolour) [a+ brightred]
upvar ::punk::args::arg_error_CLR CLR
set forcereload 0 ;#no need for forcereload to be true for initial run - empty array will trigger initial load
if {[info exists CLR(testsinglecolour)]} {
set terminal_colour_is_on [expr {[string length [a+ yellow]]}]
set error_colour_is_on [expr {[string length $CLR(testsinglecolour)]}]
if {$terminal_colour_is_on ^ $error_colour_is_on} {
#results differ
set forcereload 1
}
}
_argerror_load_colours $forcereload
if {[llength $args] %2 != 0} {
set arg_error_isrunning 0
@ -3348,12 +3360,11 @@ tcl::namespace::eval punk::args {
#hack some basics for now.
#for coloured schemes - use bold as well as brightcolour in case colour off.
upvar ::punk::args::arg_error_CLR CLR
switch -- $scheme {
nocolour {
variable arg_error_CLR_nocolour
array set CLR [array get arg_error_CLR_nocolour
array set CLR [array get arg_error_CLR_nocolour]
}
info {
variable arg_error_CLR_info

4
src/modules/punk/args/tclcore-999999.0a1.0.tm

@ -5642,8 +5642,8 @@ tcl::namespace::eval punk::args::tclcore {
then user input will be processed during the next call to update."
@leaders -min 0 -max 0
@opts
-idletasks -type none
@values -min 0 -max 0
@values -min 0 -max 1
idletasks -type literalprefix(idletasks) -optional 1
} "@doc -name Manpage: -url [manpage_tcl update]"\
{@examples -help {
Run computations for about a second and then finish:

2
src/modules/punk/args/tkcore-999999.0a1.0.tm

@ -400,7 +400,7 @@ tcl::namespace::eval punk::args::tkcore {
-disabledforeground\
-font\
-foreground|-fg\
-highligthbackground\
-highlightbackground\
-highlightcolor\
-highlightthickness\
-image\

2
src/modules/punk/lib-999999.0a1.0.tm

@ -1147,7 +1147,7 @@ namespace eval punk::lib {
- then the normal = separator will be replaced with a coloured (or underlined if colour off) 'mismatch' indicator.
e.g4 set list {{k1 v1 k2 v2} {k1 vv1 k2 vv2}}; pdict list @0-end/@@k2 @*/@@k1
Here we supply 2 separate pattern hierarchies, where @0-end and @* are list operations and are equivalent
The second level segement in each pattern switches to a dict operation to retrieve the value by key.
The second level segment in each pattern switches to a dict operation to retrieve the value by key.
When a list operation such as @* is used - integer list indexes are displayed on the left side of the = for that hierarchy level.
}
}]

17
src/modules/punk/libunknown-0.1.tm

@ -829,13 +829,23 @@ tcl::namespace::eval punk::libunknown {
}
}
}
#refresh ifneeded scripts for just_added/just_changed
#review: searchpaths are in auto_path order - earliest has precedence for any particular pkg-version
#REVIEW: what is to stop an auto_path package e.g from os, overriding a .tm ifneeded script from an item earlier in the package_mode list configured in punk's main.tcl?
#e.g when package_mode is {dev-os} we don't want a pkgIndex package from ::env(TCLLIBPATH) overriding a .tm from the dev paths (even if version nums the same)
#conversely we do want a dev path pkIndex package overriding an existing ifneeded script from a .tm in os
#to accomodate this - we may need to maintain a subdict in epoch of paths/path-prefixes to package_mode members os, dev, internal
#this 'refresh' is really a 'reversion' to what was already stored in epoch pkg epochs <currente> added
#
set e [dict get $epoch pkg current]
set pkgvdone [dict create]
set dict_added [dict get $epoch pkg epochs $e added]
#keys are in reverse order due to tclPkgUnknown processing order
set ordered_searchpaths [lreverse [dict keys $dict_added]];# orderd as in auto_path
set ordered_searchpaths [lreverse [dict keys $dict_added]];# ordered as in auto_path
dict for {pkg versiond} $refresh_dict {
set versions [dict keys $versiond]
@ -847,8 +857,11 @@ tcl::namespace::eval punk::libunknown {
if {[dict exists $addedinfo $pkg $v]} {
ledit versions $vidx $vidx
set iscript [dict get $addedinfo $pkg $v scr]
#todo - find the iscript in the '$epoch pkg epochs <e> added paths' lists and determine os vs dev vs internal
#(scanning for path directly in the ifneeded script for pkgs is potentially error prone)
#for .tm ifneeded scripts - the syntax is simple enough to determine directly (and ifneeded scr not stored for those anyway)
if {[package ifneeded $pkg $v] ne $iscript} {
#puts "---->refreshing $pkg $v using path:$searchpath"
#puts "---->refreshing $pkg $v - reverting to already stored from path:$searchpath"
package ifneeded $pkg $v $iscript
#dict set pkgvdone $pkg $v 1
}

34
src/modules/punk/ns-999999.0a1.0.tm

@ -765,6 +765,13 @@ tcl::namespace::eval punk::ns {
}
#REVIEW - ansi codes can be *very* confusing to the user when trying to handle lists etc..
punk::args::define {
@id -id ::punk::ns::get_nslist
@cmd -name punk::ns::get_nslist
@opts
-match -default ""
-nsdict -type dict -default {}
}
proc get_nslist {args} {
set known_types [list children commands exported imported aliases procs ensembles ooclasses ooobjects ooprivateobjects ooprivateclasses native coroutines interps zlibstreams]
set defaults [dict create\
@ -775,6 +782,9 @@ tcl::namespace::eval punk::ns {
set opts [dict merge $defaults $args]
# -- --- --- --- --- --- --- --- --- ---
set fq_glob [dict get $opts -match]
if {$fq_glob eq ""} {
set fq_glob [uplevel 1 nsthis]::*
}
set requested_types [dict get $opts -types]
set opt_nsdict [dict get $opts -nsdict]
@ -835,7 +845,7 @@ tcl::namespace::eval punk::ns {
set zlibstreams [list]
set usageinfo [list]
if {$opt_nsdict eq ""} {
if {![dict size $opt_nsdict]} {
set nsmatches [get_ns_dicts $fq_glob -allbelow 0]
set itemcount 0
set matches_with_results [list]
@ -867,6 +877,8 @@ tcl::namespace::eval punk::ns {
}
if {"commands" in $types} {
set commands [dict get $contents commands]
}
set usageinfo [dict get $contents usageinfo]
foreach t $types {
switch -- $t {
exported {
@ -910,8 +922,6 @@ tcl::namespace::eval punk::ns {
}
}
}
set usageinfo [dict get $contents usageinfo]
}
set numchildren [llength $children]
if {$numchildren} {
@ -1148,7 +1158,11 @@ tcl::namespace::eval punk::ns {
the child namespaces and commands within
the namespace(s) matched by glob."
@opts
-nspathcommands -type boolean -default 0
-nspathcommands -type boolean -default 0 -help\
"When a namespace has entries configured in 'namespace path', the default result for nslist
will display just a basic note: 'Also resolving cmds in namespace paths: <namespaces>'.
If -nspathcommands is true, it will also display subtables showing the commands resolvable
via any such listed namespaces."
-types
@values -min 0 -max -1
glob -multiple 1 -optional 1 -default "*"
@ -1207,9 +1221,9 @@ tcl::namespace::eval punk::ns {
if {[dict size [dict get $nsdict namespacepath]]} {
set path_text ""
if {!$opt_nspathcommands} {
append path_text \n " also resolving cmds in namespace paths: [dict keys [dict get $nsdict namespacepath]]"
append path_text \n " Also resolving cmds in namespace paths: [dict keys [dict get $nsdict namespacepath]]"
} else {
append path_text \n " also resolving cmds in namespace paths:"
append path_text \n " Also resolving cmds in namespace paths:"
set nspathdict [dict get $nsdict namespacepath]
if {!$has_textblock} {
dict for {k v} $nspathdict {
@ -1218,8 +1232,14 @@ tcl::namespace::eval punk::ns {
append path_text \n " cmds: $cmds"
}
} else {
#todo - change to display in column order to be same as main command listing
dict for {k v} $nspathdict {
set t [textblock::list_as_table -title $k -columns 6 [lsort [dict get $v commands]]]
set pathcommands [dict get $v commands]
set columns 6
if {[llength $pathcommands] < 6} {
set columns [llength $v]
}
set t [textblock::list_as_table -title $k -columns $columns [lsort $pathcommands]]
append path_text \n $t
}
}

7
src/modules/punk/repl-999999.0a1.0.tm

@ -2829,14 +2829,15 @@ namespace eval repl {
package require punk::packagepreference
punk::packagepreference::install
#jjj
package require punk::ansi::colourmap
package require punk::args
#package require Thread
if {[catch {package require thread} errM]} {
puts stdout "initscript lib load fail on package require thread\n$errM"
puts stdout ">>auto_path : $::auto_path"
puts stdout ">>tcl::tm::list: [tcl::tm::list]"
puts stdout ">>repl::init initscript lib load fail on package require thread\n$errM"
puts stdout ">>repl::init auto_path : $::auto_path"
puts stdout ">>repl::init tcl::tm::list: [tcl::tm::list]"
}
#-----

8
src/modules/shellrun-0.1.1.tm

@ -367,7 +367,8 @@ namespace eval shellrun {
lappend chunklist [list "stderr" [dict get $exitinfo errorInfo]]
} else {
set c [a+ Yellow red bold]
lappend chunklist [list "info" "$c$exitinfo$n"]
#lappend chunklist [list "info" "$c$exitinfo$n"]
lappend chunklist [list "info" [punk::ansi::ansiwrap_raw $c \x1b\[m "" $exitinfo]]
}
@ -486,7 +487,7 @@ namespace eval shellrun {
#we raise an error because an error during calling is different to collecting stderr from a command, and the caller should be able to wrap in a catch
# to determine something other than just a nonzero exit code or output on stderr.
if {[dict exists $exitinfo error]} {
if {"-tcl" in $runopts} {
if {[dict exists $received "-tcl"]} {
} else {
#todo - check errorInfo makes sense.. return -code? tailcall?
@ -513,7 +514,8 @@ namespace eval shellrun {
lappend chunklist [list "info" "errorInfo [list [dict get $exitinfo errorInfo]]"]
} else {
set c [a+ Yellow red bold]
lappend chunklist [list "info" "$c$exitinfo$n"]
#lappend chunklist [list "info" "$c$exitinfo$n"]
lappend chunklist [list "info" [punk::ansi::ansiwrap_raw $c "\x1b\[m" "" $exitinfo]]
}

8
src/modules/test/punk/#modpod-args-999999.0a1.0/args-0.1.5_testsuites/args/opts.test

@ -55,14 +55,14 @@ namespace eval ::testspace {
-setup $common -body {
#prefixed choice with and without prefixed flagname
set argd [punk::args::parse {--filename=x} withdef @opts {--filename= -default spud -type string -choices {abc xyz}}]
lappend opts [dict get $argd opts]
lappend result [dict get $argd opts]
set argd [punk::args::parse {--file=x} withdef @opts {--filename= -default spud -type string -choices {abc xyz}}]
lappend opts [dict get $argd opts]
lappend result [dict get $argd opts]
#unprefixed choice with and without prefixed flagname
set argd [punk::args::parse {--filename=xyz} withdef @opts {--filename= -default spud -type string -choices {abc xyz}}]
lappend opts [dict get $argd opts]
lappend result [dict get $argd opts]
set argd [punk::args::parse {--file=xyz} withdef @opts {--filename= -default spud -type string -choices {abc xyz}}]
lappend opts [dict get $argd opts]
lappend result [dict get $argd opts]
}\
-cleanup {

578
src/vfs/_config/punk_main.tcl

@ -106,36 +106,33 @@ apply { args {
}
# -- --- ---
#maintain a separate auto_path_additions
# - we add to both tail and head of this depending on internal/external - and then append to existing auto_path
#ie existing auto_path from env etc has precedence over what we add.
set auto_path_additions [list]
set tm_additions_internal [list]
set tm_additions_dev [list]
set auto_path_additions_internal [list]
set auto_path_additions_dev [list]
set lc_auto_path [string tolower $::auto_path]
#inital auto_path setup by init.tcl
#firstly it includes env(TCLLIBPATH)
#then it adds the tcl_library folder and its parent
#e.g //zipfs:/app/tcl_library and //zipfs:/app
#all our auto_path_additions will come after these - and thus are lower priority.
#when 'dev' is not supplied - any non internal paths (usually those from env(TCLLIBPATH) will be stripped
#when 'dev' or 'os' is not supplied - any non internal paths (usually those from env(TCLLIBPATH) will be stripped
#so that everything is self-contained in the kit/zipkit
#when 'dev' is supplied - the executable or script relative paths will be placed before other internal paths - except for those that init.tcl set up
#todo - place externals from TCLLIBPATH at end so lower priority than dev paths.
#puts "\x1b\[1\;33m main.tcl original auto_path: $::auto_path"
if {[info exists ::tcl::kitpath] && $::tcl::kitpath ne ""} {
set kp $::tcl::kitpath
set existing_module_paths [string tolower [tcl::tm::list]]
#set existing_module_paths [string tolower [tcl::tm::list]]
foreach p [list modules modules_tcl$tclmajorv] {
if {[string tolower [file join $kp $p]] ni $existing_module_paths} {
tcl::tm::add [file join $kp $p]
}
#if {[string tolower [file join $kp $p]] ni $existing_module_paths} {
# tcl::tm::add [file join $kp $p]
#}
lappend tm_additions_internal [file join $kp $p]
}
foreach l [list lib lib_tcl$tclmajorv] {
if {[string tolower [file join $kp $l]] ni [list {*}$lc_auto_path {*}$auto_path_additions]} {
lappend auto_path_additions [file join $kp $l]
}
foreach p [list lib lib_tcl$tclmajorv] {
lappend auto_path_additions_internal [file join $kp $p]
}
}
if {$has_zipfs_attached} {
@ -143,30 +140,28 @@ apply { args {
#default 'zipfs root' has trailing slash (//zipfs:/) - but file join does the right thing
set zipbase [file join [tcl::zipfs::root] app]
if {"$zipbase" in [tcl::zipfs::mount]} {
set existing_module_paths [string tolower [tcl::tm::list]]
#set existing_module_paths [string tolower [tcl::tm::list]]
foreach p [list modules modules_tcl$tclmajorv] {
if {[string tolower [file join $zipbase $p]] ni $existing_module_paths} {
tcl::tm::add [file join $zipbase $p]
}
#if {[string tolower [file join $zipbase $p]] ni $existing_module_paths} {
# tcl::tm::add [file join $zipbase $p]
#}
lappend tm_additions_internal [file join $zipbase $p]
}
foreach l [list lib lib_tcl$tclmajorv] {
if {[string tolower [file join $zipbase $l]] ni [list {*}$lc_auto_path {*}$auto_path_additions]} {
lappend auto_path_additions [file join $zipbase $l]
}
foreach p [list lib lib_tcl$tclmajorv] {
lappend auto_path_additions_internal [file join $zipbase $p]
}
}
}
if {$has_cookfs_attached} {
set existing_module_paths [string tolower [tcl::tm::list]]
#set existing_module_paths [string tolower [tcl::tm::list]]
foreach p [list modules modules_tcl$tclmajorv] {
if {[string tolower [file join $cookbase $p]] ni $existing_module_paths} {
tcl::tm::add [file join $cookbase $p]
}
#if {[string tolower [file join $cookbase $p]] ni $existing_module_paths} {
# tcl::tm::add [file join $cookbase $p]
#}
lappend tm_additions_internal [file join $cookbase $p]
}
foreach l [list lib lib_tcl$tclmajorv] {
if {[string tolower [file join $cookbase $l]] ni [list $lc_auto_path {*}$auto_path_additions]} {
lappend auto_path_additions [file join $cookbase $l]
}
foreach p [list lib lib_tcl$tclmajorv] {
lappend auto_path_additions_internal [file join $cookbase $p]
}
}
@ -183,34 +178,85 @@ apply { args {
if {$has_cookfs} {
lappend internal_paths $cookbase
}
#REVIEW
if {[info exists ::punkboot::internal_paths] && [llength $::punkboot::internal_paths]} {
#somewhat ugly cooperation with external sourcing scripts
lappend internal_paths {*}$::punkboot::internal_paths
}
if {[lindex $args 0] in {dev devquiet}} {
set arglist [lassign $args devmode]
set ::argv $arglist
set ::argc [llength $arglist]
if {$devmode ne "devquiet"} {
puts stderr "DEV MODE - preferencing external libraries and modules"
# -----------------------------------------------------------------------------------------------------------
# dev - refers to module and library paths relative to the project (executable path)
# os - refers to modules and library paths gleaned from ::env (TCLLIBPATH and TCL<MAJOR>_<MINOR>_TM_PATH)
# internal - refers to modules and libraries supplied from the mounted filesystem of a kit or zipfs based executable
# -----------------------------------------------------------------------------------------------------------
# Note that unlike standard 'package unknown' punk::libunknown does not stop searching for packages when a .tm file is found that matches requirements,
# The auto_path is still examined. (avoids quirks where higher versioned pkgIndex based package not always found)
# -----------------------------------------------------------------------------------------------------------
set all_package_modes [list dev os internal]
#package_mode is specified as a dash-delimited ordered value e.g dev-os
#"internal" is the default and if not present is always added to the list
#i.e "dev-os" is equivalent to "dev-os-internal"
#"os" is equivalent to "os-internal"
#"internal-os" and "internal" are left as is.
#The effective package_mode has 1 2 or 3 members.
# The only case where it has 1 member is if just "internal" is specified.
#This gives the number of permutations as how many ways to choose 3 items plus how many ways to choose 2 of the 3 items (one must be 'internal') plus the sole allowable way to choose 1
#for a total of 11 possible final orderings.
#(16 possible values for package_mode argument when you include the short-forms "",os,dev,os-dev,dev-os which always have 'internal' appended)
set test_package_mode [lindex $args 0]
switch -exact -- $test_package_mode {
internal -
os-internal - dev-internal - internal-os - internal-dev -
os-dev-internal - os-internal-dev - dev-os-internal - dev-internal-os - internal-os-dev - internal-dev-os {
#fully specified ('internal' is present)
set package_modes [split $test_package_mode -]
set arglist [lrange $args 1 end]
}
os - dev - os-dev - dev-os {
#partially specified - 'internal' ommitted but implied at tail
set package_modes [list {*}[split $test_package_mode -] internal]
set arglist [lrange $args 1 end]
}
default {
#empty first arg - or some unrelated arg
set package_modes internal
if {$test_package_mode eq ""} {
#consume the empty first arg as an equivalent of 'internal'
#don't consume any first arg that isn't recognised as a package_mode
set arglist [lrange $args 1 end]
} else {
set arglist $args
}
}
#Note regarding the use of package forget and binary packages
#If the package has loaded a binary component - then a package forget and a subsequent package require can result in both binaries being present, as seen in 'info loaded' result - potentially resulting in anomalous behaviour
#In general package forget after a package has already been required may need special handling and should be avoided where possible.
#Only a limited set of package support unloading a binary component
#We limit the use of 'package forget' here to packages that have not been loaded (whether pure-tcl or not)
#ie in this context it is used only for manipulating preferences of which packages are loaded in the first place
#Unintuitive preferencing can occur if the same package version is for example present in a tclkit and in a module or lib folder external to the kit.
#It may be desired for performance or testing reasons to preference the library outside of the kit - and raising the version number may not always be possible/practical.
#If the executable is a kit - we don't know what packages it contains or whether it allows loading from env based external paths.
#For app-punk projects - the lib/module paths based on the project being run should take preference, even if the version number is the same.
#(these are the 'info nameofexecutable' or 'info script' or 'pwd' relative paths that are added here)
#Some kits will remove lib/module paths (from auto_path & tcl::tm::list) that have been added via TCLLIBPATH / TCLX_Y_TM_PATH environment variables
#Some kits will remove those env-provided lib paths but fail to remove the env-provided module paths
#(differences in boot.tcl in the kits)
}
#assert: arglist has had any first arg that is a package_mode (including empty string) stripped.
set ::argv $arglist
set ::argc [llength $arglist]
#assert: package_modes is now a list of at least length 1 (in which case the only possible value is: internal)
#Note regarding the use of package forget and binary packages
#If the package has loaded a binary component - then a package forget and a subsequent package require can result in both binaries being present, as seen in 'info loaded' result - potentially resulting in anomalous behaviour
#In general package forget after a package has already been required may need special handling and should be avoided where possible.
#Only a limited set of packages support unloading a binary component anyway.
#We limit the use of 'package forget' here to packages that have not been loaded (whether pure-tcl or not)
#ie in this context it is used only for manipulating preferences of which packages are loaded in the first place
#Unintuitive preferencing can occur if the same package version is for example present in a tclkit and in a module or lib folder external to the kit.
#It may be desired for performance or testing reasons to preference the library outside of the kit - and raising the version number may not always be possible/practical.
#If the executable is a kit - we don't know what packages it contains or whether it allows loading from env based external paths.
#For app-punk projects - the lib/module paths based on the project being run should take preference if 'dev' is earlier in the list, even if the version number is the same.
#(these are the 'info nameofexecutable' or 'info script' or 'pwd' relative paths that are added here)
#Some kits will remove lib/module paths (from auto_path & tcl::tm::list) that have been added via TCLLIBPATH / TCLX_Y_TM_PATH environment variables
#Some kits will remove those env-provided lib paths but fail to remove the env-provided module paths
#(differences in boot.tcl in the kits)
if {[llength $package_modes] > 1} {
puts stderr "main.tcl PACKAGE MODE is preferencing libraries and modules in the order: $package_modes"
puts stderr "main.tcl original auto_path: $::auto_path"
#------------------------------------------------------------------------------
#Module loading
@ -219,9 +265,9 @@ apply { args {
# - but first add our other known relative modules paths - as it won't make sense to use current directory as a modulepath if it's an ancestor of one of these..
#original tm list at this point consists of whatever the kit decided + some prepended internal kit paths that punk decided on.
#we want to bring the existing external paths to the front (probably from the kit looking at various env TCL* values)
#we want to bring the existing external paths to the position specified by package_mode (probably from the kit looking at various env TCL* values)
#we want to maintain the order of the internal paths.
#we then want to add our external dev paths of the total list
#we want to add our external dev paths to the position specified by package_mode
#assert [llength [package names]] should be small at this point ~ <10 ?
@ -266,54 +312,160 @@ apply { args {
#case differences could represent different paths on unix-like platforms.
#It's perhaps a little unwise to configure matching paths with only case differences for a cross-platform tool .. but we should support it for those who use it and have no interest in windows - todo! review
set normexe_dir [file dirname $normexe]
if {[file tail $normexe_dir] eq "bin"} {
#underlying exe in a bin dir - backtrack 1
lappend exe_module_folders [file dirname $normexe_dir]/modules
lappend exe_module_folders [file dirname $normexe_dir]/modules_tcl$tclmajorv
} else {
lappend exe_module_folders $normexe_dir/modules
lappend exe_module_folders $normexe_dir/modules_tcl$tclmajorv
if {"dev" in $package_modes} {
set normexe_dir [file dirname $normexe]
if {[file tail $normexe_dir] eq "bin"} {
#underlying exe in a bin dir - backtrack 1
lappend exe_module_folders [file dirname $normexe_dir]/modules
lappend exe_module_folders [file dirname $normexe_dir]/modules_tcl$tclmajorv
} else {
lappend exe_module_folders $normexe_dir/modules
lappend exe_module_folders $normexe_dir/modules_tcl$tclmajorv
}
set nameexe_dir [file dirname [info nameofexecutable]]
#possible symlink (may resolve to same path as above - we check below to not add in twice)
if {[file tail $nameexe_dir] eq "bin"} {
lappend exe_module_folders [file dirname $nameexe_dir]/modules
lappend exe_module_folders [file dirname $nameexe_dir]/modules_tcl$tclmajorv
} else {
lappend exe_module_folders $nameexe_dir/modules
lappend exe_module_folders $nameexe_dir/modules_tcl$tclmajorv
}
#foreach modulefolder $exe_module_folders {
# set lc_external_tm_dirs [string tolower $external_tm_dirs]
# set lc_modulefolder [string tolower $modulefolder]
# if {$lc_modulefolder in [string tolower $original_external_tm_dirs]} {
# #perhaps we have an env var set pointing to one of our dev foldersl. We don't want to rely on how the kit ordered it.
# #bring to front if not already there.
# #assert it must be present in $lc_external_tm_dirs if it's in $original_external_tm_dirs
# set posn [lsearch $lc_external_tm_dirs $lc_modulefolder]
# if {$posn > 0} {
# #don't rely on lremove here. Not all runtimes have it and we don't want to load our forward-compatibility packages yet.
# #(still need to support tcl 8.6 - and this script used in multiple kits)
# set external_tm_dirs [lreplace $external_tm_dirs $posn $posn]
# #don't even add it back in if it doesn't exist in filesystem
# if {[file isdirectory $modulefolder]} {
# set external_tm_dirs [linsert $external_tm_dirs 0 $modulefolder]
# }
# }
# } else {
# if {$lc_modulefolder ni $lc_external_tm_dirs && [file isdirectory $modulefolder]} {
# set external_tm_dirs [linsert $external_tm_dirs 0 $modulefolder] ;#linsert seems faster than 'concat [list $modulefolder] $external_tm_dirs' - review
# }
# }
#}
if {![llength $exe_module_folders]} {
puts stderr "Warning - no 'modules' or 'modules_tcl$tclmajorv' folders found relative to executable (or it's symlink if any)"
} else {
set tm_additions_dev $exe_module_folders
}
}
set nameexe_dir [file dirname [info nameofexecutable]]
#possible symlink (may resolve to same path as above - we check below to not add in twice)
if {[file tail $nameexe_dir] eq "bin"} {
lappend exe_module_folders [file dirname $nameexe_dir]/modules
lappend exe_module_folders [file dirname $nameexe_dir]/modules_tcl$tclmajorv
} else {
lappend exe_module_folders $nameexe_dir/modules
lappend exe_module_folders $nameexe_dir/modules_tcl$tclmajorv
if {"os" in $package_modes} {
#2) support developer running from a folder containing *.tm files they want to make available
# could cause problems if user happens to be in a subdirectory of a tm folder structure as namespaced modules won't work if not at a tm path root.
#The current dir could also be a subdirectory of an existing tm_dir which would fail during tcl::tm::add - we will need to wrap all additions in catch
set currentdir_modules [glob -nocomplain -dir [pwd] -type f -tail *.tm]
#we assume [pwd] will always return an external (not kit) path at this point - REVIEW
if {[llength $currentdir_modules]} {
#now add current dir (if no conflict with above)
set external_tm_dirs [linsert $external_tm_dirs 0 $currentdir_modules]
if {[file exists [pwd]/modules] || [file exists [pwd]/modules_tcl$tclmajorv]} {
puts stderr "WARNING: modules or modules_tcl$tclmajorv folders not added to tcl::tm::path due to modules found in current workding dir [pwd]"
}
} else {
#modules or modules_tclX subdir relative to cwd cannot be added if [pwd] has been added
set cwd_modules_folder [file normalize [file join [pwd] modules]]
if {[file isdirectory $cwd_modules_folder]} {
if {[string tolower $cwd_modules_folder] ni [string tolower $external_tm_dirs]} {
#prepend
set external_tm_dirs [linsert $external_tm_dirs 0 $cwd_modules_folder]
}
}
set cwd_modules_folder [file normalize [file join [pwd] modules_tcl$tclmajorv]]
if {[file isdirectory $cwd_modules_folder]} {
if {[string tolower $cwd_modules_folder] ni [string tolower $external_tm_dirs]} {
#prepend
set external_tm_dirs [linsert $external_tm_dirs 0 $cwd_modules_folder]
}
}
}
}
foreach modulefolder $exe_module_folders {
set lc_external_tm_dirs [string tolower $external_tm_dirs]
set lc_modulefolder [string tolower $modulefolder]
if {$lc_modulefolder in [string tolower $original_external_tm_dirs]} {
#perhaps we have an env var set pointing to one of our dev foldersl. We don't want to rely on how the kit ordered it.
#bring to front if not already there.
#assert it must be present in $lc_external_tm_dirs if it's in $original_external_tm_dirs
set posn [lsearch $lc_external_tm_dirs $lc_modulefolder]
if {$posn > 0} {
#don't rely on lremove here. Not all runtimes have it and we don't want to load our forward-compatibility packages yet.
#(still need to support tcl 8.6 - and this script used in multiple kits)
set external_tm_dirs [lreplace $external_tm_dirs $posn $posn]
#don't even add it back in if it doesn't exist in filesystem
if {[file isdirectory $modulefolder]} {
set external_tm_dirs [linsert $external_tm_dirs 0 $modulefolder]
#assert tcl::tm::list still empty here
#restore module paths
# -- --- --- --- --- --- --- ---
set new_tm_path [list]
foreach mode $package_modes {
switch -exact -- $mode {
internal {
#review
#even though the internal_tm_dirs came from either ::env or the executable's init - we don't treat them as 'os' paths
#Add them before our own internal additions
foreach n $internal_tm_dirs {
if {$n ni $new_tm_path} {
lappend new_tm_path $n
}
}
foreach n $tm_additions_internal {
if {$n ni $new_tm_path} {
lappend new_tm_path $n
}
}
}
} else {
if {$lc_modulefolder ni $lc_external_tm_dirs && [file isdirectory $modulefolder]} {
set external_tm_dirs [linsert $external_tm_dirs 0 $modulefolder] ;#linsert seems faster than 'concat [list $modulefolder] $external_tm_dirs' - review
dev {
foreach n $tm_additions_dev {
if {$n ni $new_tm_path} {
lappend new_tm_path $n
}
}
}
os {
foreach n $external_tm_dirs {
if {$n ni $new_tm_path} {
lappend new_tm_path $n
}
}
}
}
}
if {$devmode ne "devquiet" && ![llength $exe_module_folders]} {
puts stderr "Warning - no 'modules' or 'modules_tcl$tclmajorv' folders found relative to executable (or it's symlink if any)"
foreach p [lreverse $new_tm_path] {
if {[catch {tcl::tm::add $p} errM]} {
puts stderr "Failed to add tm module dir '$p' to tcl::tm::list\n$errM"
}
}
##tcl::tm::add internals first (so they end up at the end of the tmlist) as in 'dev' mode (dev as first argument on launch) we preference external modules
##note use of lreverse to maintain same order
#foreach p [lreverse $internal_tm_dirs] {
# if {$p ni [tcl::tm::list]} {
# #Items that end up at the beginning of the tm list are processed first.. but an item of same version later in the tm list will not override the ifneeded script of an already encountered .tm.
# #addition can fail if one path is a prefix of another
# if {[catch {tcl::tm::add $p} errM]} {
# puts stderr "Failed to add internal module dir '$p' to tcl::tm::list\n$errM"
# }
# }
#}
##push externals to *head* of tcl::tm::list - as they have priority
#foreach p [lreverse $external_tm_dirs] {
# if {$p ni [tcl::tm::list]} {
# if {[catch {tcl::tm::add $p} errM]} {
# puts stderr "Failed to add external module dir '$p' to tcl::tm::list\n$errM"
# }
# }
#}
#AUTO_PATH
#auto_path - add *external* exe-relative after exe-relative path
#add lib and lib_tcl8 lib_tcl9 etc based on tclmajorv
#libs appended to end of ::auto_path are processed first (reverse order processing in 'package unknown'), but ifneeded scripts are overridden by earlier ones
@ -323,132 +475,122 @@ apply { args {
#we can't rely on builtin ledit (tcl9+) or loadable version such as punk::lib::compat::ledit at this point
#so we prepend to auto_path using a slightly inefficient method. Should be fine on relatively small list like this
#eventually it should just be something like 'ledit ::auto_path -1 -1 $libfolder'
if {"windows" eq $::tcl_platform(platform)} {
#case differences dont matter - but can stop us finding path in auto_path
foreach libsub [list lib_tcl$tclmajorv lib] {
if {[file tail $nameexe_dir] eq "bin"} {
set libfolder [file dirname $nameexe_dir]/$libsub
} else {
set libfolder $nameexe_dir/$libsub
}
if {[string tolower $libfolder] ni [list {*}$lc_auto_path {*}$auto_path_additions] && [file isdirectory $libfolder]} {
#lappend ::auto_path $libfolder
set auto_path_additions [list $libfolder {*}$auto_path_additions]
}
# -------------
if {[file tail $normexe_dir] eq "bin"} {
set libfolder [file dirname $normexe_dir]/$libsub
} else {
set libfolder $normexe_dir/$libsub
}
if {[string tolower $libfolder] ni [list {*}$lc_auto_path {*}$auto_path_additions] && [file isdirectory $libfolder]} {
#lappend ::auto_path $libfolder
set auto_path_additions [list $libfolder {*}$auto_path_additions]
}
# -------------
set libfolder [pwd]/$libsub
if {[string tolower $libfolder] ni [list {*}$lc_auto_path {*}$auto_path_additions] && [file isdirectory $libfolder]} {
#lappend ::auto_path $libfolder
set auto_path_additions [list $libfolder {*}$auto_path_additions]
}
}
} else {
#on other platforms, case differences could represent different paths
foreach libsub [list lib_tcl$tclmajorv lib] {
if {[file tail $nameexe_dir] eq "bin"} {
set libfolder [file dirname $nameexe_dir]/$libsub
} else {
set libfolder $nameexe_dir/$libsub
}
if {$libfolder ni [list {*}$::auto_path {*}$auto_path_addtions] && [file isdirectory $libfolder]} {
#lappend ::auto_path $libfolder
set auto_path_additions [list $libfolder {*}$auto_path_additions]
}
# -------------
if {[file tail $normexe_dir] eq "bin"} {
set libfolder [file dirname $normexe_dir]/$libsub
} else {
set libfolder $normexe_dir/$libsub
}
if {$libfolder ni [list {*}$::auto_path {*}$auto_path_additions] && [file isdirectory $libfolder]} {
#lappend ::auto_path $libfolder
set auto_path_additions [list $libfolder {*}$auto_path_additions]
if {"dev" in $package_modes} {
if {"windows" eq $::tcl_platform(platform)} {
#case differences dont matter - but can stop us finding path in auto_path
foreach libsub [list lib_tcl$tclmajorv lib] {
if {[file tail $nameexe_dir] eq "bin"} {
set libfolder [file dirname $nameexe_dir]/$libsub
} else {
set libfolder $nameexe_dir/$libsub
}
if {[file isdirectory $libfolder]} {
lappend auto_path_additions_dev $libfolder
}
# -------------
if {[file tail $normexe_dir] eq "bin"} {
set libfolder [file dirname $normexe_dir]/$libsub
} else {
set libfolder $normexe_dir/$libsub
}
if {[file isdirectory $libfolder]} {
lappend auto_path_additions_dev $libfolder
}
# -------------
set libfolder [pwd]/$libsub
if {[file isdirectory $libfolder]} {
lappend auto_path_additions_dev $libfolder
}
}
# -------------
set libfolder [pwd]/$libsub
if {$libfolder ni [list {*}$::auto_path {*}$auto_path_additions] && [file isdirectory $libfolder]} {
#lappend ::auto_path $libfolder
set auto_path_additions [list $libfolder {*}$auto_path_additions]
} else {
#on other platforms, case differences could represent different paths
foreach libsub [list lib_tcl$tclmajorv lib] {
if {[file tail $nameexe_dir] eq "bin"} {
set libfolder [file dirname $nameexe_dir]/$libsub
} else {
set libfolder $nameexe_dir/$libsub
}
if {[file isdirectory $libfolder]} {
lappend auto_path_additions_dev $libfolder
}
# -------------
if {[file tail $normexe_dir] eq "bin"} {
set libfolder [file dirname $normexe_dir]/$libsub
} else {
set libfolder $normexe_dir/$libsub
}
if {[file isdirectory $libfolder]} {
lappend auto_path_additions_dev $libfolder
}
# -------------
set libfolder [pwd]/$libsub
if {[file isdirectory $libfolder]} {
lappend auto_path_additions_dev $libfolder
}
}
}
}
set ::auto_path [list {*}$::auto_path {*}$auto_path_additions]
#2) support developer running from a folder containing *.tm files they want to make available
# could cause problems if user happens to be in a subdirectory of a tm folder structure as namespaced modules won't work if not at a tm path root.
#The current dir could also be a subdirectory of an existing tm_dir which would fail during tcl::tm::add - we will need to wrap all additions in catch
set currentdir_modules [glob -nocomplain -dir [pwd] -type f -tail *.tm]
#we assume [pwd] will always return an external (not kit) path at this point - REVIEW
if {[llength $currentdir_modules]} {
#now add current dir (if no conflict with above)
#catch {tcl::tm::add [pwd]}
set external_tm_dirs [linsert $external_tm_dirs 0 $currentdir_modules]
if {$devmode ne "devquiet" && ([file exists [pwd]/modules] || [file exists [pwd]/modules_tcl$tclmajorv])} {
puts stderr "WARNING: modules or modules_tcl$tclmajorv folders not added to tcl::tm::path due to modules found in current workding dir [pwd]"
}
} else {
#modules or modules_tclX subdir relative to cwd cannot be added if [pwd] has been added
set cwd_modules_folder [file normalize [file join [pwd] modules]]
if {[file isdirectory $cwd_modules_folder]} {
if {[string tolower $cwd_modules_folder] ni [string tolower $external_tm_dirs]} {
#prepend
set external_tm_dirs [linsert $external_tm_dirs 0 $cwd_modules_folder]
# -- --- --- --- --- --- --- ---
#split existing ::auto_path entries into internal & external
set internal_ap_dirs [list] ;#
set external_ap_dirs [list]
set lcase_internal_paths [string tolower $internal_paths]
foreach pkgpath $::auto_path {
set pkgpathlower [string tolower $pkgpath]
set is_internal 0
foreach okprefix $lcase_internal_paths {
if {[string match "$okprefix*" $pkgpathlower]} {
lappend internal_ap_dirs $pkgpath
set is_internal 1
break
}
}
set cwd_modules_folder [file normalize [file join [pwd] modules_tcl$tclmajorv]]
if {[file isdirectory $cwd_modules_folder]} {
if {[string tolower $cwd_modules_folder] ni [string tolower $external_tm_dirs]} {
#prepend
set external_tm_dirs [linsert $external_tm_dirs 0 $cwd_modules_folder]
}
if {!$is_internal} {
lappend external_ap_dirs $pkgpath
}
}
#assert tcl::tm::list still empty here
#restore module paths
#tcl::tm::add internals first (so they end up at the end of the tmlist) as in 'dev' mode (dev as first argument on launch) we preference external modules
#note use of lreverse to maintain same order
foreach p [lreverse $internal_tm_dirs] {
if {$p ni [tcl::tm::list]} {
#Items that end up at the beginning of the tm list are processed first.. but an item of same version later in the tm list will not override the ifneeded script of an already encountered .tm.
#addition can fail if one path is a prefix of another
if {[catch {tcl::tm::add $p} errM]} {
puts stderr "Failed to add internal module dir '$p' to tcl::tm::list\n$errM"
# -- --- --- --- --- --- --- ---
set new_auto_path [list]
foreach mode $package_modes {
switch -exact -- $mode {
internal {
#review
#even though the internal_ap_dirs came from either ::env or the executable's init - we don't treat them as 'os' paths
#Add them before our own internal additions
foreach n $internal_ap_dirs {
if {$n ni $new_auto_path} {
lappend new_auto_path $n
}
}
foreach n $auto_path_additions_internal {
if {$n ni $new_auto_path} {
lappend new_auto_path $n
}
}
}
}
}
#push externals to *head* of tcl::tm::list - as they have priority
foreach p [lreverse $external_tm_dirs] {
if {$p ni [tcl::tm::list]} {
if {[catch {tcl::tm::add $p} errM]} {
puts stderr "Failed to add external module dir '$p' to tcl::tm::list\n$errM"
dev {
foreach n $auto_path_additions_dev {
if {$n ni $new_auto_path} {
lappend new_auto_path $n
}
}
}
os {
foreach n $external_ap_dirs {
if {$n ni $new_auto_path} {
lappend new_auto_path $n
}
}
}
}
}
#------------------------------------------------------------------------------
#REVIEW
#package require a bogus package to ensure Tcl scans the whole auto_path/tm list - otherwise a lower-versioned module earlier in the path may be loaded
#This seems to take not insignificant time depending on size of auto_path and tcl::tm::list (e.g 2023 i9 ssd 100ms) - but seems unavoidable for now
#catch {package require flobrudder666_nonexistant}
#------------------------------------------------------------------------------
set ::auto_path $new_auto_path
} else {
#not dev/devquiet
#package_mode 'internal' only
#Tcl_Init will most likely have set up some external paths
#As our app has been started without the 'dev' first arg - we will prune paths that are not zipfs or tclkit
#As our app has been started without first arg (package_mode) indicating anything other than 'internal' - we will prune paths that are not zipfs or tclkit
#(or set via punkboot::internal_paths)
set filtered_auto_path [list]
#review - case insensitive ok for windows - but could cause issues on other platforms?
@ -464,16 +606,27 @@ apply { args {
puts stderr "main.tcl internal_paths: $internal_paths"
puts stderr "main.tcl filtered_auto_path: $filtered_auto_path"
set new_tm_list [list]
set filtered_tm_list [list]
foreach tm [tcl::tm::list] {
set tmlower [string tolower $tm]
foreach okprefix $internal_paths {
if {[string match "[string tolower $okprefix]*" $tmlower]} {
lappend new_tm_list $tm
lappend filtered_tm_list $tm
break
}
}
}
set new_tm_list [list]
foreach p $filtered_tm_list {
if {$p ni $new_tm_list && [file exists $p]} {
lappend new_tm_list $p
}
}
foreach p $tm_additions_internal {
if {$p ni $new_tm_list && [file exists $p]} {
lappend new_tm_list $p
}
}
tcl::tm::remove {*}[tcl::tm::list]
tcl::tm::add {*}[lreverse $new_tm_list]
@ -494,16 +647,16 @@ apply { args {
#add back the info lib reported by the executable.. as we can't access the one built into a kit
if {[file exists [info library]]} {
if {[string tolower [info library]] ni [string tolower [list {*}$filtered_auto_path {*}$auto_path_additions]]} {
lappend auto_path_additions [info library]
if {[string tolower [info library]] ni [string tolower [list {*}$filtered_auto_path {*}$auto_path_additions_internal]]} {
lappend auto_path_additions_internal [info library]
}
}
set lib_types [list lib lib_tcl$tclmajorv]
foreach l $lib_types {
set lib [file join $vfsdir $l]
if {[file exists $lib] && [string tolower $lib] ni [string tolower [list {*}$filtered_auto_path {*}$auto_path_additions]]} {
lappend auto_path_additions $lib
if {[file exists $lib] && [string tolower $lib] ni [string tolower [list {*}$filtered_auto_path {*}$auto_path_additions_internal]]} {
lappend auto_path_additions_internal $lib
}
}
#foreach l $lib_types {
@ -512,7 +665,7 @@ apply { args {
# lappend ::auto_path $lib
# }
#}
set ::auto_path [list {*}$filtered_auto_path {*}$auto_path_additions]
set ::auto_path [list {*}$filtered_auto_path {*}$auto_path_additions_internal]
puts stderr "main.tcl final auto_path: $::auto_path"
@ -532,11 +685,12 @@ apply { args {
#}
} else {
#normal case main.tcl from vfs
set ::auto_path [list {*}$filtered_auto_path {*}$auto_path_additions]
set ::auto_path [list {*}$filtered_auto_path {*}$auto_path_additions_internal]
}
#force rescan
#catch {package require flobrudder666_nonexistant}
set arglist $args
puts stderr "main.tcl auto_path :$::auto_path"
puts stderr "main.tcl tcl::tm::list:[tcl::tm::list]"
}
if {$has_zipfs_attached} {

106
src/vfs/_vfscommon.vfs/lib/app-punk/repl.tcl

@ -1,53 +1,53 @@
package provide app-punk 1.0
#punk linerepl launcher
#By the time we get here, we don't expect other packages to have been loaded - but the lib/module paths have already been scanned to populate 'package names'
#puts stdout "$::auto_path"
#puts stderr "-----------"
#puts stderr "tcl::tm::list"
#puts stderr "-----------"
#puts stderr "[join [tcl::tm::list] \n]"
#puts stderr "-----------"
#puts stderr "auto_path"
#puts stderr "-----------"
#puts stderr "[join $::auto_path \n]"
#puts stderr "-----------"
#puts stderr "thread? [package provide Thread]"
set thread_version [package require Thread]
#puts stderr "repl.tcl thread version:$thread_version"
#puts stderr "info loaded:"
#puts stderr [join [info loaded] \n]
#set tpath [lindex [info loaded] 0 0]
#puts stdout "--$tpath--"
#puts stdout "--[file exists $tpath]--"
#set tid [thread::create -preserved]
#thread::send $tid {puts thread1}
#puts stdout "mythread: [thread::id]"
#review
#catch {package require tcllibc}
#punk & shellrun should be in codethreads - but not required in the parent repl threads
package require shellfilter
package require punk::repl
set v [package provide punk::repl]
puts stderr "punk::repl version:$v script: [package ifneeded punk::repl $v]"
#puts stderr "package names"
#set packages_present [list]
#foreach p [package names] {
# if {[package provide $p] ne ""} {
# lappend packages_present $p
# }
#}
#puts stderr [join $packages_present \n]
repl::init -safe 0
#puts stderr "Launching repl::start stdin -title app-punk"
#flush stderr
repl::start stdin -title app-punk
#puts "- repl app done -"
#flush stdout
package provide app-punk 1.0
#punk linerepl launcher
#By the time we get here, we don't expect other packages to have been loaded - but the lib/module paths have already been scanned to populate 'package names'
#puts stdout "$::auto_path"
#puts stderr "-----------"
#puts stderr "tcl::tm::list"
#puts stderr "-----------"
#puts stderr "[join [tcl::tm::list] \n]"
#puts stderr "-----------"
#puts stderr "auto_path"
#puts stderr "-----------"
#puts stderr "[join $::auto_path \n]"
#puts stderr "-----------"
#puts stderr "thread? [package provide Thread]"
set thread_version [package require Thread]
#puts stderr "repl.tcl thread version:$thread_version"
#puts stderr "info loaded:"
#puts stderr [join [info loaded] \n]
#set tpath [lindex [info loaded] 0 0]
#puts stdout "--$tpath--"
#puts stdout "--[file exists $tpath]--"
#set tid [thread::create -preserved]
#thread::send $tid {puts thread1}
#puts stdout "mythread: [thread::id]"
#review
#catch {package require tcllibc}
#punk & shellrun should be in codethreads - but not required in the parent repl threads
package require shellfilter
package require punk::repl
#set v [package provide punk::repl]
#puts stderr "punk::repl version:$v script: [package ifneeded punk::repl $v]"
#puts stderr "package names"
#set packages_present [list]
#foreach p [package names] {
# if {[package provide $p] ne ""} {
# lappend packages_present $p
# }
#}
#puts stderr [join $packages_present \n]
repl::init -safe 0
#puts stderr "Launching repl::start stdin -title app-punk"
#flush stderr
repl::start stdin -title app-punk
#puts "- repl app done -"
#flush stdout

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

@ -561,14 +561,68 @@ namespace eval punk {
@id -id ::punk::grepstr
@cmd -name punk::grepstr\
-summary\
"Grep for regex pattern in supplied (possibly ANSI) string."\
"Grep for regex pattern in plaintext of supplied (possibly ANSI) string."\
-help\
""
"The grepstr command can find strings in ANSI text even if there are interspersed
ANSI colour codes etc. Even if a word has different coloured/styled letters, the
regex can match the plaintext. (Search is performed on ansistripped text, and then
the matched sections are highlighted and overlayed on the original styled/colourd
input.
If the input string has ANSI movement codes - the resultant text may not be directly
searchable because the parts of a word may be separated by various codes and other
plain text. To search such an input string, the string should first be 'rendered' to
a form where the ANSI only represents SGR styling (and perhaps other non-movement
codes) using something like overtype::renderline or overtype::rendertext."
@leaders -min 0 -max 0
@opts
-returnlines -type string -default all -choices {matched all}
-ansistrip -type none
-no-linenumbers -type none
-returnlines -type string -typesynopsis matched|all -default breaksandmatches -choicecolumns 1 -choices {matched all breaksandmatches} -choicelabels {
"matched"\
" Return only lines that matched."
"breaksandmatches"\
" Return configured --break= lines in between non-consecutive matches"
"all"\
" Return all lines.
This has a similar effect to the 'grep' trick of matching on 'pattern|$'
(The $ matches all lines that have an end; ie all lines, but there is no
associated character to which to apply highlighting)
except that when instead using -returnlines all with --line-number, the *
indicator after the linenumber will only be highlighted for lines with matches,
and the following matchcount will indicate zero for non-matching lines."
}
-B|--before-context= -parsekey "--before-context" -default 0 -type integer -typesynopsis num
-C|--context= -parsekey "--context" -default 0 -type integer -typesynopsis num -help\
"Print num lines of leading and trailing context surrounding each match."
-A|--after-context= -parsekey "--after-context" -default 0 -type integer -typesynopsis num
--break= -type string -default "-- %c%\U2260" -help\
"When returning matched lines and there is a break in consecutive output,
display the break with the given string. %c% is a placeholder for the
number of lines skipped.
Use empty-string for an empty line as a break display.
grepstr --break= needle $haystacklines
The unix grep utility commonly uses -- for this indicator.
grepstr --break=-- needle $haystacklines
Customisation example:
grepstr -n \"--break=(skipped %c% lines)\" needle $haystacklines
"
-ansistrip -type none -help\
"Strip all ansi codes from the input string before processing.
This is not necessary for regex matching purposes, as the matching is always
performed on the ansistripped characters anyway, but by stripping ANSI, the
result only has the ANSI supplied by the -highlight option."
#-n|--line-number as per grep utility, except that we include a * for matches
-n|--line-number -type none -help\
"Each output line is preceded by its relative line number in the file, starting at line 1.
For lines that matched the regex, the line number will be suffixed with a * indicator
with the same highlighting as the matched string(s).
The number of matches in the line immediately follows the *
For lines with no matches the * indicator is present with no highlighting and suffixed
with zeros."
-i|--ignore-case -type none -help\
"Perform case insensitive matching."
-highlight -type list -typesynopsis ansinames -default {green bold Black underline overline} -help\
"list of ANSI SGR style codes as supported by and documented in punk::ansi::a?"
-- -type none
@ -585,12 +639,26 @@ namespace eval punk {
if {[dict exists $received -ansistrip]} {
set data [punk::ansi::ansistrip $data]
}
set highlight [dict get $opts -highlight]
set returnlines [dict get $opts -returnlines]
if {[dict exists $received -no-linenumbers]} {
set do_linenums 0
set highlight [dict get $opts -highlight]
set opt_returnlines [dict get $opts -returnlines]
set context [dict get $opts --context] ;#int
set beforecontext [dict get $opts --before-context]
set beforecontext [expr {max($beforecontext,$context)}]
set aftercontext [dict get $opts --after-context]
set aftercontext [expr {max($aftercontext,$context)}]
set break [dict get $opts --break]
set ignorecase [dict exists $received --ignore-case]
if {$ignorecase} {
set nocase "-nocase"
} else {
set nocase ""
}
if {[dict exists $received --line-number]} {
set do_linenums 1 ;#display lineindex+1
} else {
set do_linenums 1
set do_linenums 0
}
if {[llength $highlight] == 0} {
@ -604,56 +672,116 @@ namespace eval punk {
set data [string map {\r\n \n} $data]
if {![punk::ansi::ta::detect $data]} {
set lines [split $data \n]
set matches [lsearch -all -regexp $lines $pattern]
set matches [lsearch -all {*}$nocase -regexp $lines $pattern]
set result ""
if {$returnlines eq "all"} {
if {$opt_returnlines eq "all"} {
set returnlines [punk::lib::range 0 [llength $lines]-1]
} else {
#matches|breaksandmatches
set returnlines $matches
}
set max [lindex $returnlines end]
if {[string is integer -strict $max]} {
incr max
}
set w1 [string length $max]
foreach linenum $returnlines {
#lineindex is zero based - display of linenums is 1 based
set resultlines [dict create]
foreach lineindex $returnlines {
set ln [lindex $lines $lineindex]
set col1 ""
if {$do_linenums} {
set col1 "[format %${w1}s $linenum] "
set col1 [format "%${w1}s " [expr {$lineindex+1}]]
}
if {$lineindex in $matches} {
set ln [regsub -all {*}$nocase -- $pattern $ln $H&$R] ;#n
set matchcount [regexp -all {*}$nocase -- $pattern $ln]
if {$do_linenums} {
append col1 $H*$R[format %03s $matchcount]
}
} else {
set col1 ""
if {$do_linenums} {
append col1 "*000"
}
}
set ln [lindex $lines $linenum]
if {$linenum in $matches} {
set ln [regsub -all -- $pattern $ln $H&$R]
#---------------------------------------------------------------
set prelines [lrange $lines $lineindex-$beforecontext $lineindex-1]
set s [expr {$lineindex-$beforecontext-1}]
if {$s < -1} {set s -1}
foreach p $prelines {
incr s
#append result "[format %${w1}s [expr {$s+1}]]- " " " $p \n
if {![dict exists $resultlines $s]} {
if {$do_linenums} {
set show "[format "%${w1}s " [expr {$s+1}]]- $p"
} else {
set show $p
}
dict set resultlines $s $show
}
}
append result $col1 $ln \n
#---------------------------------------------------------------
if {$do_linenums} {
set show "$col1 $ln"
} else {
set show $ln
}
dict set resultlines $lineindex $show
#---------------------------------------------------------------
set postlines [lrange $lines $lineindex+1 $lineindex+$aftercontext]
set s $lineindex
foreach p $postlines {
incr s
if {![dict exists $resultlines $s]} {
if {$do_linenums} {
set show "[format "%${w1}s " [expr {$s+1}]]- $p"
} else {
set show $p
}
dict set resultlines $s $show
}
}
#---------------------------------------------------------------
}
set result [string trimright $result \n]
return $result
} else {
set plain [punk::ansi::ansistrip $data]
set plainlines [split $plain \n]
set lines [split $data \n]
set matches [lsearch -all -regexp $plainlines $pattern]
if {$returnlines eq "all"} {
set matches [lsearch -all {*}$nocase -regexp $plainlines $pattern]
if {$opt_returnlines eq "all"} {
set returnlines [punk::lib::range 0 [llength $lines]-1]
} else {
set returnlines $matches
}
set max [lindex $returnlines end]
if {[string is integer -strict $max]} {
#if max index is 9 - linenum will be 10, (99->100 etc) - so add one in case we're on such a boundary.
incr max
}
set w1 [string length $max]
set result ""
set placeholder \UFFEF ;#review
foreach linenum $returnlines {
set ln [lindex $lines $linenum]
set resultlines [dict create]
foreach lineindex $returnlines {
set ln [lindex $lines $lineindex]
set col1 ""
if {$do_linenums} {
set col1 "[format %${w1}s $linenum] "
} else {
set col1 ""
}
if {$linenum in $matches} {
set plain_ln [lindex $plainlines $linenum]
set parts [regexp -all -indices -inline -- $pattern $plain_ln]
set col1 [format "%${w1}s " [expr {$lineindex+1}]]
}
if {$lineindex in $matches} {
set plain_ln [lindex $plainlines $lineindex]
set parts [regexp -all {*}$nocase -indices -inline -- $pattern $plain_ln]
set matchcount [llength $parts]
if {$do_linenums} {
append col1 $H*$R[format %03s $matchcount]
}
if {[llength $parts] == 0} {
#shouldn't happen
append result $col1 $ln \n
#This probably can't happen (?)
#If it does.. it's more likely to be an issue with our line index than with regexp
puts stderr "Unexpected regex mismatch in grepstr - line marked with ??? (shouldn't happen)"
set matchshow "??? $ln"
#dict set resultlines $lineindex $show
} else {
set overlay ""
set i 0
@ -667,14 +795,85 @@ namespace eval punk {
append overlay [string repeat $placeholder [string length $tail]]
#puts "$overlay"
#puts "$ln"
append result $col1 [overtype::renderline -transparent $placeholder -insert_mode 0 $ln $overlay] \n
set rendered [overtype::renderline -transparent $placeholder -insert_mode 0 $ln $overlay]
if {$do_linenums} {
set matchshow "$col1 $rendered"
} else {
set matchshow $rendered
}
}
#---------------------------------------------------------------
set prelines [lrange $lines $lineindex-$beforecontext $lineindex-1]
set s [expr {$lineindex-$beforecontext-1}]
if {$s < -1} {set s -1}
foreach p $prelines {
incr s
#append result "[format %${w1}s [expr {$s+1}]]- " " " $p \n
if {![dict exists $resultlines $s]} {
if {$do_linenums} {
set show "[format "%${w1}s " [expr {$s+1}]]- $p"
} else {
set show $p
}
dict set resultlines $s $show
}
}
#---------------------------------------------------------------
dict set resultlines $lineindex $matchshow
#---------------------------------------------------------------
set postlines [lrange $lines $lineindex+1 $lineindex+$aftercontext]
set s $lineindex
foreach p $postlines {
incr s
if {![dict exists $resultlines $s]} {
if {$do_linenums} {
set show "[format "%${w1}s " [expr {$s+1}]]- $p"
} else {
set show $p
}
dict set resultlines $s $show
}
}
#---------------------------------------------------------------
} else {
append result $col1 $ln \n
if {$do_linenums} {
append col1 "*000"
set show "$col1 $ln"
} else {
set show $ln
}
dict set resultlines $lineindex $show
}
}
return $result
}
set ordered_resultlines [lsort -integer [dict keys $resultlines]]
set result ""
set i -1
set do_break 0
if {$opt_returnlines eq "breaksandmatches"} {
set do_break 1
}
if {$do_break} {
foreach r $ordered_resultlines {
incr i
if {$r > $i} {
set c [expr {$r - $i}]
append result [string map [list %c% $c] $break] \n
}
append result [dict get $resultlines $r] \n
set i $r
}
if {$i<[llength $lines]-1} {
set c [expr {[llength $lines]-1-$i}]
append result [string map [list %c% $c] $break] \n
}
} else {
foreach r $ordered_resultlines {
append result [dict get $resultlines $r] \n
}
}
set result [string trimright $result \n]
return $result
}
proc stacktrace {} {
@ -932,20 +1131,6 @@ namespace eval punk {
}
return $varlist
}
proc splitstrposn {s p} {
if {$p <= 0} {
if {$p == 0} {
list "" $s
} else {
list $s ""
}
} else {
scan $s %${p}s%s
}
}
proc splitstrposn_nonzero {s p} {
scan $s %${p}s%s
}
proc _split_var_key_at_unbracketed_comma {varspecs} {
set varlist [list]
@ -971,18 +1156,8 @@ namespace eval punk {
}
} else {
if {$c eq ","} {
#lappend varlist [splitstrposn $token $first_term]
set var $token
set spec ""
if {$first_term > 0} {
lassign [scan $token %${first_term}s%s] var spec
} else {
if {$first_term == 0} {
set var ""
set spec $token
}
}
lappend varlist [list $var $spec]
lappend varlist [punk::lib::string_splitbefore $token $first_term]
set token ""
set token_index -1 ;#reduce by 1 because , not included in next token
set first_term -1
@ -999,18 +1174,7 @@ namespace eval punk {
incr token_index
}
if {[string length $token]} {
#lappend varlist [splitstrposn $token $first_term]
set var $token
set spec ""
if {$first_term > 0} {
lassign [scan $token %${first_term}s%s] var spec
} else {
if {$first_term == 0} {
set var ""
set spec $token
}
}
lappend varlist [list $var $spec]
lappend varlist [punk::lib::string_splitbefore $token $first_term]
}
return $varlist
}
@ -1034,6 +1198,7 @@ namespace eval punk {
} else {
if {$c eq ","} {
if {$first_term > -1} {
#lassign [punk::lib::string_splitbefore $token $first_term] v k
set v [string range $token 0 $first_term-1]
set k [string range $token $first_term end] ;#key section includes the terminal char
lappend varlist [list $v $k]
@ -7141,74 +7306,210 @@ namespace eval punk {
return $result
}
##dict of lists?
#a
# 1
# 2
#b
# 3
# 4
# ""
# etc
# d
# D
# "ok then"
##dict of dicts
#a
# x
# 1
# y
# 2
#b
# x
# 11
##dict of mixed
#list
# a
# b
# c
#dict
# a
# aa
# b
# bb
#val
# x
#list
# a
# b
# each line has 1 key or value OR part of 1 key or value. ie <=1 key/val per line!
##multiline
#key
# "multi
# line value"
#
#--------------------------------
#a
# 1
# 2
#vs
#a
# 1
# 2
#dict of list-len 2 is equiv to dict of dict with one keyval pair
#--------------------------------
#!!!todo fix - linedict is unfinished and non-functioning
#linedict based on indents
proc linedict {args} {
puts stderr "linedict is experimental and incomplete"
set data [lindex $args 0]
set opts [lrange $args 1 end] ;#todo
set nlsplit [split $data \n]
set rootindent -1
set stepindent -1
#set wordlike_parts [regexp -inline -all {\S+} $lastitem]
set d [dict create]
set keys [list]
set i 1
set firstkeyline "N/A"
set firststepline "N/A"
#first do a partial loop through lines and work out the rootindent and stepindent.
#we could do this in the main loop - but we do it here to remove a small bit of logic from the main loop.
#review - if we ever move to streaming a linedict - we'll need to re-arrange to validating indents as we go anyway.
set linenum 0
set firstkey_line "N/A"
set firstkey_linenum -1
set firststep_line "N/A"
set firststep_linenum -1
set indents_seen [dict create]
foreach ln $nlsplit {
incr linenum
if {![string length [string trim $ln]]} {
incr i
continue
}
set is_rootkey 0
#todo - use info complete to accept keys/values with newlines
regexp {(\s*)(.*)} $ln _ space linedata
puts stderr ">>line:'$ln' [string length $space] $linedata"
set this_indent [string length $space]
if {$rootindent < 0} {
set firstkeyline $ln
set rootindent $this_indent
if {[catch {lindex $linedata 0}]} {
error "bad line value '$linedata' - each line must be a valid Tcl list of length 1. Use quotes or curly braces as necessary"
}
if {$this_indent == $rootindent} {
set is_rootkey 1
if {[llength $linedata] > 1} {
error "bad line value '$linedata' - each line must be a valid Tcl list of length 1. Use quotes or curly braces as necessary"
}
if {$this_indent < $rootindent} {
error "bad root indentation ($this_indent) at line: $i smallest indent was set by first key line: $firstkeyline"
#puts stderr "--linenum:[format %-3s $linenum] line:[format "%-20s" $ln] [format %-4s [string length $space]] $linedata"
set this_indent [string length $space]
if {[dict exists $indents_seen $this_indent]} {
continue
}
if {$is_rootkey} {
dict set d $linedata {}
lappend keys $linedata
if {$rootindent < 0} {
set firstkey_line $ln
set firstkey_linenum $linenum
set rootindent $this_indent
dict set indents_seen $this_indent 1
} elseif {$stepindent < 0} {
if {$this_indent > $rootindent} {
set firststep_line $ln
set firststep_linenum $linenum
set stepindent [expr {$this_indent - $rootindent}]
dict set indents_seen $this_indent 1
} elseif {$this_indent < $rootindent} {
error "bad root indentation ($this_indent) at line: $linenum. Smallest indent was set on linenumber: $firstkey_linenum by first key line: $firstkey_line"
}
#if equal - it's just another root key
} else {
if {$stepindent < 0} {
set stepindent $this_indent
set firststepline $ln
#validate all others
if {$this_indent < $rootindent} {
error "bad root indentation ($this_indent) at line: $linenum. Smallest indent was set on linenumber: $firstkey_linenum by first key line: $firstkey_line"
}
if {$this_indent == $stepindent} {
dict set d [lindex $keys end] $ln
if {($this_indent - $rootindent) % $stepindent != 0} {
error "bad indentation ($this_indent) at linenum: $linenum line:'$ln'. this_indent - rootindent ($this_indent - $rootindent == [expr {$this_indent - $rootindent}]) is not a multiple of the first key indent $stepindent seen on linenumber: $firststep_linenum value:'$firststep_line'"
} else {
if {($this_indent % $stepindent) != 0} {
error "bad indentation ($this_indent) at line: $i not a multiple of the first key indent $step_indent seen on $firststepline"
}
dict set indents_seen $this_indent 1
}
}
}
#set wordlike_parts [regexp -inline -all {\S+} $lastitem]
set d [dict create]
set keys [list]
set linenum 0 ;#line-numbers 1 based
foreach ln $nlsplit {
incr linenum
if {![string length [string trim $ln]]} {
incr linenum
continue
}
regexp {(\s*)(.*)} $ln _ space linedata
puts stderr ">>linenum:[format %-3s $linenum] line:[format "%-20s " $ln] [format %-4s [string length $space]] $linedata"
set linedata [lindex $linedata 0]
set this_indent [string length $space]
#todo fix!
if {$this_indent == $rootindent} {
#is rootkey
dict set d $linedata {}
set keys [list $linedata]
} else {
set ispan [expr {$this_indent - $rootindent}]
set numsteps [expr {$ispan / $stepindent}]
#assert - since validated in initial loop - numsteps is always >= 1
set keydepth [llength $keys]
if {$numsteps > $keydepth + 1} {
#too deep - not tested for in initial loop. ? todo - convert to leading spaces in key/val?
error "bad indentation ($this_indent) at linenum: $linenum line:'$ln'. (too deep) - review"
}
if {$numsteps > ($keydepth - 1)} {
#assert - from above test - must be 1 or 2 deeper
set parentkey [lindex $keys end]
lappend keys [list $parentkey $ln]
set oldval [dict get $d $parentkey]
if {[string length $oldval]} {
set new [dict create $oldval $ln]
set oldval [dict get $d {*}$parentkey]
if {$numsteps - ($keydepth -1) == 1} {
#1 deeper
if {$oldval ne {}} {
lappend keys [list {*}$parentkey $linedata]
dict unset d {*}$parentkey
#dict set d {*}$parentkey $oldval $linedata
dict set d {*}$parentkey $oldval {} ;#convert to key?
dict set d {*}$parentkey $linedata {}
} else {
dict set d {*}$parentkey $linedata
}
} else {
dict set d $parentkey $ln
#2 deeper - only ok if there is an existing val
if {$oldval eq {}} {
error "bad indentation ($this_indent) at linenum: $linenum line:'$ln'. (too deep) - review"
}
puts ">>> 2deep d:'$d' oldval:$oldval linedata:$linedata parentkey:$parentkey"
dict unset d {*}$parentkey
dict set d {*}$parentkey $oldval $linedata
lappend keys [list {*}$parentkey $oldval]
}
} elseif {$numsteps < ($keydepth - 1)} {
set diff [expr {$keydepth - 1 - $numsteps}]
set keys [lrange $keys 0 end-$diff]
#now treat as same level
set parentkey [lindex $keys end-1]
lset keys end end $linedata
dict set d {*}$parentkey $linedata {}
} else {
#same level
set parentkey [lindex $keys end-1]
lset keys end end $linedata
dict set d {*}$parentkey $linedata {}
}
}
incr i
#puts ">>keys:$keys"
}
return $d
}
proc dictline {d} {
proc dictline {d {indent 2}} {
puts stderr "unimplemented"
set lines [list]
@ -7451,25 +7752,29 @@ namespace eval punk {
1 {
#val may have ansi - including resets. Pass through ansibase_lines to
if {$ansibase ne ""} {
set displayval [::textblock::ansibase_lines $displayval $ansibase]
#set displayval [::textblock::ansibase_lines $displayval $ansibase]
set displayval [punk::ansi::ansiwrap_raw $ansibase "" "" $displayval]
}
}
2 {
set displayval $ansibase[ansistring VIEW $displayval]
if {$ansibase ne ""} {
set displayval [::textblock::ansibase_lines $displayval $ansibase]
#set displayval [::textblock::ansibase_lines $displayval $ansibase]
set displayval [punk::ansi::ansiwrap_raw $ansibase "" "" $displayval]
}
}
3 {
set displayval $ansibase[ansistring VIEWCODE $displayval]
if {$ansibase ne ""} {
set displayval [::textblock::ansibase_lines $displayval $ansibase]
#set displayval [::textblock::ansibase_lines $displayval $ansibase]
set displayval [punk::ansi::ansiwrap_raw $ansibase "" "" $displayval]
}
}
4 {
set displayval $ansibase[ansistring VIEWSTYLE $displayval]
if {$ansibase ne ""} {
set displayval [::textblock::ansibase_lines $displayval $ansibase]
#set displayval [::textblock::ansibase_lines $displayval $ansibase]
set displayval [punk::ansi::ansiwrap_raw $ansibase "" "" $displayval]
}
}
}

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

@ -723,7 +723,8 @@ tcl::namespace::eval punk::ansi {
}
lappend adjusted_row $i
}
append result [textblock::join_basic -- {*}$adjusted_row] \n
#append result [textblock::join_basic -- {*}$adjusted_row] \n
append result [textblock::join_basic_raw {*}$adjusted_row] \n
incr rowindex
}
@ -1981,10 +1982,6 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
set blockrow [list]
set height 50 ;#number of lines (excluding header) vertically in a blockrow
set columns 5 ;#number of columns in a blockrow
set i -1
set t ""
set start 0
set colidx -1
variable TK_colour_map ;#use the version without lowercased additions - this gives the display names with casing as shown in Tk colour man page.
if {!$do_merge} {
set map $TK_colour_map
@ -2031,9 +2028,13 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
set overheight 0
set t ""
set start 0
set colidx -1
set i -1
foreach cname $keys {
set data [dict get $map $cname]
incr i
set data [dict get $map $cname]
if {$overheight || $i % $height == 0} {
set overheight 0
incr colidx
@ -2072,17 +2073,17 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
set fg "rgb-$cdec-contrasting"
$t configure_row [expr {[$t row_count]-1}] -ansibase [a+ {*}$fc $fg Rgb-$cdec]
}
if {$i == 0 || $i % $height != 0} {
if {$t ne ""} {
$t configure -frametype {}
$t configure_column 0 -headers [list "TK colours $start - $i"]
$t configure_column 0 -header_colspans [list any]
$t configure -ansibase_header [a+ {*}$fc web-black Web-white]
lappend blockrow [$t print] " "
lappend blocklist $blockrow
$t destroy
}
if {$t ne ""} {
$t configure -frametype {}
$t configure_column 0 -headers [list "TK colours $start - $i"]
$t configure_column 0 -header_colspans [list any]
$t configure -ansibase_header [a+ {*}$fc web-black Web-white]
lappend blockrow [$t print] " "
lappend blocklist $blockrow
$t destroy
}
set result ""
foreach blockrow $blocklist {
append result [textblock::join -- {*}$blockrow] \n
@ -2569,16 +2570,10 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
$t add_row [list $i $descr $s [ansistring VIEW $s]]
}
x11 - X11 {
set tail [tcl::string::tolower [tcl::string::range $i 4 end]]
set cont [string range $tail end-11 end]
switch -- $cont {
-contrasting - -contrastive {
set cname [string range $tail 0 end-12]
}
default {
set cname $tail
}
}
set cname [tcl::string::tolower [tcl::string::range $i 4 end]]
set cont [string range $cname end-11 end]
switch -exact -- $cont {-contrasting - -contrastive {set cname [string range $tail end-12]}}
if {[tcl::dict::exists $X11_colour_map $cname]} {
set dec [tcl::dict::get $X11_colour_map $cname]
set hex [colour_dec2hex $dec]
@ -2854,18 +2849,12 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
#variable WEB_colour_map
#upvar ::punk::ansi::WEB_colour_map WEB_colour_map
#foreground web colour
set tail [tcl::string::tolower [tcl::string::range $i 4 end]]
set cname [tcl::string::tolower [tcl::string::range $i 4 end]]
#-contrasting
#-contrastive
set cont [string range $tail end-11 end]
switch -- $cont {
-contrasting - -contrastive {
set cname [string range $tail 0 end-12]
}
default {
set cname $tail
}
}
set cont [string range $cname end-11 end]
switch -- $cont { -contrasting - -contrastive {set cname [string range $cname 0 end-12]} }
if {[tcl::dict::exists $WEB_colour_map $cname]} {
set rgbdash [tcl::dict::get $WEB_colour_map $cname]
switch -- $cont {
@ -3184,16 +3173,10 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
tk {
#foreground tk names
variable TK_colour_map_lookup ;#use the dict with added lowercase versions
set tail [tcl::string::tolower [tcl::string::range $i 3 end]]
set cont [string range $tail end-11 end]
switch -- $cont {
-contrasting - -contrastive {
set cname [string range $tail 0 end-12]
}
default {
set cname $tail
}
}
set cname [tcl::string::tolower [tcl::string::range $i 3 end]]
lassign [punk::lib::string_splitbefore $cname end-11] c cont
switch -exact -- $cont { -contrasting - -contrastive {set cname $c} }
if {[tcl::dict::exists $TK_colour_map_lookup $cname]} {
set rgbdash [tcl::dict::get $TK_colour_map_lookup $cname]
@ -3216,17 +3199,11 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
Tk - TK {
#background X11 names
variable TK_colour_map_lookup ;#with lc versions
set tail [tcl::string::tolower [tcl::string::range $i 3 end]]
set cont [string range $tail end-11 end]
switch -- $cont {
-contrasting - -contrastive {
set cname [string range $tail 0 end-12]
}
default {
set cname $tail
}
}
#set cname [tcl::string::tolower [tcl::string::range $i 3 end]]
set cname [tcl::string::tolower [tcl::string::range $i 3 end]]
lassign [punk::lib::string_splitbefore $cname end-11] c cont
switch -- $cont { -contrasting - -contrastive {set cname $c} }
if {[tcl::dict::exists $TK_colour_map_lookup $cname]} {
set rgbdash [tcl::dict::get $TK_colour_map_lookup $cname]
switch -- $cont {
@ -3251,7 +3228,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
} elseif {[tcl::string::first : $i] > 0} {
lappend e $i
} else {
puts stderr "punk::ansi::a+ ansi name unmatched: '$i' in call 'a+ $args' Perhaps missing prefix? e.g web- x11- term- rgb# rgb-"
puts stderr "punk::ansi::a+ ansi name unmatched: '$i' in call 'a+ $args' Perhaps missing prefix? e.g web- x11- tk- term- rgb# rgb-"
}
}
}
@ -3751,8 +3728,6 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
-rawansi -type ansi -default ""
-resetcodes -type list -default {reset}
-rawresets -type ansi -default ""
-fullcodemerge -type boolean -default 0 -help\
"experimental"
-overridecodes -type list -default {}
-rawoverrides -type ansi -default ""
@values -min 1 -max 1
@ -3767,13 +3742,13 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
}
#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
#no opts - skip args parser
#maint: keep defaults in sync with definition above
set codelists $args
set text [lpop codelists]
set R [a] ;#plain ansi reset (equiv of default "reset")
set rawansi ""
set rawresets ""
set fullmerge 0
set overrides ""
set rawoverrides ""
} else {
@ -3784,7 +3759,6 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
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]]]
set rawoverrides [punk::ansi::ta::get_codes_single [dict get $opts -rawoverrides]]
}
@ -3793,22 +3767,18 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
#there can be SGR unmergeables (due to enhanced underlines) as well as non SGR codes
set codes [concat {*}$codelists] ;#flatten
set base [a+ {*}$codes]
set baselist [punk::ansi::ta::get_codes_single $base]
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]]
}
set base [punk::ansi::codetype::sgr_merge_singles [list {*}$baselist {*}$rawcodes]]
set baselist [punk::ansi::ta::get_codes_single $base]
}
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 Rcodes [punk::ansi::ta::get_codes_single $R]
set R [punk::ansi::codetype::sgr_merge_singles [list {*}$Rcodes {*}$rawresetcodes]]
}
if {$rawoverrides ne ""} {
set rawoverridecodes [punk::ansi::ta::get_codes_single $rawoverrides]
set overrides [list {*}$overrides {*}$rawoverridecodes]
@ -3830,20 +3800,105 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
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
append emit [punk::ansi::codetype::sgr_merge_singles [list {*}$baselist {*}$codestack {*}$overrides]] $pt $R
}
}
default {
append emit [punk::ansi::codetype::sgr_merge_singles [list {*}$baselist {*}$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 {
append emit [punk::ansi::codetype::sgr_merge_singles [list $base {*}$codestack {*}$overrides]] $pt $R
}
}
7GFX {
switch -- [tcl::string::index $code 2] {
"0" {
set o_gx_state on
}
"B" {
set o_gx_state off
}
}
}
default {
#other ansi codes
}
}
default {
if {$fullmerge} {
append emit [punk::ansi::codetype::sgr_merge [list $base {*}$codestack {*}$overrides]] $pt $R
append emit $code
}
}
return [append emit $R]
} else {
return $base$text$R
}
}
proc ansiwrap_raw {rawansi rawresets rawoverrides text} {
set codelists ""
set R ""
set overrides ""
#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
set base [a+ {*}$codes]
set baselist [punk::ansi::ta::get_codes_single $base]
if {$rawansi ne ""} {
set rawcodes [punk::ansi::ta::get_codes_single $rawansi] ;#caller may have supplied as [a+ xxx][a+ yyy]
set base [punk::ansi::codetype::sgr_merge_singles [list {*}$baselist {*}$rawcodes]]
set baselist [punk::ansi::ta::get_codes_single $base]
}
if {$rawresets ne ""} {
set rawresetcodes [punk::ansi::ta::get_codes_single $rawresets]
set Rcodes [punk::ansi::ta::get_codes_single $R]
set R [punk::ansi::codetype::sgr_merge_singles [list {*}$Rcodes {*}$rawresetcodes]]
}
if {$rawoverrides ne ""} {
set rawoverridecodes [punk::ansi::ta::get_codes_single $rawoverrides]
set overrides [list {*}$overrides {*}$rawoverridecodes]
}
set codestack [list]
if {[punk::ansi::ta::detect $text]} {
set emit ""
#set parts [punk::ansi::ta::split_codes $text]
set parts [punk::ansi::ta::split_codes_single $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 [punk::ansi::codetype::sgr_merge_singles [list $base {*}$codestack {*}$overrides]] $pt $R
#append emit [lindex $o_codestack 0]$pt
append emit [punk::ansi::codetype::sgr_merge_singles [list {*}$baselist {*}$codestack {*}$overrides]] $pt $R
}
}
default {
append emit [punk::ansi::codetype::sgr_merge_singles [list {*}$baselist {*}$codestack {*}$overrides]] $pt $R
}
}
#parts ends on a pt - last code always empty string
if {$code ne ""} {
@ -3889,6 +3944,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
} else {
return $base$text$R
}
}
proc ansiwrap_naive {codes text} {
return [a_ {*}$codes]$text[a]

3
src/vfs/_vfscommon.vfs/modules/punk/ansi/colourmap-0.1.0.tm

@ -49,9 +49,6 @@ package require Tcl 8.6-
#*** !doctools
#[item] [package {Tcl 8.6}]
# #package require frobz
# #*** !doctools
# #[item] [package {frobz}]
#*** !doctools
#[list_end]

3053
src/vfs/_vfscommon.vfs/modules/punk/args-0.2.tm

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

2
src/vfs/_vfscommon.vfs/modules/punk/args/tkcore-0.1.0.tm

@ -400,7 +400,7 @@ tcl::namespace::eval punk::args::tkcore {
-disabledforeground\
-font\
-foreground|-fg\
-highligthbackground\
-highlightbackground\
-highlightcolor\
-highlightthickness\
-image\

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

@ -449,7 +449,7 @@ tcl::namespace::eval punk::config {
Accepts globs eg XDG*"
@leaders -min 1 -max 1
#todo - load more whichconfig choices?
whichconfig -type string -choices {config startup-configuration running-configuration}
whichconfig -type any -choices {config startup-configuration running-configuration}
@values -min 0 -max -1
globkey -type string -default * -optional 1 -multiple 1
}]
@ -495,7 +495,7 @@ tcl::namespace::eval punk::config {
@cmd -name punk::config::configure -help\
"Get/set configuration values from a config"
@leaders -min 1 -max 1
whichconfig -type string -choices {defaults startup-configuration running-configuration}
whichconfig -type any -choices {defaults startup-configuration running-configuration}
@values -min 0 -max 2
key -type string -optional 1
newvalue -optional 1

176
src/vfs/_vfscommon.vfs/modules/punk/lib-0.1.2.tm

@ -347,7 +347,7 @@ tcl::namespace::eval punk::lib::compat {
proc ledit {lvar first last args} {
upvar $lvar l
#use lindex_resolve to support for example: ledit lst end+1 end+1 h i
set fidx [punk::lib::lindex_resolve $l $first]
set fidx [punk::lib::lindex_resolve [llength $l] $first]
switch -exact -- $fidx {
-3 {
#index below lower bound
@ -363,7 +363,7 @@ tcl::namespace::eval punk::lib::compat {
set pre [lrange $l 0 $first-1]
}
}
set lidx [punk::lib::lindex_resolve $l $last]
set lidx [punk::lib::lindex_resolve [llength $l] $last]
switch -exact -- $lidx {
-3 {
#index below lower bound
@ -741,14 +741,15 @@ namespace eval punk::lib {
proc lswap {lvar a z} {
upvar $lvar l
if {[lindex_resolve_basic $l $a] < 0 || [lindex_resolve_basic $l $z] < 0} {
set len [llength $l]
if {[lindex_resolve_basic $len $a] < 0 || [lindex_resolve_basic $len $z] < 0} {
#lindex_resolve_basic returns only -1 if out of range
#if we didn't do this check - we could raise an error on second lset below - leaving list corrupted because only one lset occurred
#(e.g using: lswap mylist end-2 end on a two element list)
#on the unhapy path we can take time to check the nature of the out-of-boundness to give a nicer report
#use full 'lindex_resolve' which can report which side via -3 and -2 special results being lower and upper bound breaches respectively (-1 never returned)
set a_index [lindex_resolve $l $a]
set a_index [lindex_resolve $len $a]
set a_msg ""
switch -- $a_index {
-2 {
@ -758,7 +759,7 @@ namespace eval punk::lib {
set a_msg "1st supplied index $a is below the lower bound for the list (0)"
}
}
set z_index [lindex_resolve $l $z]
set z_index [lindex_resolve $len $z]
set z_msg ""
switch -- $z_index {
-2 {
@ -1146,7 +1147,7 @@ namespace eval punk::lib {
- then the normal = separator will be replaced with a coloured (or underlined if colour off) 'mismatch' indicator.
e.g4 set list {{k1 v1 k2 v2} {k1 vv1 k2 vv2}}; pdict list @0-end/@@k2 @*/@@k1
Here we supply 2 separate pattern hierarchies, where @0-end and @* are list operations and are equivalent
The second level segement in each pattern switches to a dict operation to retrieve the value by key.
The second level segment in each pattern switches to a dict operation to retrieve the value by key.
When a list operation such as @* is used - integer list indexes are displayed on the left side of the = for that hierarchy level.
}
}]
@ -1514,7 +1515,7 @@ namespace eval punk::lib {
if {![regexp $re_idxdashidx $p _match a b]} {
error "unrecognised pattern $p"
}
set lower_resolve [punk::lib::lindex_resolve $dval $a] ;#-2 for too low, -1 for too high
set lower_resolve [punk::lib::lindex_resolve [llength $dval] $a] ;#-2 for too low, -1 for too high
#keep lower_resolve as separate var to lower for further checks based on which side out-of-bounds
if {${lower_resolve} == -2} {
##x
@ -1527,7 +1528,7 @@ namespace eval punk::lib {
} else {
set lower $lower_resolve
}
set upper [punk::lib::lindex_resolve $dval $b]
set upper [punk::lib::lindex_resolve [llength $dval] $b]
if {$upper == -3} {
##x
#upper bound is below list range -
@ -1880,7 +1881,8 @@ namespace eval punk::lib {
if {$last_hidekey} {
append result \n
}
append result [textblock::join_basic -- $kblock $sblock $vblock] \n
#append result [textblock::join_basic -- $kblock $sblock $vblock] \n
append result [textblock::join_basic_raw $kblock $sblock $vblock] \n
}
set last_hidekey $hidekey
incr kidx
@ -2240,18 +2242,19 @@ namespace eval punk::lib {
}
}
# showdict uses lindex_resolve results -2 & -3 to determine whether index is out of bunds on upper vs lower side
# showdict uses lindex_resolve results -2 & -3 to determine whether index is out of bounds on upper vs lower side
#REVIEW: This shouldn't really need the list itself - just the length would suffice
punk::args::define {
@id -id ::punk::lib::lindex_resolve
@cmd -name punk::lib::lindex_resolve\
-summary\
"Resolve an indexexpression to an integer based on supplied list."\
"Resolve an indexexpression to an integer based on supplied list or string length."\
-help\
"Resolve an index which may be of the forms accepted by Tcl list commands such as end-2 or 2+2
to the actual integer index for the supplied list, or to a negative value below -1 indicating
whether the index was below or above the range of possible indices for the list.
"Resolve an index which may be of the forms accepted by Tcl list or string commands such as end-2 or 2+2
to the actual integer index for the supplied list/string length, or to a negative value below -1 indicating
whether the index was below or above the range of possible indices for the length supplied.
Users may define procs which accept a list index and wish to accept the forms understood by Tcl.
Users may define procs which accept a list/string index and wish to accept the forms understood by Tcl.
This means the proc may be called with something like $x+2 end-$y etc
Sometimes the actual integer index is desired.
@ -2261,33 +2264,33 @@ namespace eval punk::lib {
a) -3 if the supplied index expression is below the lower bound for the supplied list. (< 0)
b) -2 if the supplied index expression is above the upper bound for the supplied list. (> end)
lindex_resolve never returns -1 - as the similar function lindex_resolve_basic uses this to denote
out of range at either end of the list
Otherwise it will return an integer corresponding to the position in the list.
This is in stark contrast to Tcl list function indices which will return empty strings for out of
out of range at either end of the list/string.
Otherwise it will return an integer corresponding to the position in the data.
This is in stark contrast to Tcl list/string function indices which will return empty strings for out of
bounds indices, or in the case of lrange, return results anyway.
Like Tcl list commands - it will produce an error if the form of the index is not acceptable
For empty lists, end and end+x indices are considered to be out of bounds on the upper side
Like Tcl list commands - it will produce an error if the form of the index is not acceptable.
For empty lists/string (datalength 0), end and end+x indices are considered to be out of bounds on the upper side
- thus returning -2
Note that for an index such as $x+1 - we never see the '$x' as it is substituted in the calling command.
We will get something like 10+1 - which can be resolved safely with expr
"
@values -min 2 -max 2
list -type list
datalength -type integer
index -type indexexpression
}
proc lindex_resolve {list index} {
proc lindex_resolve {len index} {
#*** !doctools
#[call [fun lindex_resolve] [arg list] [arg index]]
#[para]Resolve an index which may be of the forms accepted by Tcl list commands such as end-2 or 2+2 to the actual integer index for the supplied list
#[para]Users may define procs which accept a list index and wish to accept the forms understood by Tcl.
#[call [fun lindex_resolve] [arg len] [arg index]]
#[para]Resolve an index which may be of the forms accepted by Tcl list commands such as end-2 or 2+2 to the actual integer index for the supplied list/string length
#[para]Users may define procs which accept a list/string index and wish to accept the forms understood by Tcl.
#[para]This means the proc may be called with something like $x+2 end-$y etc
#[para]Sometimes the actual integer index is desired.
#[para]We want to resolve the index used, without passing arbitrary expressions into the 'expr' function - which could have security risks.
#[para]lindex_resolve will parse the index expression and return:
#[para] a) -3 if the supplied index expression is below the lower bound for the supplied list. (< 0)
#[para] b) -2 if the supplied index expression is above the upper bound for the supplied list. (> end)
#[para] We don't return -1 - as the similar function lindex_resolve_basic uses this to denote out of range at either end of the list
#[para] We don't return -1 - as the similar function lindex_resolve_basic uses this to denote out of range at either end of the list/string
#[para]Otherwise it will return an integer corresponding to the position in the list.
#[para]This is in stark contrast to Tcl list function indices which will return empty strings for out of bounds indices, or in the case of lrange, return results anyway.
#[para]Like Tcl list commands - it will produce an error if the form of the index is not acceptable
@ -2298,12 +2301,16 @@ namespace eval punk::lib {
# #review
# return ???
#}
if {![string is integer -strict $len]} {
#<0 ?
error "lindex_resolve len must be an integer"
}
set index [tcl::string::map {_ {}} $index] ;#forward compatibility with integers such as 1_000
if {[string is integer -strict $index]} {
#can match +i -i
if {$index < 0} {
return -3
} elseif {$index >= [llength $list]} {
} elseif {$index >= $len} {
return -2
} else {
#integer may still have + sign - normalize with expr
@ -2320,7 +2327,7 @@ namespace eval punk::lib {
}
} else {
#index is 'end'
set index [expr {[llength $list]-1}]
set index [expr {$len-1}]
if {$index < 0} {
#special case - 'end' with empty list - treat end like a positive number out of bounds
return -2
@ -2329,7 +2336,7 @@ namespace eval punk::lib {
}
}
if {$offset == 0} {
set index [expr {[llength $list]-1}]
set index [expr {$len-1}]
if {$index < 0} {
return -2 ;#special case as above
} else {
@ -2337,7 +2344,7 @@ namespace eval punk::lib {
}
} else {
#by now, if op = + then offset = 0 so we only need to handle the minus case
set index [expr {([llength $list]-1) - $offset}]
set index [expr {($len-1) - $offset}]
}
if {$index < 0} {
return -3
@ -2362,33 +2369,32 @@ namespace eval punk::lib {
}
if {$index < 0} {
return -3
} elseif {$index >= [llength $list]} {
} elseif {$index >= $len} {
return -2
}
return $index
}
}
}
proc lindex_resolve_basic {list index} {
proc lindex_resolve_basic {len index} {
#*** !doctools
#[call [fun lindex_resolve_basic] [arg list] [arg index]]
#[call [fun lindex_resolve_basic] [arg len] [arg index]]
#[para] Accepts index of the forms accepted by Tcl's list commands. (e.g compound indices such as 3+1 end-2)
#[para] returns -1 for out of range at either end, or a valid integer index
#[para] Unlike lindex_resolve; lindex_resolve_basic can't determine if an out of range index was out of range at the lower or upper bound
#[para] This is only likely to be faster than average over lindex_resolve for Tcl which has the builtin lseq command
#[para] This is only likely to be faster than average over lindex_resolve for small lists and for Tcl which has the builtin lseq command
#[para] The performance advantage is more likely to be present when using compound indexes such as $x+1 or end-1
#[para] For pure integer indices the performance should be equivalent
#set indices [list] ;#building this may be somewhat expensive in terms of storage and compute for large lists - we could use lseq in Tcl 8.7+
# - which
#for {set i 0} {$i < [llength $list]} {incr i} {
# lappend indices $i
#}
if {![string is integer -strict $len]} {
error "lindex_resolve_basic len must be an integer"
}
set index [tcl::string::map {_ {}} $index] ;#forward compatibility with integers such as 1_000
if {[string is integer -strict $index]} {
#can match +i -i
#avoid even the lseq overhead when the index is simple
if {$index < 0 || ($index >= [llength $list])} {
if {$index < 0 || ($index >= $len)} {
#even though in this case we could return -2 or -3 like lindex_resolve; for consistency we don't, as it's not always determinable for compound indices using the lseq method.
return -1
} else {
@ -2396,13 +2402,15 @@ namespace eval punk::lib {
return [expr {$index}]
}
}
if {[llength $list]} {
set indices [punk::lib::range 0 [expr {[llength $list]-1}]] ;# uses lseq if available, has fallback.
#if lseq was available - $indices is an 'arithseries' - theoretically not taking up ram(?)
if {$len > 0} {
#For large len - this is a wasteful allocation if no true lseq available in Tcl version.
#lseq produces an 'arithseries' object which we can index into without allocating an entire list (REVIEW)
set testlist [punk::lib::range 0 [expr {$len-1}]] ;# uses lseq if available, has fallback.
} else {
set indices [list]
set testlist [list]
#we want to call 'lindex' even in this case - to get the appropriate error message
}
set idx [lindex $indices $index]
set idx [lindex $testlist $index]
if {$idx eq ""} {
#we have no way to determine if out of bounds is at lower vs upper end
return -1
@ -2421,6 +2429,81 @@ namespace eval punk::lib {
}
}
proc string_splitbefore {str index} {
if {![string is integer -strict $index]} {
set index [punk::lib::lindex_resolve [string length $str] $index]
switch -- $index {
-2 {
return [list $str ""]
}
-3 {
return [list "" $str]
}
}
}
return [list [string range $str 0 $index-1] [string range $str $index end]]
#scan %s stops at whitespace - not useful here.
#scan $s %${p}s%s
}
proc string_splitbefore_indices {str args} {
set parts [list $str]
set sizes [list [string length $str]]
set s 0
foreach index $args {
if {![string is integer -strict $index]} {
set index [punk::lib::lindex_resolve [string length $str] $index]
switch -- $index {
-2 {
if {[lindex $sizes end] != 0} {
ledit parts end end [lindex $parts end] {}
ledit sizes end end [lindex $sizes end] 0
}
continue
}
-3 {
if {[lindex $sizes 0] != 0} {
ledit parts 0 0 {} [lindex $parts 0]
ledit sizes 0 0 0 [lindex $sizes 0]
}
continue
}
}
}
if {$index <= 0} {
if {[lindex $sizes 0] != 0} {
ledit parts 0 0 {} [lindex $parts 0]
ledit sizes 0 0 0 [lindex $sizes 0]
}
continue
}
if {$index >= [string length $str]} {
if {[lindex $sizes end] != 0} {
ledit parts end end [lindex $parts end] {}
ledit sizes end end [lindex $sizes end] 0
}
continue
}
set i -1
set a 0
foreach sz $sizes {
incr i
if {$a + $sz > $index} {
set p [lindex $parts $i]
#puts "a:$a index:$index"
if {$a == $index} {
break
}
ledit parts $i $i [string range $p 0 [expr {$index -$a -1}]] [string range $p $index-$a end]
ledit sizes $i $i [expr {$index - $a}] [expr {($a + $sz)-$index}]
break
}
incr a $sz
}
#puts "->parts:$parts"
#puts "->sizes:$sizes"
}
return $parts
}
proc K {x y} {return $x}
#*** !doctools
@ -3250,8 +3333,7 @@ namespace eval punk::lib {
#Each resulting line should have a reset of some type at start and a pure-reset at end to stop
#see if we can find an ST sequence that most terminals will not display for marking sections?
if {$opt_ansireplays} {
#package require punk::ansi
<require_punk_ansi>
<require_punk_ansi> ;#package require punk::ansi
if {$opt_ansiresets} {
set RST "\x1b\[0m"
} else {

121
src/vfs/_vfscommon.vfs/modules/punk/libunknown-0.1.tm

@ -129,7 +129,6 @@ tcl::namespace::eval punk::libunknown {
#whie it is not the most common configuration - a package could be provided both as a .tm and by packageIndex.tcl files
}
#variable paths
upvar ::tcl::tm::paths paths
#variable pkgpattern
@ -475,6 +474,9 @@ tcl::namespace::eval punk::libunknown {
#question is whether some pkgIndex.tcl files may do a package forget? They probably don't/shouldn't(?) Does that matter here anyway?
set before_dict [dict create]
#J2
#siblings that have been affected by source scripts - need to retest ifneeded scripts at end for proper ordering.
set refresh_dict [dict create]
#Note that autopath is being processed from the end to the front
#ie last lappended first. This means if there are duplicate versions earlier in the list,
@ -487,6 +489,7 @@ tcl::namespace::eval punk::libunknown {
while {[llength $use_path]} {
set dir [lindex $use_path end]
# Make sure we only scan each directory one time.
if {[info exists tclSeenPath($dir)]} {
set use_path [lrange $use_path 0 end-1]
@ -526,7 +529,6 @@ tcl::namespace::eval punk::libunknown {
#if {$has_zipfs && [string match $zipfsroot* $dir]} {
#static auto_path dirs
if {!$must_scan} {
#can avoid scan if added via this path in any epoch
if {[dict exists $epoch pkg epochs $pkg_epoch added $currentsearchpath]} {
if {![dict exists $epoch pkg epochs $pkg_epoch added $currentsearchpath $name]} {
#$name wasn't provided by this path before - in static zipfs it shouldn't be necessary to look here again.
@ -551,7 +553,13 @@ tcl::namespace::eval punk::libunknown {
#(or misordered min max e.g package require md5 1-0 i.e a deliberately unsatisfiable version range)
set sourced 0
set just_added [dict create]
set just_changed [dict create]
#set sourced_files [list]
#J2
#set can_skip_sourcing 0
if {!$can_skip_sourcing} {
#Note - naive comparison of before_pkgs vs after_pkgs isn't quite enough to tell if something was added. It could have added a version.
#this will stop us rescanning everything properly by doing a 'package require nonexistant'
@ -577,7 +585,7 @@ tcl::namespace::eval punk::libunknown {
set dir [file dirname $file]
if {![info exists procdDirs($dir)]} {
try {
#if {[string match //zipfs* $file]} {
#if {[string match //zipfs*registry* $file]} {
# puts stderr "----->0 sourcing zipfs file $file"
#}
incr sourced ;#count as sourced even if source fails; keep before actual source action
@ -642,15 +650,16 @@ tcl::namespace::eval punk::libunknown {
}
set after_pkgs [package names]
set just_added [dict create]
#puts "@@@@pkg epochs $pkg_epoch searchpath:$currentsearchpath name:$name before: [llength $before_pkgs] after: [llength $after_pkgs]"
if {[llength $after_pkgs] > [llength $before_pkgs]} {
foreach a $after_pkgs {
foreach v [package versions $a] {
if {![dict exists $before_dict $a $v]} {
dict set just_added $a $v 1
set iscript [package ifneeded $a $v]
#J2
#dict set epoch pkg epochs $pkg_epoch added $currentsearchpath $a [dict create e $pkg_epoch v $v]
dict set epoch pkg epochs $pkg_epoch added $currentsearchpath $a $v e$pkg_epoch
dict set epoch pkg epochs $pkg_epoch added $currentsearchpath $a $v [dict create e $pkg_epoch scr $iscript]
if {$must_scan} {
dict unset epoch pkg untracked $a
}
@ -693,18 +702,20 @@ tcl::namespace::eval punk::libunknown {
#The last one found (earlier in auto_path) for a version is the one that supplies the final 'package provide' statement (by overriding it)
#
dict for {bp bpversionscripts} $before_dict {
if {!$must_scan && ![dict exists $epoch pkg epochs $pkg_epoch added $currentsearchpath $bp]} {
#puts -nonewline .
continue
}
#if {!$must_scan && ![dict exists $epoch pkg epochs $pkg_epoch added $currentsearchpath $bp]} {
# #puts -nonewline .
# continue
#}
dict for {bv bscript} $bpversionscripts {
set nowscript [package ifneeded $bp $bv]
if {$bscript ne $nowscript} {
#ifneeded script has changed. The same version of bp was supplied on this path.
#As it's processed later - it will be the one in effect.
#dict set epoch pkg epochs $pkg_epoch added $currentsearchpath $bp [dict create e $pkg_epoch v $bv]
dict set epoch pkg epochs $pkg_epoch added $currentsearchpath $bp $bv e$pkg_epoch
dict set epoch pkg epochs $pkg_epoch added $currentsearchpath $bp $bv [dict create e $pkg_epoch scr $nowscript]
dict set before_dict $bp $bv $nowscript
dict set just_changed $bp $bv 1
#j2
if {$must_scan} {
dict unset epoch pkg untracked $bp
}
@ -806,7 +817,63 @@ tcl::namespace::eval punk::libunknown {
}
}
set old_path $auto_path
dict for {pkg versions} $just_changed {
foreach v [dict keys $versions] {
dict set refresh_dict $pkg $v 1
}
}
dict for {pkg versions} $just_added {
foreach v [dict keys $versions] {
dict set refresh_dict $pkg $v 1
}
}
}
#refresh ifneeded scripts for just_added/just_changed
#review: searchpaths are in auto_path order - earliest has precedence for any particular pkg-version
#REVIEW: what is to stop an auto_path package e.g from os, overriding a .tm ifneeded script from an item earlier in the package_mode list configured in punk's main.tcl?
#e.g when package_mode is {dev-os} we don't want a pkgIndex package from ::env(TCLLIBPATH) overriding a .tm from the dev paths (even if version nums the same)
#conversely we do want a dev path pkIndex package overriding an existing ifneeded script from a .tm in os
#to accomodate this - we may need to maintain a subdict in epoch of paths/path-prefixes to package_mode members os, dev, internal
#this 'refresh' is really a 'reversion' to what was already stored in epoch pkg epochs <currente> added
#
set e [dict get $epoch pkg current]
set pkgvdone [dict create]
set dict_added [dict get $epoch pkg epochs $e added]
#keys are in reverse order due to tclPkgUnknown processing order
set ordered_searchpaths [lreverse [dict keys $dict_added]];# ordered as in auto_path
dict for {pkg versiond} $refresh_dict {
set versions [dict keys $versiond]
foreach searchpath $ordered_searchpaths {
set addedinfo [dict get $dict_added $searchpath]
set vidx -1
foreach v $versions {
incr vidx
if {[dict exists $addedinfo $pkg $v]} {
ledit versions $vidx $vidx
set iscript [dict get $addedinfo $pkg $v scr]
#todo - find the iscript in the '$epoch pkg epochs <e> added paths' lists and determine os vs dev vs internal
#(scanning for path directly in the ifneeded script for pkgs is potentially error prone)
#for .tm ifneeded scripts - the syntax is simple enough to determine directly (and ifneeded scr not stored for those anyway)
if {[package ifneeded $pkg $v] ne $iscript} {
#puts "---->refreshing $pkg $v - reverting to already stored from path:$searchpath"
package ifneeded $pkg $v $iscript
#dict set pkgvdone $pkg $v 1
}
}
}
if {[llength $versions] == 0} {
break
}
}
}
#puts "zipfs_tclPkgUnknown DONE"
}
variable last_auto_path
@ -1091,7 +1158,7 @@ tcl::namespace::eval punk::libunknown {
set callerposn [lsearch $args -caller]
if {$callerposn > -1} {
set caller [lindex $args $callerposn+1]
puts stderr "\x1b\[1\;33m punk::libunknown::init - caller:$caller\x1b\[m"
#puts stderr "\x1b\[1\;33m punk::libunknown::init - caller:$caller\x1b\[m"
#puts stderr "punk::libunknown::init auto_path : $::auto_path"
#puts stderr "punk::libunknown::init tcl::tm::list: [tcl::tm::list]"
}
@ -1176,15 +1243,15 @@ tcl::namespace::eval punk::libunknown {
#update the epoch info with where the tm versions came from
#(not tracking version numbers in epoch - just package to the indexbase)
foreach vdata $versionlist {
lassign $vdata v _t type _index index _indexbase indexbase
lassign $vdata v _t type _index index _indexbase indexbase _script iscript
if {$type eq "tm"} {
if {![dict exists $epoch tm epochs 0 added $indexbase]} {
#dict set epoch tm epochs 0 added $indexbase [dict create $p [dict create e 0 v $v]]
dict set epoch tm epochs 0 added $indexbase $p $v e0
dict set epoch tm epochs 0 added $indexbase $p $v [dict create e 0 scr $iscript]
} else {
set idxadded [dict get $epoch tm epochs 0 added $indexbase]
#dict set idxadded $p [dict create e 0 v $v]
dict set idxadded $p $v e0
dict set idxadded $p $v [dict create e 0 scr $iscript]
dict set epoch tm epochs 0 added $indexbase $idxadded
}
dict unset epoch tm untracked $p
@ -1395,7 +1462,9 @@ tcl::namespace::eval punk::libunknown {
#}
if {![interp issafe]} {
#J2
package unknown {::punk::libunknown::zipfs_tm_UnknownHandler ::punk::libunknown::zipfs_tclPkgUnknown}
#package unknown {::punk::libunknown::zipfs_tm_UnknownHandler ::tclPkgUnknown}
}
}
@ -1426,10 +1495,26 @@ tcl::namespace::eval punk::libunknown {
dict set r_added $path [dict get $epoch pkg epochs $pkg_epoch added $path]
}
#set pkg_added [punk::lib::showdict [dict get $epoch pkg epochs $pkg_epoch added] */$pkgname]
set pkg_added [punk::lib::showdict $r_added */$pkgname]
set title "PKG epoch $pkg_epoch - added"
set added [textblock::frame -title $title $pkg_added]
#set pkg_added [punk::lib::showdict $r_added */$pkgname]
#set added [textblock::frame -title $title $pkg_added]
set rows [list]
dict for {path pkgs} $r_added {
set c1 $path
set c2 [dict size $pkgs]
set c3 ""
if {[dict exists $pkgs $pkgname]} {
set vdict [dict get $pkgs $pkgname]
dict for {v data} $vdict {
set scriptlen [string length [dict get $data scr]]
append c3 "$v epoch[dict get $data e] ifneededchars:$scriptlen" \n
}
}
set r [list $path $c2 $c3]
lappend rows $r
}
set title "[punk::ansi::a+ green] PKG epoch $pkg_epoch - added [punk::ansi::a]"
set added [textblock::table -title $title -headers [list Path Pkgcount $pkgname] -rows $rows]
set pkg_row $added

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

@ -177,10 +177,10 @@ tcl::namespace::eval punk::ns {
} else {
set fq_nspath $nspath
}
if {[catch {nseval_ifexists $fq_nspath {}}]} {
return 0
} else {
if {[nseval_ifexists $fq_nspath {::string cat ok}] eq "ok"} {
return 1
} else {
return 0
}
}
@ -758,13 +758,20 @@ tcl::namespace::eval punk::ns {
}
set marks [dict create oo \u25c6 ooc \u25c7 ooo \u25c8 punkargs \U1f6c8 ensemble \u24ba native \u24c3 unknown \U2370]
if {[llength $ansinames]} {
return "[punk::ansi::a+ {*}$ansinames][dict get $marks $type][punk::ansi::a]"
return "[punk::ansi::a+ {*}$ansinames][dict get $marks $type]\x1b\[0m"
} else {
return [dict get $marks $type]
}
}
#REVIEW - ansi codes can be *very* confusing to the user when trying to handle lists etc..
punk::args::define {
@id -id ::punk::ns::get_nslist
@cmd -name punk::ns::get_nslist
@opts
-match -default ""
-nsdict -type dict -default {}
}
proc get_nslist {args} {
set known_types [list children commands exported imported aliases procs ensembles ooclasses ooobjects ooprivateobjects ooprivateclasses native coroutines interps zlibstreams]
set defaults [dict create\
@ -775,6 +782,9 @@ tcl::namespace::eval punk::ns {
set opts [dict merge $defaults $args]
# -- --- --- --- --- --- --- --- --- ---
set fq_glob [dict get $opts -match]
if {$fq_glob eq ""} {
set fq_glob [uplevel 1 nsthis]::*
}
set requested_types [dict get $opts -types]
set opt_nsdict [dict get $opts -nsdict]
@ -835,7 +845,7 @@ tcl::namespace::eval punk::ns {
set zlibstreams [list]
set usageinfo [list]
if {$opt_nsdict eq ""} {
if {![dict size $opt_nsdict]} {
set nsmatches [get_ns_dicts $fq_glob -allbelow 0]
set itemcount 0
set matches_with_results [list]
@ -867,6 +877,8 @@ tcl::namespace::eval punk::ns {
}
if {"commands" in $types} {
set commands [dict get $contents commands]
}
set usageinfo [dict get $contents usageinfo]
foreach t $types {
switch -- $t {
exported {
@ -910,8 +922,6 @@ tcl::namespace::eval punk::ns {
}
}
}
set usageinfo [dict get $contents usageinfo]
}
set numchildren [llength $children]
if {$numchildren} {
@ -1068,7 +1078,7 @@ tcl::namespace::eval punk::ns {
} else {
}
if {$cmd in $imported} {
set prefix [overtype::right $prefix "-[a+ yellow bold]I[a+]"]
set prefix [overtype::right $prefix "-[a+ yellow bold]I[a]"]
}
}
if {$cmd in $usageinfo} {
@ -1076,7 +1086,8 @@ tcl::namespace::eval punk::ns {
} else {
set u ""
}
set cmd$i "${prefix} $c$cmd_display$u"
#set cmd$i "${prefix} $c$cmd_display$u"
set cmd$i "${prefix} [punk::ansi::ansiwrap -rawansi $c $cmd_display]$u"
#set c$i $c
set c$i ""
lappend seencmds $cmd
@ -1147,7 +1158,11 @@ tcl::namespace::eval punk::ns {
the child namespaces and commands within
the namespace(s) matched by glob."
@opts
-nspathcommands -type boolean -default 0
-nspathcommands -type boolean -default 0 -help\
"When a namespace has entries configured in 'namespace path', the default result for nslist
will display just a basic note: 'Also resolving cmds in namespace paths: <namespaces>'.
If -nspathcommands is true, it will also display subtables showing the commands resolvable
via any such listed namespaces."
-types
@values -min 0 -max -1
glob -multiple 1 -optional 1 -default "*"
@ -1206,9 +1221,9 @@ tcl::namespace::eval punk::ns {
if {[dict size [dict get $nsdict namespacepath]]} {
set path_text ""
if {!$opt_nspathcommands} {
append path_text \n " also resolving cmds in namespace paths: [dict keys [dict get $nsdict namespacepath]]"
append path_text \n " Also resolving cmds in namespace paths: [dict keys [dict get $nsdict namespacepath]]"
} else {
append path_text \n " also resolving cmds in namespace paths:"
append path_text \n " Also resolving cmds in namespace paths:"
set nspathdict [dict get $nsdict namespacepath]
if {!$has_textblock} {
dict for {k v} $nspathdict {
@ -1217,8 +1232,14 @@ tcl::namespace::eval punk::ns {
append path_text \n " cmds: $cmds"
}
} else {
#todo - change to display in column order to be same as main command listing
dict for {k v} $nspathdict {
set t [textblock::list_as_table -title $k -columns 6 [lsort [dict get $v commands]]]
set pathcommands [dict get $v commands]
set columns 6
if {[llength $pathcommands] < 6} {
set columns [llength $v]
}
set t [textblock::list_as_table -title $k -columns $columns [lsort $pathcommands]]
append path_text \n $t
}
}
@ -3682,6 +3703,21 @@ tcl::namespace::eval punk::ns {
comment inserted to display information such as the
namespace origin. Such a comment begins with #corp#."
@opts
-syntax -default basic -choices {none basic}\
-choicelabels {
none\
" Plain text output"
basic\
" Comment and bracket highlights.
This is a basic colourizer - not
a full Tcl syntax highlighter."
}\
-help\
"Type of syntax highlighting on result.
Note that -syntax none will always return a proper Tcl
List: proc <name> <arglist> <body>
- but a syntax highlighter may return a string that
is not a Tcl list."
@values -min 1 -max -1
commandname -help\
"May be either the fully qualified path for the command,
@ -3690,7 +3726,8 @@ tcl::namespace::eval punk::ns {
}
proc corp {args} {
set argd [punk::args::parse $args withid ::punk::ns::corp]
set path [dict get $argd values commandname]
set path [dict get $argd values commandname]
set syntax [dict get $argd opts -syntax]
#thanks to Richard Suchenwirth for the original - wiki.tcl-lang.org/page/corp
#Note: modified here to support aliases and relative/absolute name (with respect to namespace .ie ::name vs name)
if {[info exists punk::console::tabwidth]} {
@ -3775,7 +3812,19 @@ tcl::namespace::eval punk::ns {
lappend argl $a
}
#list proc [nsjoin ${targetns} $name] $argl $body
list proc $resolved $argl $body
switch -- $syntax {
basic {
#rudimentary colourising only
set argl [punk::grepstr -return all -highlight tk-darkcyan {\{|\}} $argl]
set body [punk::grepstr -return all -highlight green {^\s*#.*} $body] ;#Note, will not highlight comments at end of line - like this one.
set body [punk::grepstr -return all -highlight tk-darkcyan {\{|\}} $body]
set body [punk::grepstr -return all -highlight tk-orange {\[|\]} $body]
#ansi colourised items in list format may not always have desired string representation (list escaping can occur)
#return as a string - which may not be a proper Tcl list!
return "proc $resolved {$argl} {\n$body\n}"
}
}
list proc $resolved $argl $body
}

53
src/vfs/_vfscommon.vfs/modules/punk/packagepreference-0.1.0.tm

@ -194,7 +194,7 @@ tcl::namespace::eval punk::packagepreference {
if {!$is_exact && [llength $vwant] <= 1 } {
#required version unspecified - or specified singularly
set available_versions [$COMMANDSTACKNEXT_ORIGINAL versions $pkg]
if {[llength $available_versions] > 1} {
if {[llength $available_versions] >= 1} {
# ---------------------------------------------------------------
#An attempt to detect dll/so loaded and try to load same version
#dll/so files are often named with version numbers that don't contain dots or a version number at all
@ -202,9 +202,11 @@ tcl::namespace::eval punk::packagepreference {
set pkgloadedinfo [lsearch -nocase -inline -index 1 [info loaded] $pkg]
if {[llength $pkgloadedinfo]} {
puts stderr "--> pkg not already 'provided' but shared object seems to be loaded: $pkgloadedinfo - and multiple versions available"
lassign $pkgloadedinfo path name
set lcpath [string tolower $path]
if {[llength $available_versions] > 1} {
puts stderr "--> pkg $pkg not already 'provided' but shared object seems to be loaded: $pkgloadedinfo - and [llength $available_versions] versions available"
}
lassign $pkgloadedinfo loaded_path name
set lc_loadedpath [string tolower $loaded_path]
#first attempt to find a match for our loaded sharedlib path in a *simple* package ifneeded statement.
set lcpath_to_version [dict create]
foreach av $available_versions {
@ -212,17 +214,19 @@ tcl::namespace::eval punk::packagepreference {
#ifneeded script not always a valid tcl list
if {![catch {llength $scr} scrlen]} {
if {$scrlen == 3 && [lindex $scr 0] eq "load" && [string match -nocase [lindex $scr 2] $pkg]} {
#a basic 'load <path> <pkg>' statement
dict set lcpath_to_version [string tolower [lindex $scr 1]] $av
}
}
}
if {[dict exists $lcpath_to_version $lcpath]} {
set lversion [dict get $lcpath_to_version $lcpath]
if {[dict exists $lcpath_to_version $lc_loadedpath]} {
set lversion [dict get $lcpath_to_version $lc_loadedpath]
} else {
#fallback to a best effort guess based on the path
set lversion [::punk::packagepreference::system::slibpath_guess_pkgversion $path $pkg]
set lversion [::punk::packagepreference::system::slibpath_guess_pkgversion $loaded_path $pkg]
}
#puts "====lcpath_to_version: $lcpath_to_version"
if {$lversion ne ""} {
#name matches pkg
#hack for known dll version mismatch
@ -232,8 +236,40 @@ tcl::namespace::eval punk::packagepreference {
if {[llength $vwant] == 1} {
#todo - still check vsatisfies - report a conflict? review
}
return [$COMMANDSTACKNEXT require $pkg $lversion-$lversion]
#return [$COMMANDSTACKNEXT require $pkg $lversion-$lversion]
try {
set result [$COMMANDSTACKNEXT require $pkg $lversion-$lversion]
} trap {} {emsg eopts} {
#REVIEW - this occurred in punkmagic (rebuild of tclmagic) - probably due to multiple versions of registry
#under different auto_path folders - and mal-ordering in punk::libunknown's tclPkgUnknown
#May be obsolete.. issue still not clear
#A hack for 'couldn't open "<path.dll>": permission denied'
#This happens for example with the tcl9registry13.dll when loading from zipfs - but not in all systems, and not for all dlls.
#exact cause unknown.
#e.g
#%package ifneeded registry 1.3.7
#- load //zipfs:/app/lib_tcl9/registry1.3/tcl9registry13.dll Registry
#%load //zipfs:/app/lib_tcl9/registry1.3/tcl9registry13.dll Registry
#couldn't open "C:/Users/sleek/AppData/Local/Temp/TCL00003cf8/tcl9registry13.dll": permission denied
#a subsequent load of the path used in the error message works.
#if {[string match "couldn't open \"*\": permission denied" $emsg]} {}
if {[regexp {couldn't open "(.*)":.*permission denied.*} $emsg _ newpath]} {
#Since this is a hack that shouldn't be required - be noisy about it.
puts stderr ">>> $emsg"
puts stderr "punk::packagepreference::require hack: Re-trying load of $pkg with path: $newpath"
return [load $newpath $pkg]
} else {
#puts stderr "??? $emsg"
#dunno - re-raise
return -options $eopts $emsg
}
}
return $result
}
#else puts stderr "> no version determined for pkg: $pkg loaded_path: $loaded_path"
}
}
}
@ -291,6 +327,7 @@ tcl::namespace::eval punk::packagepreference {
#we should be able to load more specific punk::args pkg based on result of [package present $pkg]
catch {
#$COMMANDSTACKNEXT require $pkg {*}$vwant
#j2
$COMMANDSTACKNEXT require punk::args::$dp
}
}

3
src/vfs/_vfscommon.vfs/modules/punk/pipe-1.0.tm

@ -373,6 +373,7 @@ tcl::namespace::eval punk::pipe::lib {
if {$end_var_posn > 0} {
#tcl scan with %s will not handle whitespace as desired. Be explicit using string range instead.
#lassign [scan $token %${end_var_posn}s%s] var spec
#lassign [punk::lib::string_splitbefore $token $end_var_posn] var spec
set var [string range $token 0 $end_var_posn-1]
set spec [string range $token $end_var_posn end] ;#key section includes the terminal char which ended the var and starts the spec
} else {
@ -430,7 +431,7 @@ tcl::namespace::eval punk::pipe::lib {
}
#if {[string length $token]} {
# #lappend varlist [splitstrposn $token $end_var_posn]
# #lappend varlist [punk::lib::string_splitbefore $token $end_var_posn]
# set var $token
# set spec ""
# if {$end_var_posn > 0} {

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

@ -2472,7 +2472,8 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
set h [textblock::height $text]
set promptcol [string repeat $resultprompt\n $h]
set promptcol [string range $promptcol 0 end-1]
rputs [textblock::join_basic -- $promptcol $text]
#rputs [textblock::join_basic -- $promptcol $text]
rputs [textblock::join_basic_raw $promptcol $text]
#puts -nonewline stdout $text
}
@ -2530,7 +2531,8 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
set promptcol [string repeat $resultprompt\n $h]
set promptcol [string range $promptcol 0 end-1]
#promptcol is uniform-width lines, result may not be. We are ok to join with ragged rhs col here, so use join_basic instead of join
rputs [textblock::join_basic -- $promptcol $result]
#rputs [textblock::join_basic -- $promptcol $result]
rputs [textblock::join_basic_raw $promptcol $result]
#orig
#rputs $resultprompt[string map [list \r\n "\n$resultprompt" \n "\n$resultprompt"] $result]
@ -2827,14 +2829,15 @@ namespace eval repl {
package require punk::packagepreference
punk::packagepreference::install
#jjj
package require punk::ansi::colourmap
package require punk::args
#package require Thread
if {[catch {package require thread} errM]} {
puts stdout "initscript lib load fail on package require thread\n$errM"
puts stdout ">>auto_path : $::auto_path"
puts stdout ">>tcl::tm::list: [tcl::tm::list]"
puts stdout ">>repl::init initscript lib load fail on package require thread\n$errM"
puts stdout ">>repl::init auto_path : $::auto_path"
puts stdout ">>repl::init tcl::tm::list: [tcl::tm::list]"
}
#-----

25
src/vfs/_vfscommon.vfs/modules/shellfilter-0.2.tm

@ -735,6 +735,7 @@ namespace eval shellfilter::chan {
variable o_encbuf ;#buffering for partial encoding bytes
variable o_colour
variable o_do_colour
variable o_do_colourlist
variable o_do_normal
variable o_is_junction
variable o_codestack
@ -747,11 +748,17 @@ namespace eval shellfilter::chan {
set settingsdict [tcl::dict::get $tf -settings]
if {[tcl::dict::exists $settingsdict -colour]} {
set o_colour [tcl::dict::get $settingsdict -colour]
set o_do_colour [punk::ansi::a+ {*}$o_colour]
#warning - we can't merge certain extended attributes such as undercurly into single SGR escape sequence
#while some terminals may handle these extended attributes even when merged - we need to cater for those that
#don't. Keeping them as a separate escape allows terminals that don't handle them to ignore just that code without
#affecting the interpretation of the other codes.
set o_do_colour [punk::ansi::a+ {*}$o_colour]
set o_do_colourlist [punk::ansi::ta::get_codes_single $o_do_colour]
set o_do_normal [punk::ansi::a]
} else {
set o_colour {}
set o_do_colour ""
set o_do_colourlist {}
set o_do_normal ""
}
set o_codestack [list]
@ -793,11 +800,11 @@ namespace eval shellfilter::chan {
set o_codestack [list]
} else {
#append emit [lindex $o_codestack 0]$pt
append emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$pt
append emit [punk::ansi::codetype::sgr_merge_singles [list {*}$o_do_colourlist {*}$o_codestack]]$pt
}
}
default {
append emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$pt
append emit [punk::ansi::codetype::sgr_merge_singles [list {*}$o_do_colourlist {*}$o_codestack]]$pt
}
}
#if {( ![llength $o_codestack] || ([llength $o_codestack] == 1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]))} {
@ -864,11 +871,11 @@ namespace eval shellfilter::chan {
set o_codestack [list]
} else {
#append emit [lindex $o_codestack 0]$trailing_pt
append emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$trailing_pt
append emit [punk::ansi::codetype::sgr_merge_singles [list {*}$o_do_colourlist {*}$o_codestack]]$trailing_pt
}
}
default {
append emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$trailing_pt
append emit [punk::ansi::codetype::sgr_merge_singles [list {*}$o_do_colourlist {*}$o_codestack]]$trailing_pt
}
}
#if {![llength $o_codestack] || ([llength $o_codestack] ==1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]])} {
@ -957,12 +964,12 @@ namespace eval shellfilter::chan {
set o_codestack [list]
} else {
#set emit [lindex $o_codestack 0]$buf
set emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$buf
set emit [punk::ansi::codetype::sgr_merge_singles [list {*}$o_do_colourlist {*}$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 [punk::ansi::codetype::sgr_merge_singles [list {*}$o_do_colourlist {*}$o_codestack]]$buf
}
}
#if {( ![llength $o_codestack] || ([llength $o_codestack] == 1 && [punk::ansi::codetype::is_sgr_reset [lindex $o_codestack 0]]))} {
@ -987,12 +994,12 @@ namespace eval shellfilter::chan {
set o_codestack [list]
} else {
#set emit [lindex $o_codestack 0]$buf
set emit [punk::ansi::codetype::sgr_merge_singles [list $o_do_colour {*}$o_codestack]]$buf
set emit [punk::ansi::codetype::sgr_merge_singles [list {*}$o_do_colourlist {*}$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 [punk::ansi::codetype::sgr_merge_singles [list {*}$o_do_colourlist {*}$o_codestack]]$buf
}
}
set o_buffered ""

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

@ -367,7 +367,8 @@ namespace eval shellrun {
lappend chunklist [list "stderr" [dict get $exitinfo errorInfo]]
} else {
set c [a+ Yellow red bold]
lappend chunklist [list "info" "$c$exitinfo$n"]
#lappend chunklist [list "info" "$c$exitinfo$n"]
lappend chunklist [list "info" [punk::ansi::ansiwrap_raw $c \x1b\[m "" $exitinfo]]
}
@ -486,7 +487,7 @@ namespace eval shellrun {
#we raise an error because an error during calling is different to collecting stderr from a command, and the caller should be able to wrap in a catch
# to determine something other than just a nonzero exit code or output on stderr.
if {[dict exists $exitinfo error]} {
if {"-tcl" in $runopts} {
if {[dict exists $received "-tcl"]} {
} else {
#todo - check errorInfo makes sense.. return -code? tailcall?
@ -513,7 +514,8 @@ namespace eval shellrun {
lappend chunklist [list "info" "errorInfo [list [dict get $exitinfo errorInfo]]"]
} else {
set c [a+ Yellow red bold]
lappend chunklist [list "info" "$c$exitinfo$n"]
#lappend chunklist [list "info" "$c$exitinfo$n"]
lappend chunklist [list "info" [punk::ansi::ansiwrap_raw $c "\x1b\[m" "" $exitinfo]]
}

BIN
src/vfs/_vfscommon.vfs/modules/test/punk/args-0.1.5.tm

Binary file not shown.

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

@ -174,7 +174,6 @@ tcl::namespace::eval textblock {
}
}
proc use_hash {args} {
#set argd [punk::args::get_by_id ::textblock::use_hash $args]
set argd [punk::args::parse $args withid ::textblock::use_hash]
variable use_hash
if {![dict exists $argd received hash_algorithm]} {
@ -2314,7 +2313,8 @@ tcl::namespace::eval textblock {
#JMN
#spanned_parts are all built with textblock::frame - therefore uniform-width lines - can use join_basic
set spanned_frame [textblock::join_basic -- {*}$spanned_parts]
#set spanned_frame [textblock::join_basic -- {*}$spanned_parts]
set spanned_frame [textblock::join_basic_raw {*}$spanned_parts]
if {$spans_to_rhs} {
if {$cidx == 0} {
@ -2383,7 +2383,8 @@ tcl::namespace::eval textblock {
} else {
#this_span == 1
set spanned_frame [textblock::join_basic -- $header_cell_startspan]
#set spanned_frame [textblock::join_basic -- $header_cell_startspan]
set spanned_frame [textblock::join_basic_raw $header_cell_startspan]
}
@ -4012,7 +4013,8 @@ tcl::namespace::eval textblock {
set body_build ""
} else {
#body blocks should not be ragged - so can use join_basic
set body_build [textblock::join_basic -- {*}$body_blocks]
#set body_build [textblock::join_basic -- {*}$body_blocks]
set body_build [textblock::join_basic_raw {*}$body_blocks]
}
if {$headerheight > 0} {
set table [tcl::string::cat $header_build \n $body_build]
@ -4169,7 +4171,6 @@ tcl::namespace::eval textblock {
proc periodic {args} {
#For an impressive interactive terminal app (javascript)
# see: https://github.com/spirometaxas/periodic-table-cli
#set opts [dict get [punk::args::get_by_id ::textblock::periodic $args] opts]
set opts [dict get [punk::args::parse $args withid ::textblock::periodic] opts]
set opt_return [tcl::dict::get $opts -return]
if {[tcl::dict::get $opts -forcecolour]} {
@ -4466,7 +4467,7 @@ tcl::namespace::eval textblock {
proc list_as_table {args} {
set FRAMETYPES [textblock::frametypes]
set argd [punk::args::get_by_id ::textblock::list_as_table $args]
set argd [punk::args::parse $args withid ::textblock::list_as_table]
set opts [dict get $argd opts]
set received [dict get $argd received]
@ -4664,7 +4665,8 @@ tcl::namespace::eval textblock {
if {[tcl::string::last \n $charblock] >= 0} {
if {$blockwidth > 1} {
#set row [.= val $charblock {*}[lrepeat [expr {$blockwidth -1}] |> piper_blockjoin $charblock]] ;#building a repeated "|> command arg" list to evaluate as a pipeline. (from before textblock::join could take arbitrary num of blocks )
set row [textblock::join_basic -- {*}[lrepeat $blockwidth $charblock]]
#set row [textblock::join_basic -- {*}[lrepeat $blockwidth $charblock]]
set row [textblock::join_basic_raw {*}[lrepeat $blockwidth $charblock]]
} else {
set row $charblock
}
@ -4714,7 +4716,7 @@ tcl::namespace::eval textblock {
}
proc testblock {args} {
set argd [punk::args::get_by_id ::textblock::testblock $args]
set argd [punk::args::parse $args withid ::textblock::testblock]
set colour [dict get $argd values colour]
set size [dict get $argd opts -size]
@ -4782,7 +4784,8 @@ tcl::namespace::eval textblock {
if {"noreset" in $colour} {
return [textblock::join_basic -ansiresets 0 -- {*}$clist]
} else {
return [textblock::join_basic -- {*}$clist]
#return [textblock::join_basic -- {*}$clist]
return [textblock::join_basic_raw {*}$clist]
}
} elseif {"rainbow" in $colour} {
#direction must be horizontal
@ -5039,19 +5042,20 @@ tcl::namespace::eval textblock {
-width ""\
-overflow 0\
-within_ansi 0\
-return block\
]
#known_samewidth of empty string means we don't know either way, 0 is definitely 'ragged', 1 is definitely homogenous
#review!?
#-within_ansi means after a leading ansi code when doing left pad on all but last line
#-within_ansi means before a trailing ansi code when doing right pad on all but last line
set usage "pad block ?-padchar <sp>|<character>? ?-which right|left|centre? ?-known_hasansi \"\"|<bool>? ?-known_blockwidth \"\"|<int>? ?-width auto|<int>? ?-within_ansi 1|0?"
foreach {k v} $args {
switch -- $k {
-padchar - -which - -known_hasansi - -known_samewidth - -known_blockwidth - -width - -overflow - -within_ansi {
-padchar - -which - -known_hasansi - -known_samewidth - -known_blockwidth - -width - -overflow - -within_ansi - -return {
tcl::dict::set opts $k $v
}
default {
set usage "pad block ?-padchar <sp>|<character>? ?-which right|left|centre? ?-known_hasansi \"\"|<bool>? ?-known_blockwidth \"\"|<int>? ?-width auto|<int>? ?-within_ansi 1|0? ?-return block|list?"
error "textblock::pad unrecognised option '$k'. Usage: $usage"
}
}
@ -5197,96 +5201,110 @@ tcl::namespace::eval textblock {
set line_len 0
set pad_cache [dict create] ;#key on value of 'missing' - which is width of required pad
foreach {pt ansi} $parts {
if {$pt ne ""} {
set has_nl [expr {[tcl::string::last \n $pt]>=0}]
if {$has_nl} {
if {$pt eq ""} {
#we need to store empties in order to insert text in the correct position relative to leading/trailing ansi codes
lappend line_chunks ""
} elseif {[tcl::string::last \n $pt]==-1} {
lappend line_chunks $pt
if {$known_samewidth eq "" || ($known_samewidth ne "" && !$known_samewidth) || $datawidth eq ""} {
incr line_len [punk::char::grapheme_width_cached $pt] ;#memleak - REVIEW
}
} else {
#set has_nl [expr {[tcl::string::last \n $pt]>=0}]
#if {$has_nl} {
set pt [tcl::string::map [list \r\n \n] $pt]
set partlines [split $pt \n]
} else {
set partlines [list $pt]
}
set last [expr {[llength $partlines]-1}]
set p 0
foreach pl $partlines {
lappend line_chunks $pl
#} else {
# set partlines [list $pt]
#}
#set last [expr {[llength $partlines]-1}]
#set p -1
foreach pl [lrange $partlines 0 end-1] {
#incr p
lappend line_chunks $pl ;#we need to lappend because there can already be some pt and ansi entries for the current line from previous {pt ansi} values where pt had no newline.
#incr line_len [punk::char::ansifreestring_width $pl]
#if {$known_samewidth eq "" || ($known_samewidth ne "" && !$known_samewidth) || $datawidth eq ""} {
# incr line_len [punk::char::grapheme_width_cached $pl] ;#memleak - REVIEW
#}
#do padding
if {$known_samewidth eq "" || ($known_samewidth ne "" && !$known_samewidth) || $datawidth eq ""} {
incr line_len [punk::char::grapheme_width_cached $pl] ;#memleak - REVIEW
set missing [expr {$width - $line_len}]
} else {
set missing [expr {$width - $datawidth}]
}
if {$p != $last} {
#do padding
if {$known_samewidth eq "" || ($known_samewidth ne "" && !$known_samewidth) || $datawidth eq ""} {
set missing [expr {$width - $line_len}]
} else {
set missing [expr {$width - $datawidth}]
}
if {$missing > 0} {
#commonly in a block - many lines will have the same pad - cache based on missing
if {$missing > 0} {
#commonly in a block - many lines will have the same pad - cache based on missing
#padchar may be more than 1 wide - because of 2wide unicode and or multiple chars
if {[tcl::dict::exists $pad_cache $missing]} {
set pad [tcl::dict::get $pad_cache $missing]
#padchar may be more than 1 wide - because of 2wide unicode and or multiple chars
if {[tcl::dict::exists $pad_cache $missing]} {
set pad [tcl::dict::get $pad_cache $missing]
} else {
set repeats [expr {int(ceil($missing / double($padcharsize)))}] ;#will overshoot by 1 whenever padcharsize not an exact divisor of width
if {!$pad_has_ansi} {
set pad [tcl::string::range [tcl::string::repeat $padchar $repeats] 0 $missing-1]
} else {
set repeats [expr {int(ceil($missing / double($padcharsize)))}] ;#will overshoot by 1 whenever padcharsize not an exact divisor of width
if {!$pad_has_ansi} {
set pad [tcl::string::range [tcl::string::repeat $padchar $repeats] 0 $missing-1]
} else {
set base [tcl::string::repeat " " $missing]
set pad [overtype::block -blockalign left -overflow 0 $base [tcl::string::repeat $padchar $repeats]]
}
dict set pad_cache $missing $pad
set base [tcl::string::repeat " " $missing]
set pad [overtype::block -blockalign left -overflow 0 $base [tcl::string::repeat $padchar $repeats]]
}
switch -- $which-$opt_withinansi {
r-0 {
lappend line_chunks $pad
}
r-1 {
if {[lindex $line_chunks end] eq ""} {
set line_chunks [linsert $line_chunks end-2 $pad]
} else {
lappend line_chunks $pad
}
}
r-2 {
dict set pad_cache $missing $pad
}
switch -- $which-$opt_withinansi {
r-0 {
lappend line_chunks $pad
}
r-1 {
if {[lindex $line_chunks end] eq ""} {
set line_chunks [linsert $line_chunks end-2 $pad]
} else {
lappend line_chunks $pad
}
l-0 {
set line_chunks [linsert $line_chunks 0 $pad]
}
r-2 {
lappend line_chunks $pad
}
l-0 {
set line_chunks [linsert $line_chunks 0 $pad]
}
l-1 {
if {[lindex $line_chunks 0] eq ""} {
set line_chunks [linsert $line_chunks 2 $pad]
} else {
set line_chunks [linsert $line_chunks 0 $pad]
}
l-1 {
}
l-2 {
if {$lnum == 0} {
if {[lindex $line_chunks 0] eq ""} {
set line_chunks [linsert $line_chunks 2 $pad]
} else {
set line_chunks [linsert $line_chunks 0 $pad]
}
}
l-2 {
if {$lnum == 0} {
if {[lindex $line_chunks 0] eq ""} {
set line_chunks [linsert $line_chunks 2 $pad]
} else {
set line_chunks [linsert $line_chunks 0 $pad]
}
} else {
set line_chunks [linsert $line_chunks 0 $pad]
}
} else {
set line_chunks [linsert $line_chunks 0 $pad]
}
}
}
lappend lines [::join $line_chunks ""]
set line_chunks [list]
set line_len 0
incr lnum
}
incr p
lappend lines [::join $line_chunks ""]
set line_chunks [list]
set line_len 0
incr lnum
}
} else {
#we need to store empties in order to insert text in the correct position relative to leading/trailing ansi codes
lappend line_chunks ""
#deal with last part zzz of xxx\nyyy\nzzz - not yet a complete line
set pl [lindex $partlines end]
lappend line_chunks $pl ;#we need to lappend because there can already be some pt and ansi entries for the current line from previous {pt ansi} values where pt had no newline.
if {$pl ne "" && ($known_samewidth eq "" || ($known_samewidth ne "" && !$known_samewidth) || $datawidth eq "")} {
incr line_len [punk::char::grapheme_width_cached $pl] ;#memleak - REVIEW
}
}
#don't let trailing empty ansi affect the line_chunks length
if {$ansi ne ""} {
lappend line_chunks $ansi ;#don't update line_len - review - ansi codes with visible content?
lappend line_chunks $ansi ;#don't update line_len
#- review - ansi codes with visible content?
#- There shouldn't be any, even though for example some terminals display PM content
#e.g OSC 8 is ok as it has the uri 'inside' the ansi sequence, but that's ok because the displayable part is outside and is one of our pt values from split_codes.
}
}
#pad last line
@ -5345,7 +5363,11 @@ tcl::namespace::eval textblock {
}
}
lappend lines [::join $line_chunks ""]
return [::join $lines \n]
if {[tcl::dict::get $opts -return] eq "block"} {
return [::join $lines \n]
} else {
return $lines
}
}
#left insertion into a list resulting from punk::ansi::ta::split_codes or split_codes_single
@ -5586,7 +5608,7 @@ tcl::namespace::eval textblock {
#join without regard to each line length in a block (no padding added to make each block uniform)
proc ::textblock::join_basic {args} {
set argd [punk::args::get_by_id ::textblock::join_basic $args]
set argd [punk::args::parse $args withid ::textblock::join_basic]
set ansiresets [tcl::dict::get $argd opts -ansiresets]
set blocks [tcl::dict::get $argd values blocks]
@ -5622,6 +5644,33 @@ tcl::namespace::eval textblock {
}
return [::join $outlines \n]
}
proc ::textblock::join_basic_raw {args} {
#no options. -*, -- are legimate blocks
set blocklists [lrepeat [llength $args] ""]
set blocklengths [lrepeat [expr {[llength $args]+1}] 0] ;#add 1 to ensure never empty - used only for rowcount max calc
set i -1
foreach b $args {
incr i
if {[punk::ansi::ta::detect $b]} {
#-ansireplays 1 quite expensive e.g 7ms in 2024
set blines [punk::lib::lines_as_list -ansireplays 1 -ansiresets auto -- $b]
} else {
set blines [split $b \n]
}
lset blocklengths $i [llength $blines]
lset blocklists $i $blines
}
set rowcount [tcl::mathfunc::max {*}$blocklengths]
set outlines [lrepeat $rowcount ""]
for {set r 0} {$r < $rowcount} {incr r} {
set row ""
foreach blines $blocklists {
append row [lindex $blines $r]
}
lset outlines $r $row
}
return [::join $outlines \n]
}
proc ::textblock::join_basic2 {args} {
#@cmd -name textblock::join_basic -help "Join blocks line by line but don't add padding on each line to enforce uniform width.
# Already uniform blocks will join faster than textblock::join, and ragged blocks will join in a ragged manner
@ -5706,9 +5755,12 @@ tcl::namespace::eval textblock {
}
set idx 0
set blocklists [list]
#set blocklists [list]
set blocklists [lrepeat [llength $blocks] ""]
set rowcount 0
set bidx -1
foreach b $blocks {
incr bidx
#we need the width of a rendered block for per-row renderline calls or padding
#we may as well use widthinfo to also determine raggedness state to pass on to pad function
#set bwidth [width $b]
@ -5725,18 +5777,21 @@ tcl::namespace::eval textblock {
if {[punk::ansi::ta::detect $b]} {
# - we need to join to use pad - even though we then need to immediately resplit REVIEW (make line list version of pad?)
set replay_block [::join [punk::lib::lines_as_list -ansireplays 1 -ansiresets $ansiresets -- $b] \n]
set bl [split [textblock::pad $replay_block -known_hasansi 1 -known_samewidth $is_samewidth -known_blockwidth $bwidth -width $bwidth -which right -padchar " "] \n]
#set blines [split [textblock::pad $replay_block -known_hasansi 1 -known_samewidth $is_samewidth -known_blockwidth $bwidth -width $bwidth -which right -padchar " "] \n]
set blines [textblock::pad $replay_block -return lines -known_hasansi 1 -known_samewidth $is_samewidth -known_blockwidth $bwidth -width $bwidth -which right -padchar " "]
} else {
#each block is being rendered into its own empty column - we don't need resets if it has no ansi, even if blocks to left and right do have ansi
set bl [split [textblock::pad $b -known_hasansi 0 -known_samewidth $is_samewidth -known_blockwidth $bwidth -width $bwidth -which right -padchar " "] \n]
#set blines [split [textblock::pad $b -known_hasansi 0 -known_samewidth $is_samewidth -known_blockwidth $bwidth -width $bwidth -which right -padchar " "] \n]
set blines [textblock::pad $b -return lines -known_hasansi 0 -known_samewidth $is_samewidth -known_blockwidth $bwidth -width $bwidth -which right -padchar " "]
}
set rowcount [expr {max($rowcount,[llength $bl])}]
lappend blocklists $bl
set rowcount [expr {max($rowcount,[llength $blines])}]
#lappend blocklists $bl
lset blocklists $bidx $blines
set width($idx) $bwidth
incr idx
}
set outlines [list]
set outlines [lrepeat $rowcount ""]
for {set r 0} {$r < $rowcount} {incr r} {
set row ""
for {set c 0} {$c < [llength $blocklists]} {incr c} {
@ -5746,7 +5801,8 @@ tcl::namespace::eval textblock {
}
append row $cell
}
lappend outlines $row
#lappend outlines $row
lset outlines $r $row
}
return [::join $outlines \n]
}
@ -5930,7 +5986,7 @@ tcl::namespace::eval textblock {
set table [[textblock::spantest] print]
set punks [a+ web-lawngreen][>punk . lhs][a]\n\n[a+ rgb#FFFF00][>punk . rhs][a]
set ipunks [overtype::renderspace -width [textblock::width $punks] [punk::ansi::enable_inverse]$punks]
set testblock [textblock::testblock 15 rainbow]
set testblock [textblock::testblock -size 15 rainbow]
set contents $ansi\n[textblock::join -- " " $table " " $punks " " $testblock " " $ipunks " " $punks]
set framed [textblock::frame -checkargs 0 -type arc -title [a+ cyan]Compositing[a] -subtitle [a+ red]ANSI[a] -ansiborder [a+ web-orange] $contents]
}
@ -6226,9 +6282,11 @@ tcl::namespace::eval textblock {
set spec [string map [list <ftlist> $::textblock::frametypes] {
@id -id ::textblock::framedef
@cmd -name textblock::framedef\
-summary "Return frame graphical elements as a dictionary."\
-help "Return a dict of the elements that make up a frame border.
May return a subset of available elements based on memberglob values."
@leaders -min 0 -max 0
@opts
-joins -default "" -type list\
-help "List of join directions, any of: up down left right
or those combined with another frametype e.g left-heavy down-light."
@ -6236,7 +6294,7 @@ tcl::namespace::eval textblock {
-help "-boxonly true restricts results to the corner,vertical and horizontal box elements
It excludes the extra top and side join elements htlj,hlbj,vllj,vlrj."
@values -min 1
@values -min 1 -max -1
frametype -choices "<ftlist>" -choiceprefix 0 -choicerestricted 0 -type dict\
-help "name from the predefined frametypes or an adhoc dictionary."
memberglob -type globstring -optional 1 -multiple 1 -choiceprefix 0 -choicerestricted 0 -choices {
@ -7639,7 +7697,7 @@ tcl::namespace::eval textblock {
} -help "Perform an action on the frame cache."
}
proc frame_cache {args} {
set argd [punk::args::get_by_id ::textblock::frame_cache $args]
set argd [punk::args::parse $args withid ::textblock::frame_cache]
set action [dict get $argd values action]
variable frame_cache
set all_values_dict [dict get $argd values]
@ -7684,7 +7742,7 @@ tcl::namespace::eval textblock {
endindex -default "" -type indexexpression
}
proc frame_cache_display {args} {
set argd [punk::args::get_by_id ::textblock::frame_cache_display $args]
set argd [punk::args::parse $args withid ::textblock::frame_cache_display]
variable frame_cache
lassign [dict values [dict get $argd values]] startidx endidx
set limit ""
@ -7967,7 +8025,6 @@ tcl::namespace::eval textblock {
#never need to checkargs if only one argument supplied even if it looks like an option - as it will be treated as data to frame
if {[llength $args] != 1 && (!$opts_ok || $check_args)} {
#as frame is called a lot within table building - checking args can have a *big* impact on final performance.
#set argd [punk::args::get_by_id ::textblock::frame $args]
set argd [punk::args::parse $args withid ::textblock::frame]
set opts [dict get $argd opts]
set contents [dict get $argd values contents]
@ -8569,7 +8626,8 @@ tcl::namespace::eval textblock {
#puts "frame--->ansiwrap -rawansi [ansistring VIEW $opt_ansibase] $cache_inner"
if {$opt_ansibase ne ""} {
if {[punk::ansi::ta::detect $cache_inner]} {
set cache_inner [punk::ansi::ansiwrap -rawansi $opt_ansibase $cache_inner]
#set cache_inner [punk::ansi::ansiwrap -rawansi $opt_ansibase $cache_inner]
set cache_inner [punk::ansi::ansiwrap_raw $opt_ansibase "" "" $cache_inner]
} else {
set cache_inner "$opt_ansibase$cache_inner\x1b\[0m"
}
@ -8600,7 +8658,8 @@ tcl::namespace::eval textblock {
#JMN test
#assert - lhs, cache_inner, rhs non-ragged - so can use join_basic REVIEW
#set cache_body [textblock::join -- {*}$cache_bodyparts]
set cache_body [textblock::join_basic -- {*}$cache_bodyparts]
#set cache_body [textblock::join_basic -- {*}$cache_bodyparts]
set cache_body [textblock::join_basic_raw {*}$cache_bodyparts]
append fscached $cache_body
#append fs $body
@ -8661,7 +8720,8 @@ tcl::namespace::eval textblock {
set contents_has_ansi [punk::ansi::ta::detect $contents]
if {$opt_ansibase ne ""} {
if {$contents_has_ansi} {
set contents [punk::ansi::ansiwrap -rawansi $opt_ansibase $contents]
#set contents [punk::ansi::ansiwrap -rawansi $opt_ansibase $contents]
set contents [punk::ansi::ansiwrap_raw $opt_ansibase "" "" $contents]
} else {
set contents "$opt_ansibase$contents\x1b\[0m"
set contents_has_ansi 1

Loading…
Cancel
Save