Browse Source

punk::args leading positionals

master
Julian Noble 4 months ago
parent
commit
7e890a8eb2
  1. 74
      src/modules/punk-0.1.tm
  2. 36
      src/modules/punk/aliascore-999999.0a1.0.tm
  3. 576
      src/modules/punk/args-999999.0a1.0.tm
  4. 2
      src/modules/punk/fileline-999999.0a1.0.tm
  5. 81
      src/modules/punk/lib-999999.0a1.0.tm
  6. 2
      src/modules/punk/mix/commandset/module-999999.0a1.0.tm
  7. 7
      src/modules/punk/nav/fs-999999.0a1.0.tm
  8. 40
      src/modules/punk/ns-999999.0a1.0.tm
  9. 2
      src/modules/punk/path-999999.0a1.0.tm
  10. 17
      src/modules/punk/repl-0.1.tm
  11. 1491
      src/modules/punk/safe-999999.0a1.0.tm
  12. 3
      src/modules/punk/safe-buildversion.txt
  13. 36
      src/modules/shellfilter-0.1.9.tm
  14. 181
      src/modules/termscheme-999999.0a1.0.tm
  15. 3
      src/modules/termscheme-buildversion.txt
  16. 13
      src/modules/textblock-999999.0a1.0.tm

74
src/modules/punk-0.1.tm

@ -6976,7 +6976,7 @@ namespace eval punk {
-punctchars -default { [list \{ \} \" \\ - _ + = . > , < ' : \; ` ~ ! @ # \$ % ^ & * \[ \] ( ) | / ?] } -punctchars -default { [list \{ \} \" \\ - _ + = . > , < ' : \; ` ~ ! @ # \$ % ^ & * \[ \] ( ) | / ?] }
}] }]
set argd [punk::args::get_dict $argspecs $args] set argd [punk::args::get_dict $argspecs $args]
lassign [dict values $argd] opts vals lassign [dict values $argd] leaders opts vals
set searchspecs [dict values $vals] set searchspecs [dict values $vals]
# -- --- --- --- --- --- # -- --- --- --- --- ---
@ -7560,79 +7560,13 @@ namespace eval punk {
#this hides windows cmd's mode command - probably no big deal - anyone who needs it will know how to exec it. #this hides windows cmd's mode command - probably no big deal - anyone who needs it will know how to exec it.
interp alias {} mode {} punk::mode interp alias {} mode {} punk::mode
#NOTE: an alias may match in a namespace - but not have a corresponding command that matches that name (alias renamed)
proc aliases {{glob *}} { proc aliases {{glob *}} {
set ns [uplevel 1 {::namespace current}] ;#must use :: - we can find ourselves in a namespace with a different 'namespace' command tailcall punk::lib::aliases $glob
set ns_mapped [string map {:: \uFFFF} $ns]
#puts stderr "aliases ns: $ns_mapped"
set segments [split $ns_mapped \uFFFF] ;#include empty string before leading ::
if {![string length [lindex $segments end]]} {
#special case for :: only include leading segment rather thatn {} {}
set segments [lrange $segments 0 end-1]
}
set segcount [llength $segments] ;#only match number of segments matching current ns
set all_aliases [interp aliases {}]
set matched [list]
foreach a $all_aliases {
#normalize with leading ::
if {![string match ::* $a]} {
set abs ::$a
} else {
set abs $a
}
set asegs [split [string map {:: \uFFFF} $abs] \uFFFF]
set acount [llength $asegs]
#puts "alias $abs acount:$acount asegs:$asegs segcount:$segcount segments: $segments"
if {[expr {$acount - 1}] == $segcount} {
if {[lrange $asegs 0 end-1] eq $segments} {
if {[string match $glob [lindex $asegs end]]} {
#report this alias in the current namespace - even though there may be no matching command
lappend matched $a ;#add raw alias token which may or may not have leading ::
}
}
}
}
#set matched_abs [lsearch -all -inline $all_aliases $glob]
return $matched
} }
proc alias {{aliasorglob ""} args} { proc alias {{aliasorglob ""} args} {
set nsthis [uplevel 1 {::namespace current}] ;#must use :: - we can find ourselves in a namespace with a different 'namespace' command tailcall punk::lib::alias $aliasorglob {*}$args
if {[llength $args]} {
if {$aliasorglob in [interp aliases ""]} {
set existing [interp alias "" $aliasorglob]
puts stderr "Overwriting existing alias $aliasorglob -> $existing with $aliasorglob -> $args (in current session only)"
}
if {([llength $args] ==1) && [string trim [lindex $args 0]] eq ""} {
#use empty string/whitespace as intention to delete alias
return [interp alias "" $aliasorglob ""]
}
return [interp alias "" $aliasorglob "" {*}$args]
} else {
if {![string length $aliasorglob]} {
set aliaslist [punk::aliases]
puts -nonewline stderr $aliaslist
return
}
#we need to first check for exact match of alias that happens to have glob chars i.e the supplied aliasorglob looks like a glob but is actually directly an alias
set target [interp alias "" $aliasorglob]
if {[llength $target]} {
return $target
} }
if {([string first "*" $aliasorglob] >= 0) || ([string first "?" $aliasorglob] >= 0)} {
set aliaslist [punk::aliases $aliasorglob]
puts -nonewline stderr $aliaslist
return
}
return [list]
}
}
#pipeline-toys - put in lib/scriptlib? #pipeline-toys - put in lib/scriptlib?
##geometric mean ##geometric mean
@ -7669,8 +7603,6 @@ namespace eval punk {
interp alias {} help {} punk::help interp alias {} help {} punk::help
interp alias {} aliases {} punk::aliases
interp alias {} alias {} punk::alias
interp alias {} treemore {} punk::xmore tree interp alias {} treemore {} punk::xmore tree
interp alias {} tmhere {} .= pwd |path> {::tcl::tm::add {*}$data; set path} |> inspect -label added_to_module_path <0/#| interp alias {} tmhere {} .= pwd |path> {::tcl::tm::add {*}$data; set path} |> inspect -label added_to_module_path <0/#|

36
src/modules/punk/aliascore-999999.0a1.0.tm

@ -105,6 +105,8 @@ tcl::namespace::eval punk::aliascore {
#functions must be in export list of their source namespace #functions must be in export list of their source namespace
set aliases [tcl::dict::create\ set aliases [tcl::dict::create\
aliases ::punk::lib::aliases\
alias ::punk::lib::alias\
tstr ::punk::lib::tstr\ tstr ::punk::lib::tstr\
list_as_lines ::punk::lib::list_as_lines\ list_as_lines ::punk::lib::list_as_lines\
lines_as_list ::punk::lib::lines_as_list\ lines_as_list ::punk::lib::lines_as_list\
@ -155,34 +157,48 @@ tcl::namespace::eval punk::aliascore {
set opts [dict merge $defaults $args] set opts [dict merge $defaults $args]
set opt_force [dict get $opts -force] set opt_force [dict get $opts -force]
#we never override existing aliases to ::repl::interp* even if -force = 1
#(these are our safebase aliases)
set ignore_pattern "::repl::interp*"
set ignore_aliases [list]
variable aliases variable aliases
if {!$opt_force} {
set existing [list] set existing [list]
set conflicts [list] set conflicts [list]
foreach {a cmd} $aliases { foreach {a cmd} $aliases {
if {[tcl::info::commands ::$a] ne ""} { if {[tcl::info::commands ::$a] ne ""} {
lappend existing $a lappend existing $a
if {[llength $cmd] > 1} { set existing_alias [interp alias "" $a]
#use alias mechanism if {$existing_alias ne ""} {
set existing_target [interp alias "" $a] set existing_target $existing_alias
if {[string match $ignore_pattern $existing_target]} {
#don't consider it a conflict - will use ignore_aliases to exclude it below
lappend ignore_aliases $a
continue
}
} else { } else {
#using namespace import if {[catch {tcl::namespace::origin $a} existing_command]} {
#check origin set existing_command ""
set existing_target [tcl::namespace::origin $cmd] }
set existing_target $existing_command
} }
if {$existing_target ne $cmd} { if {$existing_target ne $cmd} {
#command exists in global ns but doesn't match our defined aliases/imports #command exists in global ns but doesn't match our defined aliases/imports
lappend conflicts $a lappend conflicts $a
} }
} }
} }
if {[llength $conflicts]} { if {!$opt_force && [llength $conflicts]} {
error "punk::aliascore::init declined to create any aliases or imports because -force == 0 and conflicts found:$conflicts" error "punk::aliascore::init declined to create any aliases or imports because -force == 0 and conflicts found:$conflicts"
} }
}
set tempns ::temp_[info cmdcount] ;#temp ns for renames set tempns ::temp_[info cmdcount] ;#temp ns for renames
dict for {a cmd} $aliases { dict for {a cmd} $aliases {
#puts "aliascore $a -> $cmd" #puts "aliascore $a -> $cmd"
if {$a in $ignore_aliases} {
continue
}
if {[llength $cmd] > 1} { if {[llength $cmd] > 1} {
interp alias {} $a {} {*}$cmd interp alias {} $a {} {*}$cmd
} else { } else {
@ -205,7 +221,7 @@ tcl::namespace::eval punk::aliascore {
} }
} }
#tcl::namespace::delete $tempns #tcl::namespace::delete $tempns
return [dict keys $aliases] return [dict create aliases [dict keys $aliases] unchanged $ignore_aliases changed $conflicts]
} }

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

@ -58,7 +58,7 @@
# #setting -type none indicates a flag that doesn't take a value (solo flag) # #setting -type none indicates a flag that doesn't take a value (solo flag)
# -nocomplain -type none # -nocomplain -type none
# *values -min 1 -max -1 # *values -min 1 -max -1
# } $args]] opts values # } $args]] leaders opts values
# #
# puts "translation is [dict get $opts -translation]" # puts "translation is [dict get $opts -translation]"
# foreach f [dict values $values] { # foreach f [dict values $values] {
@ -68,7 +68,7 @@
#}] #}]
#[para]The lines beginning with * are optional in most cases and can be used to set defaults and some extra controls #[para]The lines beginning with * are optional in most cases and can be used to set defaults and some extra controls
#[para] - the above example would work just fine with only the -<optionname> lines, but would allow zero filenames to be supplied as no -min value is set for *values #[para] - the above example would work just fine with only the -<optionname> lines, but would allow zero filenames to be supplied as no -min value is set for *values
#[para]valid * lines being with *proc *opts *values #[para]valid * lines being with *proc *leaders *opts *values
#[para]lines beginning with a dash define options - a name can optionally be given to each trailing positional argument. #[para]lines beginning with a dash define options - a name can optionally be given to each trailing positional argument.
#[para]If no names are defined for positional arguments, they will end up in the values key of the dict with numerical keys starting at zero. #[para]If no names are defined for positional arguments, they will end up in the values key of the dict with numerical keys starting at zero.
#[para]e.g the result from the punk::args call above may be something like: #[para]e.g the result from the punk::args call above may be something like:
@ -299,6 +299,22 @@ tcl::namespace::eval punk::args {
} }
return $result return $result
} }
proc flatzip {l1 l2} {
concat {*}[lmap a $l1 b $l2 {list $a $b}]
}
if {[info commands lseq] ne ""} {
#tcl 8.7+ lseq significantly faster, especially for larger ranges
#The internal rep can be an 'arithseries' with no string representation
proc zero_to_n {n} {
lseq 0 $n
}
} else {
proc zero_to_n {n} {
lsearch -all [lrepeat $n 0] *
}
}
#todo? -synonym/alias ? (applies to opts only not values) #todo? -synonym/alias ? (applies to opts only not values)
#e.g -background -aliases {-bg} -default White #e.g -background -aliases {-bg} -default White
@ -309,8 +325,8 @@ tcl::namespace::eval punk::args {
variable argspec_cache variable argspec_cache
#variable argspecs ;#REVIEW!! #variable argspecs ;#REVIEW!!
variable argspec_ids variable argspec_ids
variable initial_optspec_defaults #variable initial_optspec_defaults
variable initial_valspec_defaults #variable initial_valspec_defaults
#ideally we would use a fast hash algorithm to produce a short key with low collision probability. #ideally we would use a fast hash algorithm to produce a short key with low collision probability.
#something like md5 would be ok (this is non cryptographic) - but md5 uses open and isn't usable by default in a safe interp. #something like md5 would be ok (this is non cryptographic) - but md5 uses open and isn't usable by default in a safe interp.
#review - check if there is a built-into-tcl way to do this quickly #review - check if there is a built-into-tcl way to do this quickly
@ -335,6 +351,19 @@ tcl::namespace::eval punk::args {
-regexprepass {}\ -regexprepass {}\
-validationtransform {}\ -validationtransform {}\
] ]
set leaderspec_defaults [tcl::dict::create\
-type string\
-optional 0\
-allow_ansi 1\
-validate_ansistripped 0\
-strip_ansi 0\
-nocase 0\
-choiceprefix 1\
-choicerestricted 1\
-multiple 0\
-regexprepass {}\
-validationtransform {}\
]
set valspec_defaults [tcl::dict::create\ set valspec_defaults [tcl::dict::create\
-type string\ -type string\
-optional 0\ -optional 0\
@ -357,15 +386,20 @@ tcl::namespace::eval punk::args {
#default -allow_ansi to 1 and -validate_ansistripped to 0 and -strip_ansi 0 - it takes time to strip ansi #default -allow_ansi to 1 and -validate_ansistripped to 0 and -strip_ansi 0 - it takes time to strip ansi
#todo - detect if anything in the spec uses -allow_ansi 0, -validate_ansistripped 1 or -strip_ansi 1 and set a flag indicating if punk::ansi::ta::detect should be run on the argslist #todo - detect if anything in the spec uses -allow_ansi 0, -validate_ansistripped 1 or -strip_ansi 1 and set a flag indicating if punk::ansi::ta::detect should be run on the argslist
set leader_required [list]
set opt_required [list] set opt_required [list]
set val_required [list] set val_required [list]
set arg_info [tcl::dict::create] set arg_info [tcl::dict::create]
set arg_checks [tcl::dict::create] set arg_checks [tcl::dict::create]
set opt_defaults [tcl::dict::create] set opt_defaults [tcl::dict::create]
set opt_names [list] ;#defined opts set opt_names [list] ;#defined opts
set leader_defaults [tcl::dict::create]
set val_defaults [tcl::dict::create] set val_defaults [tcl::dict::create]
set opt_solos [list] set opt_solos [list]
#first process dashed and non-dashed argspecs without regard to whether non-dashed are at the beginning or end #first process dashed and non-dashed argspecs without regard to whether non-dashed are at the beginning or end
set leader_names [list]
set val_names [list] set val_names [list]
set records [list] set records [list]
@ -439,10 +473,14 @@ tcl::namespace::eval punk::args {
} }
set proc_info {} set proc_info {}
set id_info {} ;#e.g -children <list> ?? set id_info {} ;#e.g -children <list> ??
set leader_min 0
set leader_max 0 ;#default 0 but if leader_min is set switches to -1 for no limit
#(common case of no leaders specified)
set opt_any 0 set opt_any 0
set val_min 0 set val_min 0
set val_max -1 ;#-1 for no limit set val_max -1 ;#-1 for no limit
set spec_id "" set spec_id ""
set argspace "leaders" ;#leaders -> options -> values
foreach ln $records { foreach ln $records {
set trimln [tcl::string::trim $ln] set trimln [tcl::string::trim $ln]
switch -- [tcl::string::index $trimln 0] { switch -- [tcl::string::index $trimln 0] {
@ -477,6 +515,10 @@ tcl::namespace::eval punk::args {
set proc_info $starspecs set proc_info $starspecs
} }
opts { opts {
if {$argspace eq "values"} {
error "punk::args::definition - *opts declaration must come before *values - received '$linespecs'"
}
set argspace "options"
foreach {k v} $starspecs { foreach {k v} $starspecs {
switch -- $k { switch -- $k {
-any - -any -
@ -537,7 +579,86 @@ tcl::namespace::eval punk::args {
} }
} }
} }
leaders {
if {$argspace in [list options values]} {
error "punk::args::definition - *leaders declaration must come before all options and values"
}
foreach {k v} $starspecs {
switch -- $k {
-min -
-minvalues {
if {$v < 0} {
error "punk::args::definition - minimum acceptable value for key '$k' in *opts line is 0. got $v"
}
set leader_min $v
if {$leader_max == 0} {
set leader_max -1
}
}
-max -
-maxvalues {
if {$v < -1} {
error "punk::args::definition - minimum acceptable value for key '$k' in *opts line is -1 (indicating unlimited). got $v"
}
set leader_max $v
}
-minlen - -maxlen - -range - -choices - -choicelabels - -choiceprefix - -choicerestricted - -nocase {
#review - only apply to certain types?
tcl::dict::set leaderspec_defaults $k $v
}
-nominlen - -nomaxlen - -norange - -nochoices - -nochoicelabels {
if {$v} {
tcl::dict::unset leaderspec_defaults $k
}
}
-type {
switch -- $v {
int - integer {
set v int
}
char - character {
set v char
}
bool - boolean {
set v bool
}
dict - dictionary {
set v dict
}
list {
}
default {
#todo - disallow unknown types unless prefixed with custom-
}
}
tcl::dict::set leaderspec_defaults $k $v
}
-optional -
-allow_ansi -
-validate_ansistripped -
-strip_ansi -
-regexprepass -
-regexprefail -
-validationtransform -
-multiple {
tcl::dict::set leaderspec_defaults $k $v
}
default {
set known { -min -minvalues -max -maxvalues\
-minlen -maxlen -range -choices -choicelabels -choiceprefix -choicerestricted -nocase\
-nominlen -nomaxlen -norange -nochoices -nochoicelabels\
-type -optional -allow_ansi -validate_ansistripped -strip_ansi -multiple\
-regexprepass -regexprefail -validationtransform\
}
error "punk::args::definition - unrecognised key '$k' in *leaders line. Known keys: $known"
}
}
}
}
values { values {
set argspace "values"
foreach {k v} $starspecs { foreach {k v} $starspecs {
switch -- $k { switch -- $k {
-min - -min -
@ -610,11 +731,16 @@ tcl::namespace::eval punk::args {
} }
default { default {
error "punk::args::definition - unrecognised * line in '$ln'. Expected *proc *opts or *values - use **name if paramname needs to be *name" error "punk::args::definition - unrecognised * line in '$ln'. Expected *proc *leaders *opts or *values - use **name if paramname needs to be *name"
} }
} }
continue continue
} elseif {$firstchar eq "-"} { } elseif {$firstchar eq "-"} {
if {$argspace eq "leaders"} {
set argspace "options"
} elseif {$argspace eq "values"} {
error "punk::args::definition - invalid placement of line '$ln' - must come before *values"
}
set argspecs $linespecs set argspecs $linespecs
tcl::dict::set argspecs -ARGTYPE option tcl::dict::set argspecs -ARGTYPE option
lappend opt_names $argname lappend opt_names $argname
@ -625,8 +751,16 @@ tcl::namespace::eval punk::args {
set argname [tcl::string::range $argname 1 end] set argname [tcl::string::range $argname 1 end]
} }
set argspecs $linespecs set argspecs $linespecs
if {$argspace eq "leaders"} {
tcl::dict::set argspecs -ARGTYPE leader
lappend leader_names $argname
if {$leader_max == 0} {
set leader_max [llength $leader_names]
}
} else {
tcl::dict::set argspecs -ARGTYPE value tcl::dict::set argspecs -ARGTYPE value
lappend val_names $argname lappend val_names $argname
}
set is_opt 0 set is_opt 0
} }
#assert - we only get here if it is a value or flag specification line. #assert - we only get here if it is a value or flag specification line.
@ -634,7 +768,11 @@ tcl::namespace::eval punk::args {
if {$is_opt} { if {$is_opt} {
set spec_merged $optspec_defaults set spec_merged $optspec_defaults
} else { } else {
if {$argspace eq "values"} {
set spec_merged $valspec_defaults set spec_merged $valspec_defaults
} else {
set spec_merged $leaderspec_defaults
}
} }
foreach {spec specval} $argspecs { foreach {spec specval} $argspecs {
#literal-key switch - bytecompiled to jumpTable #literal-key switch - bytecompiled to jumpTable
@ -726,23 +864,37 @@ tcl::namespace::eval punk::args {
if {![tcl::dict::get $argspecs -optional] && ![tcl::dict::exists $argspecs -default]} { if {![tcl::dict::get $argspecs -optional] && ![tcl::dict::exists $argspecs -default]} {
if {$is_opt} { if {$is_opt} {
lappend opt_required $argname lappend opt_required $argname
} else {
if {$argspace eq "leaders"} {
lappend leader_required $argname
} else { } else {
lappend val_required $argname lappend val_required $argname
} }
} }
}
if {[tcl::dict::exists $argspecs -default]} { if {[tcl::dict::exists $argspecs -default]} {
if {$is_opt} { if {$is_opt} {
tcl::dict::set opt_defaults $argname [tcl::dict::get $argspecs -default] tcl::dict::set opt_defaults $argname [tcl::dict::get $argspecs -default]
} else {
if {$argspace eq "leaders"} {
tcl::dict::set leader_defaults $argname [tcl::dict::get $argspecs -default]
} else { } else {
tcl::dict::set val_defaults $argname [tcl::dict::get $argspecs -default] tcl::dict::set val_defaults $argname [tcl::dict::get $argspecs -default]
} }
} }
} }
}
# REVIEW
foreach leadername [lrange $leader_names 0 end] {
if {[tcl::dict::get $arg_info $leadername -multiple]} {
error "bad key -multiple on argument spec for leader '$valname'. Only the last value argument specification can be marked -multiple"
}
}
#confirm any valnames before last don't have -multiple key #confirm any valnames before last don't have -multiple key
foreach valname [lrange $val_names 0 end-1] { foreach valname [lrange $val_names 0 end-1] {
if {[tcl::dict::get $arg_info $valname -multiple]} { if {[tcl::dict::get $arg_info $valname -multiple]} {
error "bad key -multiple on argument spec for '$valname'. Only the last value argument specification can be marked -multiple" error "bad key -multiple on argument spec for value '$valname'. Only the last value argument specification can be marked -multiple"
} }
} }
if {$spec_id eq "" || [tcl::string::tolower $spec_id] eq "auto"} { if {$spec_id eq "" || [tcl::string::tolower $spec_id] eq "auto"} {
@ -750,7 +902,11 @@ tcl::namespace::eval punk::args {
set spec_id "autoid_[incr id_counter]" set spec_id "autoid_[incr id_counter]"
} }
#todo - document that ambiguities in API are likely if both *leaders and *values used
#todo - do some checks for obvious bad definitions involving a mix of *leaders and *values (e.g with optional options)
set leader_checks_defaults [tcl::dict::remove $leaderspec_defaults -type -default -multiple -strip_ansi -validate_ansistripped -allow_ansi] ;#leave things like -range -minlen
set opt_checks_defaults [tcl::dict::remove $optspec_defaults -type -default -multiple -strip_ansi -validate_ansistripped -allow_ansi] ;#leave things like -range -minlen set opt_checks_defaults [tcl::dict::remove $optspec_defaults -type -default -multiple -strip_ansi -validate_ansistripped -allow_ansi] ;#leave things like -range -minlen
set val_checks_defaults [tcl::dict::remove $valspec_defaults -type -default -multiple -strip_ansi -validate_ansistripped -allow_ansi] ;#leave things like -range -minlen set val_checks_defaults [tcl::dict::remove $valspec_defaults -type -default -multiple -strip_ansi -validate_ansistripped -allow_ansi] ;#leave things like -range -minlen
@ -758,6 +914,13 @@ tcl::namespace::eval punk::args {
id $spec_id\ id $spec_id\
arg_info $arg_info\ arg_info $arg_info\
arg_checks $arg_checks\ arg_checks $arg_checks\
leader_defaults $leader_defaults\
leader_required $leader_required\
leader_names $leader_names\
leader_min $leader_min\
leader_max $leader_max\
leaderspec_defaults $leaderspec_defaults\
leader_checks_defaults $leader_checks_defaults\
opt_defaults $opt_defaults\ opt_defaults $opt_defaults\
opt_required $opt_required\ opt_required $opt_required\
opt_names $opt_names\ opt_names $opt_names\
@ -782,13 +945,33 @@ tcl::namespace::eval punk::args {
return $result return $result
} }
proc get_spec {id} { proc get_spec {id {patternlist *}} {
variable argspec_ids variable argspec_ids
if {[tcl::dict::exists $argspec_ids $id]} { if {[tcl::dict::exists $argspec_ids $id]} {
if {$patternlist eq "*"} {
return [tcl::dict::get $argspec_ids $id] return [tcl::dict::get $argspec_ids $id]
} else {
set spec [tcl::dict::get $argspec_ids $id]
set result ""
set specdict [definition $spec]
set arg_info [dict get $specdict arg_info]
foreach pat $patternlist {
set matches [dict keys $arg_info $pat]
foreach m $matches {
set def [dict get $arg_info $m]
set def [dict remove $def -ARGTYPE]
append result \n "$m $def"
}
}
return $result
}
} }
return return
} }
#proc get_spec_leaders ??
#proc get_spec_opts ??
#proc get_spec_values ??
proc get_spec_ids {{match *}} { proc get_spec_ids {{match *}} {
variable argspec_ids variable argspec_ids
return [tcl::dict::keys $argspec_ids $match] return [tcl::dict::keys $argspec_ids $match]
@ -876,17 +1059,17 @@ tcl::namespace::eval punk::args {
$t add_column -headers $blank_header_col $t add_column -headers $blank_header_col
$t add_column -headers $blank_header_col $t add_column -headers $blank_header_col
if {"$procname$prochelp" eq ""} { if {"$procname$prochelp" eq ""} {
$t configure_header 0 -values {Arg Type Default Multiple Help} $t configure_header 0 -values {Arg Type Default Multi Help}
} elseif {$procname eq ""} { } elseif {$procname eq ""} {
$t configure_header 0 -colspans {1 4 0 0 0} -values [list Description: $prochelp_display] $t configure_header 0 -colspans {1 4 0 0 0} -values [list Description: $prochelp_display]
$t configure_header 1 -values {Arg Type Default Multiple Help} $t configure_header 1 -values {Arg Type Default Multi Help}
} elseif {$prochelp eq ""} { } elseif {$prochelp eq ""} {
$t configure_header 0 -colspans {1 4 0 0 0} -values [list PROC/METHOD: $procname_display] $t configure_header 0 -colspans {1 4 0 0 0} -values [list PROC/METHOD: $procname_display]
$t configure_header 1 -values {Arg Type Default Multiple Help} $t configure_header 1 -values {Arg Type Default Multi Help}
} else { } else {
$t configure_header 0 -colspans {1 4 0 0 0} -values [list PROC/METHOD: $procname_display] $t configure_header 0 -colspans {1 4 0 0 0} -values [list PROC/METHOD: $procname_display]
$t configure_header 1 -colspans {1 4 0 0 0} -values [list Description: $prochelp_display] $t configure_header 1 -colspans {1 4 0 0 0} -values [list Description: $prochelp_display]
$t configure_header 2 -values {Arg Type Default Multiple Help} $t configure_header 2 -values {Arg Type Default Multi Help}
} }
@ -894,7 +1077,8 @@ tcl::namespace::eval punk::args {
#set A_DEFAULT [a+ brightwhite Brightgreen] #set A_DEFAULT [a+ brightwhite Brightgreen]
set A_DEFAULT "" set A_DEFAULT ""
set A_BADARG [a+ brightred] set A_BADARG [a+ brightred]
set greencheck [a+ brightgreen]\u2713[a] set greencheck [a+ brightgreen]\u2713[a] ;#green tick
set soloflag [a+ brightcyan]\u2690[a] ;#flag - may be replacement char in old dos prompt (?)
set A_PREFIX [a+ green] ;#use a+ so colour off can apply set A_PREFIX [a+ green] ;#use a+ so colour off can apply
if {$A_PREFIX eq ""} { if {$A_PREFIX eq ""} {
set A_PREFIX [a+ underline] set A_PREFIX [a+ underline]
@ -930,20 +1114,21 @@ tcl::namespace::eval punk::args {
set opt_names_display $opt_names set opt_names_display $opt_names
} }
} }
set trailing_val_names [dict get $spec_dict val_names] ;#temporarily assign all as trailing set leading_val_names [dict get $spec_dict leader_names]
set leading_val_names [list] set trailing_val_names [dict get $spec_dict val_names]
dict for {argname info} [tcl::dict::get $spec_dict arg_info] {
if {![string match -* $argname]} { #dict for {argname info} [tcl::dict::get $spec_dict arg_info] {
lappend leading_val_names [lpop trailing_val_names 0] # if {![string match -* $argname]} {
} else { # lappend leading_val_names [lpop trailing_val_names 0]
break # } else {
} # break
} # }
if {![llength $leading_val_names] && ![llength $opt_names]} { #}
#all vals were actually trailing - no opts #if {![llength $leading_val_names] && ![llength $opt_names]} {
set trailing_val_names $leading_val_names # #all vals were actually trailing - no opts
set leading_val_names {} # set trailing_val_names $leading_val_names
} # set leading_val_names {}
#}
set leading_val_names_display $leading_val_names set leading_val_names_display $leading_val_names
set trailing_val_names_display $trailing_val_names set trailing_val_names_display $trailing_val_names
@ -954,7 +1139,7 @@ tcl::namespace::eval punk::args {
foreach argshow $argnames_display arg $argnames { foreach argshow $argnames_display arg $argnames {
set arginfo [dict get $spec_dict arg_info $arg] set arginfo [dict get $spec_dict arg_info $arg]
if {[dict exists $arginfo -default]} { if {[dict exists $arginfo -default]} {
set default $A_DEFAULT[dict get $arginfo -default]$RST set default "'$A_DEFAULT[dict get $arginfo -default]$RST'"
} else { } else {
set default "" set default ""
} }
@ -1022,7 +1207,14 @@ tcl::namespace::eval punk::args {
} else { } else {
set multiple "" set multiple ""
} }
$t add_row [list $argshow [dict get $arginfo -type] $default $multiple $help] if {[::punk::args::Dict_getdef $arginfo -optional 0] == 1 || [dict exists $arginfo -default]} {
set argshow "?${argshow}?"
}
set typeshow [dict get $arginfo -type]
if {$typeshow eq "none"} {
set typeshow "$typeshow $soloflag"
}
$t add_row [list $argshow $typeshow $default $multiple $help]
if {$arg eq $badarg} { if {$arg eq $badarg} {
$t configure_row [expr {[$t row_count]-1}] -ansibase $A_BADARG $t configure_row [expr {[$t row_count]-1}] -ansibase $A_BADARG
} }
@ -1087,7 +1279,7 @@ tcl::namespace::eval punk::args {
#[para]argumentname -key val -ky2 val2... #[para]argumentname -key val -ky2 val2...
#[para]where the valid keys for each option specification are: -default -type -range -choices #[para]where the valid keys for each option specification are: -default -type -range -choices
#[para]comment lines begining with # are ignored and can be placed anywhere except within a multiline value where it would become part of that value #[para]comment lines begining with # are ignored and can be placed anywhere except within a multiline value where it would become part of that value
#[para]lines beginning with *proc *opts or *values also take -key val pairs and can be used to set defaults and control settings. #[para]lines beginning with *proc *leaders *opts or *values also take -key val pairs and can be used to set defaults and control settings.
#[para]*opts or *values lines can appear multiple times with defaults affecting flags/values that follow. #[para]*opts or *values lines can appear multiple times with defaults affecting flags/values that follow.
#[arg_def list rawargs] #[arg_def list rawargs]
#[para] This is a list of the arguments to parse. Usually it will be the $args value from the containing proc, #[para] This is a list of the arguments to parse. Usually it will be the $args value from the containing proc,
@ -1162,17 +1354,55 @@ tcl::namespace::eval punk::args {
# todo - consider allowing last leading positional to have -multiple 1 but only if there exists an eopts marker later (--) ? # todo - consider allowing last leading positional to have -multiple 1 but only if there exists an eopts marker later (--) ?
set opts $opt_defaults set opts $opt_defaults
set pre_values {} set pre_values {}
dict for {a info} $arg_info { #dict for {a info} $arg_info {
#todo - flag for possible subhandler - whether leading - or not (shellfilter concept) # #todo - flag for possible subhandler - whether leading - or not (shellfilter concept)
if {![string match -* $a]} { # if {![string match -* $a]} {
# #lappend pre_values [lpop rawargs 0]
# if {[catch {lpop rawargs 0} val]} {
# break
# } else {
# lappend pre_values $val
# }
# } else {
# break
# }
#}
set argnames [dict keys $arg_info]
set optnames [lsearch -all -inline $argnames -*]
set ridx 0
set rawargs_copy $rawargs
if {$leader_max != 0} {
foreach r $rawargs_copy {
if {$leader_max != -1 && $ridx > $leader_max-1} {
break
}
if {[string match -* $r]} {
if {$r eq "--"} {
break
}
set matchopt [::tcl::prefix::match -error {} $optnames $r]
if {$matchopt ne ""} {
#flaglike matches a known flag - don't treat as leader
break
}
if {![string match -* [lindex $argnames $ridx]]} {
#there is a named leading positional for this position
lappend pre_values [lpop rawargs 0] lappend pre_values [lpop rawargs 0]
incr ridx
continue
} else { } else {
break break
} }
} }
lappend pre_values [lpop rawargs 0]
incr ridx
}
}
#assert - rawargs has been reduced by leading positionals #assert - rawargs has been reduced by leading positionals
if {$id ne "jtest"} { set leaders [list]
set arglist {} set arglist {}
set post_values {} set post_values {}
#val_min, val_max #val_min, val_max
@ -1326,199 +1556,40 @@ tcl::namespace::eval punk::args {
break break
} }
} }
set values [list {*}$pre_values {*}$post_values] #set values [list {*}$pre_values {*}$post_values]
set leaders $pre_values
set values $post_values
} else { } else {
set values [list {*}$pre_values {*}$rawargs] ;#no -flags detected set leaders $pre_values
set values $rawargs
#set values [list {*}$pre_values {*}$rawargs] ;#no -flags detected
set arglist [list] set arglist [list]
} }
#puts stderr "--> arglist: $arglist" #puts stderr "--> arglist: $arglist"
#puts stderr "--> values: $values" #puts stderr "--> values: $values"
}
if {$id eq "jtest"} { set positionalidx 0 ;#index for unnamed positionals (both leaders and values)
if {[set eopts [lsearch -exact $rawargs "--"]] >= 0} { set ldridx 0
lappend flagsreceived -- set leadernames_received [list]
set values [lrange $rawargs $eopts+1 end] set leaders_dict $leader_defaults
set arglist [lrange $rawargs 0 $eopts-1] set num_leaders [llength $leaders]
set maxidx [expr {[llength $arglist]-1}] foreach leadername $leader_names ldr $leaders {
for {set i 0} {$i <= $maxidx} {incr i} { if {$ldridx+1 > $num_leaders} {
set a [lindex $arglist $i]
if {![tcl::string::match -* $a]} {
#we can't treat as first positional arg - as it comes before the eopt indicator --
arg_error "punk::args::get_dict bad options for [Get_caller]. Expected flag (leading -) at position $i got:$rawargs" $argspecs
}
if {![catch {tcl::prefix match -message "options for %caller%. Unexpected option" $opt_names $a } fullopt]} {
if {[tcl::dict::get $arg_info $fullopt -type] ne "none"} {
#non-solo
set flagval [lindex $arglist $i+1]
if {[dict get $arg_info $fullopt -multiple]} {
#don't lappend to default - we need to replace if there is a default
#review - what if user sets first value that happens to match a default?
if {$fullopt ni $flagsreceived && [tcl::dict::exists $opt_defaults $fullopt] && ([tcl::dict::get $opt_defaults $fullopt] eq [tcl::dict::get $opts $fullopt])} {
#first occurrence of this flag, whilst stored value matches default
tcl::dict::set opts $fullopt [list $flagval]
} else {
tcl::dict::lappend opts $fullopt $flagval
}
} else {
tcl::dict::set opts $fullopt $flagval
}
#incr i to skip flagval
if {[incr i] > $maxidx} {
arg_error "punk::args::get_dict bad options for [Get_caller]. No value supplied for last option $fullopt at index [expr {$i-1}] which is not marked with -type none" $argspecs $fullopt
}
} else {
#type none (solo-flag)
if {[tcl::dict::get $arg_info $fullopt -multiple]} {
if {[tcl::dict::get $opts $fullopt] == 0} {
#review - what if default at time opt was specified is not zero? we will end up with more items in the list than the number of times the flag was specified
tcl::dict::set opts $fullopt 1
} else {
tcl::dict::lappend opts $fullopt 1
}
} else {
tcl::dict::set opts $fullopt 1
}
}
lappend flagsreceived $fullopt ;#dups ok
} else {
if {$opt_any} {
set newval [lindex $arglist $i+1]
#opt was unspecified but is allowed due to *opt -any 1 - 'adhoc/passthrough' option
tcl::dict::set arg_info $a $optspec_defaults ;#use default settings for unspecified opt
tcl::dict::set arg_checks $a $opt_checks_defaults
if {[tcl::dict::get $arg_info $a -type] ne "none"} {
if {[tcl::dict::get $arg_info $a -multiple]} {
tcl::dict::lappend opts $a $newval
} else {
tcl::dict::set opts $a $newval
}
lappend flagsreceived $a ;#adhoc flag as supplied
if {[incr i] > $maxidx} {
arg_error "punk::args::get_dict bad options for [Get_caller]. No value supplied for last adhoc option $a at index [expr {$i-1}] which is not marked with -type none" $argspecs $a
}
} else {
#review -we can't provide a way to allow unspecified -type none flags through reliably/unambiguously unless all remaining unspecified options are -type none
if {[tcl::dict::get $arg_info $a -multiple]} {
if {![tcl::dict::exists $opts $a]} {
tcl::dict::set opts $a 1
} else {
tcl::dict::lappend opts $a 1
}
} else {
tcl::dict::set opts $a 1
}
}
} else {
#delay Get_caller so only called in the unhappy path
set errmsg [tcl::string::map [list %caller% [Get_caller]] $fullopt]
arg_error $errmsg $argspecs $fullopt
}
}
}
} else {
if {[lsearch $rawargs -*] >= 0} {
#no -- end of opts indicator
#to support option values with leading dash e.g -offset -1 , we can't just use the last flagindex to determine start of positional args.
#we break on first non-flag looking argument that isn't in an option's value position and use that index as the division.
#The caller should use -- if the first positional arg is likely or has the potential to start with a dash.
set maxidx [expr {[llength $rawargs]-1}]
for {set i 0} {$i <= $maxidx} {incr i} {
set a [lindex $rawargs $i]
#we can automatically rule out arguments containing whitespace from the set of simple flags beginning with a dash
#This helps for example when first value is a dict or list in which the first item happens to begin with a dash
#explicit -- still safer in many cases, but this is a reasonable and fast enough test
if {![tcl::string::match -* $a] || [regexp {\s+} $a]} {
#assume beginning of positional args
incr i -1
break break
} }
if {$leadername ne ""} {
if {![catch {tcl::prefix match -message "options for %caller%. Unexpected option" $opt_names $a } fullopt]} { tcl::dict::set leaders_dict $leadername $ldr
if {[tcl::dict::get $arg_info $fullopt -type] ne "none"} { lappend leadernames_received $leadername
#non-solo
set flagval [lindex $rawargs $i+1]
if {[dict get $arg_info $fullopt -multiple]} {
#don't lappend to default - we need to replace if there is a default
#review - what if user sets first value that happens to match a default?
if {$fullopt ni $flagsreceived && [tcl::dict::exists $opt_defaults $fullopt] && ([tcl::dict::get $opt_defaults $fullopt] eq [tcl::dict::get $opts $fullopt])} {
#first occurrence of this flag, whilst stored value matches default
tcl::dict::set opts $fullopt [list $flagval]
} else { } else {
tcl::dict::lappend opts $fullopt $flagval tcl::dict::set leaders_dict $positionalidx $ldr
tcl::dict::set arg_info $positionalidx $leaderspec_defaults
tcl::dict::set arg_checks $positionalidx $leader_checks_defaults
lappend leadernames_received $positionalidx
} }
} else { incr ldridx
tcl::dict::set opts $fullopt $flagval incr positionalidx
} }
#incr i to skip flagval
if {[incr i] > $maxidx} {
arg_error "punk::args::get_dict bad options for [Get_caller]. No value supplied for last option $fullopt at index [expr {$i-1}] which is not marked with -type none" $argspecs $fullopt
}
} else {
#type none (solo-flag)
if {[tcl::dict::get $arg_info $fullopt -multiple]} {
if {[tcl::dict::get $opts $fullopt] == 0} {
#review - what if default at time opt was specified is not zero? we will end up with more items in the list than the number of times the flag was specified
tcl::dict::set opts $fullopt 1
} else {
tcl::dict::lappend opts $fullopt 1
}
} else {
tcl::dict::set opts $fullopt 1
}
}
lappend flagsreceived $fullopt ;#dups ok
} else {
if {$opt_any} {
set newval [lindex $rawargs $i+1]
#opt was unspecified but is allowed due to *opt -any 1 - 'adhoc/passthrough' option
tcl::dict::set arg_info $a $optspec_defaults ;#use default settings for unspecified opt
tcl::dict::set arg_checks $a $opt_checks_defaults
if {[tcl::dict::get $arg_info $a -type] ne "none"} {
if {[tcl::dict::get $arg_info $a -multiple]} {
tcl::dict::lappend opts $a $newval
} else {
tcl::dict::set opts $a $newval
}
lappend flagsreceived $a ;#adhoc flag as supplied
if {[incr i] > $maxidx} {
arg_error "punk::args::get_dict bad options for [Get_caller]. No value supplied for last adhoc option $a at index [expr {$i-1}] which is not marked with -type none" $argspecs $a
}
} else {
#review -we can't provide a way to allow unspecified -type none flags through reliably/unambiguously unless all remaining unspecified options are -type none
if {[tcl::dict::get $arg_info $a -multiple]} {
if {![tcl::dict::exists $opts $a]} {
tcl::dict::set opts $a 1
} else {
tcl::dict::lappend opts $a 1
}
} else {
tcl::dict::set opts $a 1
}
}
} else {
#delay Get_caller so only called in the unhappy path
set errmsg [tcl::string::map [list %caller% [Get_caller]] $fullopt]
arg_error $errmsg $argspecs $fullopt
}
}
}
set arglist [lrange $rawargs 0 $i]
set values [lrange $rawargs $i+1 end]
#puts "$i--->arglist:$arglist"
#puts "$i--->values:$values"
} else {
set values $rawargs ;#no -flags detected
set arglist [list]
}
}
}
set validx 0 set validx 0
set in_multiple "" set in_multiple ""
@ -1547,13 +1618,29 @@ tcl::namespace::eval punk::args {
tcl::dict::lappend values_dict $in_multiple $val tcl::dict::lappend values_dict $in_multiple $val
#name already seen #name already seen
} else { } else {
tcl::dict::set values_dict $validx $val tcl::dict::set values_dict $positionalidx $val
tcl::dict::set arg_info $validx $valspec_defaults tcl::dict::set arg_info $positionalidx $valspec_defaults
tcl::dict::set arg_checks $validx $val_checks_defaults tcl::dict::set arg_checks $positionalidx $val_checks_defaults
lappend valnames_received $validx lappend valnames_received $positionalidx
} }
} }
incr validx incr validx
incr positionalidx
}
if {$leader_max == -1} {
#only check min
if {$num_leaders < $leader_min} {
arg_error "bad number of leading values for [Get_caller]. Got $num_leaders leaders. Expected at least $leader_min" $argspecs
}
} else {
if {$num_leaders < $leader_min || $num_leaders > $leader_max} {
if {$leader_min == $leader_max} {
arg_error "bad number of leading values for [Get_caller]. Got $num_leaders leaders. Expected exactly $leader_min" $argspecs
} else {
arg_error "bad number of leading values for [Get_caller]. Got $num_leaders leaders. Expected between $leader_min and $leader_max inclusive" $argspecs
}
}
} }
if {$val_max == -1} { if {$val_max == -1} {
@ -1592,6 +1679,9 @@ tcl::namespace::eval punk::args {
# error "Required value missing for [Get_caller]. missing values $missing marked with -optional false - so must be present" # error "Required value missing for [Get_caller]. missing values $missing marked with -optional false - so must be present"
#} #}
#for now (2024-06) punk::lib::ldiff is a better compromise across normal/safe interps e.g 0.7/0.8us #for now (2024-06) punk::lib::ldiff is a better compromise across normal/safe interps e.g 0.7/0.8us
if {[llength [set missing [punklib_ldiff $leader_required $leadernames_received]]]} {
arg_error "Required leader missing for [Get_caller]. missing values: '$missing' marked with -optional false - so must be present" $argspecs
}
if {[llength [set missing [punklib_ldiff $opt_required $flagsreceived]]]} { if {[llength [set missing [punklib_ldiff $opt_required $flagsreceived]]]} {
arg_error "Required option missing for [Get_caller]. missing flags: '$missing' are marked with -optional false - so must be present in full-length form" $argspecs arg_error "Required option missing for [Get_caller]. missing flags: '$missing' are marked with -optional false - so must be present in full-length form" $argspecs
} }
@ -1605,7 +1695,7 @@ tcl::namespace::eval punk::args {
#todo - allow defaults outside of choices/ranges #todo - allow defaults outside of choices/ranges
#check types,ranges,choices #check types,ranges,choices
set opts_and_values [tcl::dict::merge $opts $values_dict] set opts_and_values [tcl::dict::merge $leaders_dict $opts $values_dict]
#set combined_defaults [tcl::dict::merge $val_defaults $opt_defaults] ;#can be no key collisions - we don't allow a value key beginning with dash - opt names must begin with dash #set combined_defaults [tcl::dict::merge $val_defaults $opt_defaults] ;#can be no key collisions - we don't allow a value key beginning with dash - opt names must begin with dash
#puts "---opts_and_values:$opts_and_values" #puts "---opts_and_values:$opts_and_values"
#puts "---arg_info:$arg_info" #puts "---arg_info:$arg_info"
@ -1657,11 +1747,17 @@ tcl::namespace::eval punk::args {
set choiceprefix [tcl::dict::get $thisarg -choiceprefix] set choiceprefix [tcl::dict::get $thisarg -choiceprefix]
set choicerestricted [tcl::dict::get $thisarg -choicerestricted] set choicerestricted [tcl::dict::get $thisarg -choicerestricted]
set nocase [tcl::dict::get $thisarg -nocase] set nocase [tcl::dict::get $thisarg -nocase]
if {[tcl::dict::get $thisarg -ARGTYPE] eq "option"} { switch -- [tcl::dict::get $thisarg -ARGTYPE] {
leader {
set dname leaders_dict
}
option {
set dname opts set dname opts
} else { }
value {
set dname values_dict set dname values_dict
} }
}
set idx 0 ;# set idx 0 ;#
#opts/values_dict $argname membmer has been populated with the actual entered choices - which might be prefixes #opts/values_dict $argname membmer has been populated with the actual entered choices - which might be prefixes
#assert llength $vlist == llength [dict get $dname $argname] #assert llength $vlist == llength [dict get $dname $argname]
@ -1819,8 +1915,8 @@ tcl::namespace::eval punk::args {
lappend pass_quick_list_e_check $e_check lappend pass_quick_list_e_check $e_check
} }
} }
set remaining_e [punk::lib::ldiff $vlist $pass_quick_list_e] set remaining_e [punklib_ldiff $vlist $pass_quick_list_e]
set remaining_e_check [punk::lib::ldiff $vlist_check $pass_quick_list_e_check] set remaining_e_check [punklib_ldiff $vlist_check $pass_quick_list_e_check]
} }
if {$regexprefail ne ""} { if {$regexprefail ne ""} {
foreach e $remaining_e e_check $remaining_e_check { foreach e $remaining_e e_check $remaining_e_check {
@ -2036,23 +2132,37 @@ tcl::namespace::eval punk::args {
if {$is_strip_ansi} { if {$is_strip_ansi} {
set stripped_list [lmap e $vlist_original {punk::ansi::ansistrip $e}] ;#no faster or slower, but more concise than foreach set stripped_list [lmap e $vlist_original {punk::ansi::ansistrip $e}] ;#no faster or slower, but more concise than foreach
if {[tcl::dict::get $thisarg -multiple]} { if {[tcl::dict::get $thisarg -multiple]} {
if {[tcl::dict::get $thisarg -ARGTYPE] eq "option"} { switch -- [tcl::dict::get $thisarg -ARGTYPE] {
leader {
tcl::dict::set leaders_dict $argname $stripped_list
}
option {
tcl::dict::set opts $argname $stripped_list tcl::dict::set opts $argname $stripped_list
} else { }
value {
tcl::dict::set values_dict $argname $stripped_list tcl::dict::set values_dict $argname $stripped_list
} }
}
} else { } else {
if {[tcl::dict::get $thisarg -ARGTYPE] eq "option"} { switch -- [tcl::dict::get $thisarg -ARGTYPE] {
leader {
tcl::dict::set leaders_dict [lindex $stripped_list 0]
}
option {
tcl::dict::set opts $argname [lindex $stripped_list 0] tcl::dict::set opts $argname [lindex $stripped_list 0]
} else { }
value {
tcl::dict::set values_dict [lindex $stripped_list 0] tcl::dict::set values_dict [lindex $stripped_list 0]
} }
} }
} }
} }
}
#maintain order of opts $opts values $values as caller may use lassign. #maintain order of opts $opts values $values as caller may use lassign.
return [tcl::dict::create opts $opts values $values_dict] set receivednames [list {*}$leadernames_received {*}$flagsreceived {*}$valnames_received]
set received_posns [concat {*}[lmap a $receivednames b [zero_to_n [expr {[llength $receivednames]-1}]] {list $a $b}]] ;#flat zip named with overall posn including opts
return [tcl::dict::create leaders $leaders_dict opts $opts values $values_dict received $received_posns]
} }
#proc sample1 {p1 args} { #proc sample1 {p1 args} {
@ -2063,6 +2173,16 @@ tcl::namespace::eval punk::args {
#} #}
punk::args::definition {
*id punk::args::TEST
*opts -optional 0
-o1 -default 111 -help "opt 1 mandatory"
*opts -optional 1
-o2 -default 222 -help "opt 2 optional"
*values -min 0 -max 1
v -help\
"v1 optional"
}
#*** !doctools #*** !doctools

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

@ -1556,7 +1556,7 @@ namespace eval punk::fileline::lib {
set argd [punk::args::get_dict { set argd [punk::args::get_dict {
-offset -default 0 -offset -default 0
} $args] } $args]
lassign [dict values $argd] opts remainingargs lassign [dict values $argd] leaders opts remainingargs
} }

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

@ -211,6 +211,9 @@ tcl::namespace::eval punk::lib::compat {
#*** !doctools #*** !doctools
#[list_begin definitions] #[list_begin definitions]
if {"::lremove" ne [info commands ::lremove]} { if {"::lremove" ne [info commands ::lremove]} {
#puts stderr "Warning - no built-in lremove" #puts stderr "Warning - no built-in lremove"
interp alias {} lremove {} ::punk::lib::compat::lremove interp alias {} lremove {} ::punk::lib::compat::lremove
@ -393,6 +396,80 @@ namespace eval punk::lib {
set has_twapi [expr {![catch {package require twapi}]}] set has_twapi [expr {![catch {package require twapi}]}]
} }
#NOTE: an alias may match in a namespace - but not have a corresponding command that matches that name (alias renamed)
proc aliases {{glob *}} {
set ns [uplevel 1 {::namespace current}] ;#must use :: - we can find ourselves in a namespace with a different 'namespace' command
set ns_mapped [string map {:: \uFFFF} $ns]
#puts stderr "aliases ns: $ns_mapped"
set segments [split $ns_mapped \uFFFF] ;#include empty string before leading ::
if {![string length [lindex $segments end]]} {
#special case for :: only include leading segment rather thatn {} {}
set segments [lrange $segments 0 end-1]
}
set segcount [llength $segments] ;#only match number of segments matching current ns
set all_aliases [interp aliases {}]
set matched [list]
foreach a $all_aliases {
#normalize with leading ::
if {![string match ::* $a]} {
set abs ::$a
} else {
set abs $a
}
set asegs [split [string map {:: \uFFFF} $abs] \uFFFF]
set acount [llength $asegs]
#puts "alias $abs acount:$acount asegs:$asegs segcount:$segcount segments: $segments"
if {[expr {$acount - 1}] == $segcount} {
if {[lrange $asegs 0 end-1] eq $segments} {
if {[string match $glob [lindex $asegs end]]} {
#report this alias in the current namespace - even though there may be no matching command
lappend matched $a ;#add raw alias token which may or may not have leading ::
}
}
}
}
#set matched_abs [lsearch -all -inline $all_aliases $glob]
return $matched
}
proc alias {{aliasorglob ""} args} {
set nsthis [uplevel 1 {::namespace current}] ;#must use :: - we can find ourselves in a namespace with a different 'namespace' command
if {[llength $args]} {
if {$aliasorglob in [interp aliases ""]} {
set existing [interp alias "" $aliasorglob]
puts stderr "Overwriting existing alias $aliasorglob -> $existing with $aliasorglob -> $args (in current session only)"
}
if {([llength $args] ==1) && [string trim [lindex $args 0]] eq ""} {
#use empty string/whitespace as intention to delete alias
return [interp alias "" $aliasorglob ""]
}
return [interp alias "" $aliasorglob "" {*}$args]
} else {
if {![string length $aliasorglob]} {
set aliaslist [punk::lib::aliases]
puts -nonewline stderr $aliaslist
return
}
#we need to first check for exact match of alias that happens to have glob chars i.e the supplied aliasorglob looks like a glob but is actually directly an alias
set target [interp alias "" $aliasorglob]
if {[llength $target]} {
return $target
}
if {([string first "*" $aliasorglob] >= 0) || ([string first "?" $aliasorglob] >= 0)} {
set aliaslist [punk::lib::aliases $aliasorglob]
puts -nonewline stderr $aliaslist
return
}
return [list]
}
}
# == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == # == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == ==
# Maintenance - This is the primary source for tm_version... functions # Maintenance - This is the primary source for tm_version... functions
# - certain packages script require these but without package dependency # - certain packages script require these but without package dependency
@ -2894,7 +2971,7 @@ namespace eval punk::lib {
lassign [tcl::dict::values [punk::args::get_dict { lassign [tcl::dict::values [punk::args::get_dict {
-joinchar -default \n -joinchar -default \n
*values -min 1 -max 1 *values -min 1 -max 1
} $args]] opts values } $args]] leaders opts values
puts "opts:$opts" puts "opts:$opts"
puts "values:$values" puts "values:$values"
return [join [tcl::dict::get $values 0] [tcl::dict::get $opts -joinchar]] return [join [tcl::dict::get $values 0] [tcl::dict::get $opts -joinchar]]
@ -2936,7 +3013,7 @@ namespace eval punk::lib {
lassign [tcl::dict::values [punk::args::get_dict { lassign [tcl::dict::values [punk::args::get_dict {
*opts -any 1 *opts -any 1
-block -default {} -block -default {}
} $args]] opts valuedict } $args]] leaderdict opts valuedict
tailcall linelist {*}$opts {*}[tcl::dict::values $valuedict] tailcall linelist {*}$opts {*}[tcl::dict::values $valuedict]
} }

2
src/modules/punk/mix/commandset/module-999999.0a1.0.tm

@ -153,7 +153,7 @@ namespace eval punk::mix::commandset::module {
module -type string module -type string
}] }]
set argd [punk::args::get_dict $argspecs $args] set argd [punk::args::get_dict $argspecs $args]
lassign [dict values $argd] opts values lassign [dict values $argd] leaders opts values
set module [dict get $values module] set module [dict get $values module]
#set opts [dict merge $defaults $args] #set opts [dict merge $defaults $args]

7
src/modules/punk/nav/fs-999999.0a1.0.tm

@ -643,7 +643,7 @@ tcl::namespace::eval punk::nav::fs {
*values -min 0 -max -1 *values -min 0 -max -1
} }
set argd [punk::args::get_dict $argspecs $args] set argd [punk::args::get_dict $argspecs $args]
lassign [dict values $argd] opts values_dict lassign [dict values $argd] leaders opts values_dict
set opt_stripbase [dict get $opts -stripbase] set opt_stripbase [dict get $opts -stripbase]
set opt_formatsizes [dict get $opts -formatsizes] set opt_formatsizes [dict get $opts -formatsizes]
@ -726,6 +726,7 @@ tcl::namespace::eval punk::nav::fs {
# -searchbase is always passed through - and is only used to construct a location path if a relative searchspec was supplied # -searchbase is always passed through - and is only used to construct a location path if a relative searchspec was supplied
proc dirfiles_dict {args} { proc dirfiles_dict {args} {
set argspecs { set argspecs {
*id punk::nav::fs::dirfiles_dict
*opts -any 0 *opts -any 0
-searchbase -default "" -searchbase -default ""
-tailglob -default "\uFFFF" -tailglob -default "\uFFFF"
@ -735,7 +736,7 @@ tcl::namespace::eval punk::nav::fs {
*values -min 0 -max -1 -type string *values -min 0 -max -1 -type string
} }
set argd [punk::args::get_dict $argspecs $args] set argd [punk::args::get_dict $argspecs $args]
lassign [dict values $argd] opts vals lassign [dict values $argd] leaders opts vals
set searchspecs [dict values $vals] set searchspecs [dict values $vals]
#puts stderr "searchspecs: $searchspecs [llength $searchspecs]" #puts stderr "searchspecs: $searchspecs [llength $searchspecs]"
@ -1000,7 +1001,7 @@ tcl::namespace::eval punk::nav::fs {
*values -min 1 -max -1 -type dict *values -min 1 -max -1 -type dict
} }
set argd [punk::args::get_dict $argspecs $args] set argd [punk::args::get_dict $argspecs $args]
lassign [dict values $argd] opts vals lassign [dict values $argd] leaders opts vals
set list_of_dicts [dict values $vals] set list_of_dicts [dict values $vals]

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

@ -20,8 +20,8 @@
package require punk::lib package require punk::lib
package require punk::args package require punk::args
tcl::namespace::eval ::punk_dynamic::ns { tcl::namespace::eval ::punk::ns::evaluator {
#eval-_NS_xxx_NS_etc procs
} }
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
@ -29,6 +29,12 @@ tcl::namespace::eval punk::ns {
variable ns_current "::" variable ns_current "::"
variable ns_re_cache [dict create] ;#cache regular expressions used in globmatchns variable ns_re_cache [dict create] ;#cache regular expressions used in globmatchns
namespace export nsjoin nsprefix nstail nsparts nseval nsimport_noclobber corp namespace export nsjoin nsprefix nstail nsparts nseval nsimport_noclobber corp
catch {
package require debug
debug define punk.ns.compile
#debug on punk.ns.compile
#debug level punk.ns.compile 3
}
#leading colon makes it hard (impossible?) to call directly if not within the namespace #leading colon makes it hard (impossible?) to call directly if not within the namespace
proc ns/ {v {ns_or_glob ""} args} { proc ns/ {v {ns_or_glob ""} args} {
@ -200,16 +206,17 @@ tcl::namespace::eval punk::ns {
return $body return $body
} }
proc nseval {fqns script} { proc nseval {fqns script} {
#create one proc for each fully qualified namespace to evaluate script
if {![string match ::* $fqns]} { if {![string match ::* $fqns]} {
error "nseval only accepts a fully qualified namespace" error "nseval only accepts a fully qualified namespace"
} }
set loc [string map {:: _sep_} $fqns] set loc [string map {:: _NS_} $fqns]
#set cmd ::punk::pipecmds::nseval_$loc #set cmd ::punk::pipecmds::nseval_$loc
set cmd ::punk_dynamic::ns::eval-$loc set cmd ::punk::ns::evaluator::eval-$loc
if {$cmd ni [info commands $cmd]} { if {$cmd ni [info commands $cmd]} {
append body \n [nseval_script $fqns] append body \n [nseval_script $fqns]
proc $cmd {script} $body proc $cmd {script} $body
debug.punk.pipe.compile {proc $cmd} 6 debug.punk.ns.compile {proc $cmd} 2
} }
tailcall $cmd $script tailcall $cmd $script
} }
@ -800,14 +807,23 @@ tcl::namespace::eval punk::ns {
set chwidest1 [pipedata [list {*}$children1 ""] {lmap v $data {string length $v}} {tcl::mathfunc::max {*}$data}] #set chwidest1 [pipedata [list {*}$children1 ""] {lmap v $data {string length $v}} {tcl::mathfunc::max {*}$data}]
set chwidest2 [pipedata [list {*}$children2 ""] {lmap v $data {string length $v}} {tcl::mathfunc::max {*}$data}] set lenlist1 [lmap v [list {*}$children1 ""] {string length $v}]
set chwidest1 [tcl::mathfunc::max {*}$lenlist1]
#set chwidest2 [pipedata [list {*}$children2 ""] {lmap v $data {string length $v}} {tcl::mathfunc::max {*}$data}]
set chwidest2 [tcl::mathfunc::max {*}[lmap v [list {*}$children2 ""] {string length $v}]]
#wrap the cmd in [list] (just for the width calc) to get a proper length for what will actually be displayed #wrap the cmd in [list] (just for the width calc) to get a proper length for what will actually be displayed
set cmdwidest1 [pipedata [list {*}$elements1 ""] {lmap v $data {string length [list [lindex $v 1]]}} {tcl::mathfunc::max {*}$data}] #set cmdwidest1 [pipedata [list {*}$elements1 ""] {lmap v $data {string length [list [lindex $v 1]]}} {tcl::mathfunc::max {*}$data}]
set cmdwidest2 [pipedata [list {*}$elements2 ""] {lmap v $data {string length [list [lindex $v 1]]}} {tcl::mathfunc::max {*}$data}] set cmdwidest1 [tcl::mathfunc::max {*}[lmap v [list {*}$elements1 ""] {string length [list [lindex $v 1]]}]]
set cmdwidest3 [pipedata [list {*}$elements3 ""] {lmap v $data {string length [list [lindex $v 1]]}} {tcl::mathfunc::max {*}$data}]
set cmdwidest4 [pipedata [list {*}$elements4 ""] {lmap v $data {string length [list [lindex $v 1]]}} {tcl::mathfunc::max {*}$data}] #set cmdwidest2 [pipedata [list {*}$elements2 ""] {lmap v $data {string length [list [lindex $v 1]]}} {tcl::mathfunc::max {*}$data}]
set cmdwidest2 [tcl::mathfunc::max {*}[lmap v [list {*}$elements2 ""] {string length [list [lindex $v 1]]}]]
#set cmdwidest3 [pipedata [list {*}$elements3 ""] {lmap v $data {string length [list [lindex $v 1]]}} {tcl::mathfunc::max {*}$data}]
set cmdwidest3 [tcl::mathfunc::max {*}[lmap v [list {*}$elements3 ""] {string length [list [lindex $v 1]]}]]
#set cmdwidest4 [pipedata [list {*}$elements4 ""] {lmap v $data {string length [list [lindex $v 1]]}} {tcl::mathfunc::max {*}$data}]
set cmdwidest4 [tcl::mathfunc::max {*}[lmap v [list {*}$elements4 ""] {string length [list [lindex $v 1]]}]]
set displaylist [list] set displaylist [list]
set col1 [string repeat " " [expr {$chwidest1 + 8}]] set col1 [string repeat " " [expr {$chwidest1 + 8}]]
@ -1875,7 +1891,7 @@ tcl::namespace::eval punk::ns {
*values -min 1 -max 1 *values -min 1 -max 1
sourcepattern -type string -optional 0 sourcepattern -type string -optional 0
} }
lassign [dict values [punk::args::get_dict $argspecs $args]] opts values lassign [dict values [punk::args::get_dict $argspecs $args]] leaders opts values
set sourcepattern [dict get $values sourcepattern] set sourcepattern [dict get $values sourcepattern]
set source_ns [tcl::namespace::qualifiers $sourcepattern] set source_ns [tcl::namespace::qualifiers $sourcepattern]

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

@ -662,7 +662,7 @@ namespace eval punk::path {
*values -min 0 -max -1 -optional 1 -type string *values -min 0 -max -1 -optional 1 -type string
tailglobs -multiple 1 tailglobs -multiple 1
} $args] } $args]
lassign [dict values $argd] opts values lassign [dict values $argd] leaders opts values
set tailglobs [dict values $values] set tailglobs [dict values $values]
# -- --- --- --- --- --- --- # -- --- --- --- --- --- ---
set opt_antiglob_paths [dict get $opts -antiglob_paths] set opt_antiglob_paths [dict get $opts -antiglob_paths]

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

@ -2857,7 +2857,7 @@ namespace eval repl {
set ::argc 0 set ::argc 0
set ::argv {} set ::argv {}
set ::auto_path %autopath% set ::auto_path %autopath%
#puts stdout "safe interp" #puts stdout "safebase interp"
#flush stdout #flush stdout
namespace eval ::codeinterp { namespace eval ::codeinterp {
variable errstack {} variable errstack {}
@ -2879,6 +2879,17 @@ namespace eval repl {
} else { } else {
interp share {} [shellfilter::stack::item_tophandle stderr] code interp share {} [shellfilter::stack::item_tophandle stderr] code
} }
interp eval code {
package require punk::lib
package require textblock ;#may fail to load term::ansi::code::macros - (only required for altg)
}
#JMN
interp eval code {
package require shellfilter
}
#work around bug in safe base which won't load Tcl libs that have deeper nesting #work around bug in safe base which won't load Tcl libs that have deeper nesting
#(also affects tcllib page/plugins folder) #(also affects tcllib page/plugins folder)
set termversions [package versions term] set termversions [package versions term]
@ -2896,13 +2907,15 @@ namespace eval repl {
} }
#code invokehidden source c:/repo/jn/shellspy/modules/punk/lib-0.1.1.tm #code invokehidden source c:/repo/jn/shellspy/modules/punk/lib-0.1.1.tm
code alias detok ::safe::DetokPath code code alias detok ::safe::DetokPath code ;#temp - this violates the point of the {$p(:X:)} paths
#review - exit should do something slightly different #review - exit should do something slightly different
# see ::safe::interpDelete # see ::safe::interpDelete
code alias exit ::repl::interphelpers::quit code alias exit ::repl::interphelpers::quit
code alias ::md5::md5 ::repl::interphelpers::md5 code alias ::md5::md5 ::repl::interphelpers::md5
code alias ::fconfigure ::fconfigure ;#needed for shellfilter
code alias ::file ::file
interp eval code [list package provide md5 $md5version] interp eval code [list package provide md5 $md5version]
} else { } else {
interp create code interp create code

1491
src/modules/punk/safe-999999.0a1.0.tm

File diff suppressed because it is too large Load Diff

3
src/modules/punk/safe-buildversion.txt

@ -0,0 +1,3 @@
0.1.0
#First line must be a semantic version number
#all other lines are ignored.

36
src/modules/shellfilter-0.1.9.tm

@ -13,12 +13,24 @@
tcl::namespace::eval shellfilter::log { tcl::namespace::eval shellfilter::log {
variable allow_adhoc_tags 1 variable allow_adhoc_tags 1
variable open_logs [tcl::dict::create] variable open_logs [tcl::dict::create]
variable is_enabled 0
proc disable {} {
variable is_enabled
set is_enabled 0
proc ::shellfilter::log::open {tag settingsdict} {}
proc ::shellfilter::log::write {tag msg} {}
proc ::shellfilter::log::write_sync {tag msg} {}
proc ::shellfilter::log::close {tag} {}
}
proc enable {} {
variable is_enabled
set is_enabled 1
#'tag' is an identifier for the log source. #'tag' is an identifier for the log source.
# each tag will use it's own thread to write to the configured log target # each tag will use it's own thread to write to the configured log target
proc open {tag {settingsdict {}}} { proc ::shellfilter::log::open {tag {settingsdict {}}} {
upvar ::shellfilter::sources sourcelist upvar ::shellfilter::sources sourcelist
package require shellthread
if {![dict exists $settingsdict -tag]} { if {![dict exists $settingsdict -tag]} {
tcl::dict::set settingsdict -tag $tag tcl::dict::set settingsdict -tag $tag
} else { } else {
@ -36,7 +48,7 @@ tcl::namespace::eval shellfilter::log {
#puts stderr "shellfilter::log::open this_threadid: [thread::id] tag: $tag worker_tid: $worker_tid" #puts stderr "shellfilter::log::open this_threadid: [thread::id] tag: $tag worker_tid: $worker_tid"
return $worker_tid return $worker_tid
} }
proc write {tag msg} { proc ::shellfilter::log::write {tag msg} {
upvar ::shellfilter::sources sourcelist upvar ::shellfilter::sources sourcelist
variable allow_adhoc_tags variable allow_adhoc_tags
if {!$allow_adhoc_tags} { if {!$allow_adhoc_tags} {
@ -47,14 +59,16 @@ tcl::namespace::eval shellfilter::log {
shellthread::manager::write_log $tag $msg shellthread::manager::write_log $tag $msg
} }
#write_sync - synchronous processing with logging thread, slower but potentially useful for debugging/testing or forcing delay til log written #write_sync - synchronous processing with logging thread, slower but potentially useful for debugging/testing or forcing delay til log written
proc write_sync {tag msg} { proc ::shellfilter::log::write_sync {tag msg} {
shellthread::manager::write_log $tag $msg -async 0 shellthread::manager::write_log $tag $msg -async 0
} }
proc close {tag} { proc ::shellfilter::log::close {tag} {
#shellthread::manager::close_worker $tag #shellthread::manager::close_worker $tag
shellthread::manager::unsubscribe [list $tag]; #workertid will be added back to free list if no tags remain subscribed shellthread::manager::unsubscribe [list $tag]; #workertid will be added back to free list if no tags remain subscribed
} }
}
#review #review
#configure whether we can call shellfilter::log::write without having called open first #configure whether we can call shellfilter::log::write without having called open first
proc require_open {{is_open_required {}}} { proc require_open {{is_open_required {}}} {
@ -73,6 +87,12 @@ tcl::namespace::eval shellfilter::log {
} }
} }
} }
if {[catch {package require shellthread}]} {
shellfilter::log::disable
} else {
shellfilter::log::enable
}
} }
namespace eval shellfilter::pipe { namespace eval shellfilter::pipe {
#write channel for program. workerthread reads other end of fifo2 and writes data somewhere #write channel for program. workerthread reads other end of fifo2 and writes data somewhere
@ -1594,7 +1614,13 @@ namespace eval shellfilter::stack {
set tag "SHELLFILTER::STACK" set tag "SHELLFILTER::STACK"
#JMN - load from config #JMN - load from config
#::shellfilter::log::open $tag {-syslog 127.0.0.1:514} #::shellfilter::log::open $tag {-syslog 127.0.0.1:514}
if {[catch {
::shellfilter::log::open $tag {-syslog ""} ::shellfilter::log::open $tag {-syslog ""}
} err]} {
#e.g safebase interp can't load required modules such as shellthread (or Thread)
puts stderr "shellfilter::show_pipeline cannot open log"
return
}
::shellfilter::log::write $tag "transform stack for $pipename $args" ::shellfilter::log::write $tag "transform stack for $pipename $args"
foreach tf $stack { foreach tf $stack {
::shellfilter::log::write $tag " $tf" ::shellfilter::log::write $tag " $tf"

181
src/modules/termscheme-999999.0a1.0.tm

@ -0,0 +1,181 @@
# -*- tcl -*-
# Maintenance Instruction: leave the 999999.xxx.x as is and use punkshell 'dev make' or bin/punkmake to update from <pkg>-buildversion.txt
# module template: shellspy/src/decktemplates/vendor/punk/modules/template_module-0.0.3.tm
#
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem.
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# (C) 2024
#
# @@ Meta Begin
# Application termscheme 999999.0a1.0
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[manpage_begin shellspy_module_termscheme 0 999999.0a1.0]
#[copyright "2024"]
#[titledesc {Module API}] [comment {-- Name section and table of contents description --}]
#[moddesc {-}] [comment {-- Description at end of page heading --}]
#[require termscheme]
#[keywords module]
#[description]
#[para] -
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section Overview]
#[para] overview of termscheme
#[subsection Concepts]
#[para] -
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[subsection dependencies]
#[para] packages used by termscheme
#[list_begin itemized]
package require Tcl 8.6-
#*** !doctools
#[item] [package {Tcl 8.6}]
# #package require frobz
# #*** !doctools
# #[item] [package {frobz}]
#*** !doctools
#[list_end]
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section API]
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# oo::class namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#tcl::namespace::eval termscheme::class {
#*** !doctools
#[subsection {Namespace termscheme::class}]
#[para] class definitions
#if {[tcl::info::commands [tcl::namespace::current]::interface_sample1] eq ""} {
#*** !doctools
#[list_begin enumerated]
# oo::class create interface_sample1 {
# #*** !doctools
# #[enum] CLASS [class interface_sample1]
# #[list_begin definitions]
# method test {arg1} {
# #*** !doctools
# #[call class::interface_sample1 [method test] [arg arg1]]
# #[para] test method
# puts "test: $arg1"
# }
# #*** !doctools
# #[list_end] [comment {-- end definitions interface_sample1}]
# }
#*** !doctools
#[list_end] [comment {--- end class enumeration ---}]
#}
#}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Base namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
tcl::namespace::eval termscheme {
tcl::namespace::export {[a-z]*} ;# Convention: export all lowercase
#variable xyz
#*** !doctools
#[subsection {Namespace termscheme}]
#[para] Core API functions for termscheme
#[list_begin definitions]
#proc sample1 {p1 n args} {
# #*** !doctools
# #[call [fun sample1] [arg p1] [arg n] [opt {option value...}]]
# #[para]Description of sample1
# #[para] Arguments:
# # [list_begin arguments]
# # [arg_def tring p1] A description of string argument p1.
# # [arg_def integer n] A description of integer argument n.
# # [list_end]
# return "ok"
#}
#*** !doctools
#[list_end] [comment {--- end definitions namespace termscheme ---}]
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Secondary API namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
tcl::namespace::eval termscheme::lib {
tcl::namespace::export {[a-z]*} ;# Convention: export all lowercase
tcl::namespace::path [tcl::namespace::parent]
#*** !doctools
#[subsection {Namespace termscheme::lib}]
#[para] Secondary functions that are part of the API
#[list_begin definitions]
#proc utility1 {p1 args} {
# #*** !doctools
# #[call lib::[fun utility1] [arg p1] [opt {?option value...?}]]
# #[para]Description of utility1
# return 1
#}
#*** !doctools
#[list_end] [comment {--- end definitions namespace termscheme::lib ---}]
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section Internal]
#tcl::namespace::eval termscheme::system {
#*** !doctools
#[subsection {Namespace termscheme::system}]
#[para] Internal functions that are not part of the API
#}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
package provide termscheme [tcl::namespace::eval termscheme {
variable pkg termscheme
variable version
set version 999999.0a1.0
}]
return
#*** !doctools
#[manpage_end]

3
src/modules/termscheme-buildversion.txt

@ -0,0 +1,3 @@
0.1.0
#First line must be a semantic version number
#all other lines are ignored.

13
src/modules/textblock-999999.0a1.0.tm

@ -62,7 +62,14 @@ catch {package require patternpunk}
package require overtype package require overtype
#safebase interps as at 2024-08 can't access deeper paths - even though they are below the supposed safe list. #safebase interps as at 2024-08 can't access deeper paths - even though they are below the supposed safe list.
if {[catch {
package require term::ansi::code::macros ;#required for frame if old ansi g0 used - review - make package optional? package require term::ansi::code::macros ;#required for frame if old ansi g0 used - review - make package optional?
} errM]} {
#catch this too in case stderr not available
catch {
puts stderr "textblock package failed to load term::ansi::code::macros with error: $errM"
}
}
package require textutil package require textutil
@ -5182,7 +5189,7 @@ tcl::namespace::eval textblock {
lassign [punk::args::get_dict { lassign [punk::args::get_dict {
-ansiresets -default 1 -type integer -ansiresets -default 1 -type integer
blocks -type string -multiple 1 blocks -type string -multiple 1
} $args] _o opts _v values } $args] _l leaders _o opts _v values
set blocks [tcl::dict::get $values blocks] set blocks [tcl::dict::get $values blocks]
set idx 0 set idx 0
@ -5844,7 +5851,7 @@ tcl::namespace::eval textblock {
*values -min 1 *values -min 1
frametype -choices "<ftlist>" -choiceprefix 0 -choicerestricted 0 -type dict\ frametype -choices "<ftlist>" -choiceprefix 0 -choicerestricted 0 -type dict\
-help "name from the predefined frametypes or an adhoc dictionary." -help "name from the predefined frametypes or an adhoc dictionary."
memberglob -type globstring -multiple 1 -choiceprefix 0 -choicerestricted 0 -choices { memberglob -type globstring -optional 1 -multiple 1 -choiceprefix 0 -choicerestricted 0 -choices {
corner noncorner top bottom vertical horizontal left right corner noncorner top bottom vertical horizontal left right
hl hlt hlb vsl vll vlr tlc trc blc brc hltj hlbj vllj vlrj hl hlt hlb vsl vll vlr tlc trc blc brc hltj hlbj vllj vlrj
}\ }\
@ -8110,7 +8117,7 @@ tcl::namespace::eval textblock {
Whereas for a block size of 24, -max_cross_size of 1,2,3,4,6,8,12 or 24 will work. (all divisors) Whereas for a block size of 24, -max_cross_size of 1,2,3,4,6,8,12 or 24 will work. (all divisors)
If a number chosen for -max_cross_size isn't a divisor, the largest divisor below the chosen value will be used. If a number chosen for -max_cross_size isn't a divisor, the largest divisor below the chosen value will be used.
" "
*values -min 1 -max 1 *values -min 0 -max 1
size -default 1 -type integer size -default 1 -type integer
} }
proc gcross {args} { proc gcross {args} {

Loading…
Cancel
Save