Browse Source

punk::ansi and overtype fixes and tests, grepstr fixes, punk::args synopsis improvements, voo module

master
Julian Noble 2 months ago
parent
commit
4c6a3058c3
  1. 405
      src/bootsupport/modules/overtype-1.7.4.tm
  2. 256
      src/bootsupport/modules/punk-0.1.tm
  3. 4
      src/bootsupport/modules/punk/aliascore-0.1.0.tm
  4. 1542
      src/bootsupport/modules/punk/ansi-0.1.1.tm
  5. 2
      src/bootsupport/modules/punk/ansi/colourmap-0.1.0.tm
  6. 604
      src/bootsupport/modules/punk/args-0.2.1.tm
  7. 246
      src/bootsupport/modules/punk/args/moduledoc/tclcore-0.1.0.tm
  8. 21
      src/bootsupport/modules/punk/auto_exec-0.1.0.tm
  9. 6
      src/bootsupport/modules/punk/char-0.1.0.tm
  10. 20
      src/bootsupport/modules/punk/console-0.1.1.tm
  11. 438
      src/bootsupport/modules/punk/lib-0.1.6.tm
  12. 1
      src/bootsupport/modules/punk/libunknown-0.1.tm
  13. 8
      src/bootsupport/modules/punk/mix/commandset/repo-0.1.0.tm
  14. 89
      src/bootsupport/modules/punk/ns-0.1.0.tm
  15. 41
      src/bootsupport/modules/punk/repl-0.1.2.tm
  16. 4
      src/bootsupport/modules/punk/repo-0.1.1.tm
  17. 4
      src/bootsupport/modules/punkcheck-0.1.0.tm
  18. 42
      src/bootsupport/modules/shellfilter-0.2.1.tm
  19. 39
      src/bootsupport/modules/textblock-0.1.3.tm
  20. 405
      src/modules/overtype-999999.0a1.0.tm
  21. 256
      src/modules/punk-0.1.tm
  22. 4
      src/modules/punk/aliascore-999999.0a1.0.tm
  23. 1542
      src/modules/punk/ansi-999999.0a1.0.tm
  24. 2
      src/modules/punk/ansi/colourmap-999999.0a1.0.tm
  25. 604
      src/modules/punk/args-999999.0a1.0.tm
  26. 246
      src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm
  27. 21
      src/modules/punk/auto_exec-999999.0a1.0.tm
  28. 88
      src/modules/punk/basictelnet-999999.0a1.0.tm
  29. 6
      src/modules/punk/char-999999.0a1.0.tm
  30. 20
      src/modules/punk/console-999999.0a1.0.tm
  31. 71
      src/modules/punk/imap4-999999.0a1.0.tm
  32. 438
      src/modules/punk/lib-999999.0a1.0.tm
  33. 1
      src/modules/punk/libunknown-0.1.tm
  34. 2
      src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/modules/template_test-0.0.1.tm
  35. 8
      src/modules/punk/mix/commandset/repo-999999.0a1.0.tm
  36. 89
      src/modules/punk/ns-999999.0a1.0.tm
  37. 41
      src/modules/punk/repl-999999.0a1.0.tm
  38. 4
      src/modules/punk/repo-999999.0a1.0.tm
  39. 2
      src/modules/punk/safe-999999.0a1.0.tm
  40. 4
      src/modules/punkcheck-0.1.0.tm
  41. 42
      src/modules/shellfilter-999999.0a1.0.tm
  42. 35
      src/modules/test/#modpod-overtype-999999.0a1.0/overtype-1.7.4_testsuites/overtype/renderline.test
  43. 0
      src/modules/test/#modpod-overtype-999999.0a1.0/overtype-1.7.4_testsuites/tests/renderline.test#..+overtype+renderline.test.fauxlink
  44. 139
      src/modules/test/#modpod-overtype-999999.0a1.0/overtype-999999.0a1.0.tm
  45. 3
      src/modules/test/overtype-buildversion.txt
  46. 158
      src/modules/test/punk/#modpod-ansi-999999.0a1.0/ansi-0.1.1_testsuites/ansi/ansistrip.test
  47. 1216
      src/modules/test/punk/#modpod-args-999999.0a1.0/args-0.1.5_testsuites/args/args.test
  48. 388
      src/modules/test/punk/#modpod-args-999999.0a1.0/args-0.1.5_testsuites/args/choices.test
  49. 254
      src/modules/test/punk/#modpod-args-999999.0a1.0/args-0.1.5_testsuites/args/define.test
  50. 448
      src/modules/test/punk/#modpod-args-999999.0a1.0/args-0.1.5_testsuites/args/mashopts.test
  51. 150
      src/modules/test/punk/#modpod-args-999999.0a1.0/args-0.1.5_testsuites/args/opts.test
  52. 326
      src/modules/test/punk/#modpod-args-999999.0a1.0/args-0.1.5_testsuites/args/synopsis.test
  53. 67
      src/modules/test/punk/#modpod-lib-999999.0a1.0/lib-0.1.3_testsuites/lib/index_functions.test
  54. 57
      src/modules/test/punk/#modpod-lib-999999.0a1.0/lib-0.1.3_testsuites/lib/lineprocessing.test
  55. 39
      src/modules/textblock-999999.0a1.0.tm
  56. 405
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/overtype-1.7.4.tm
  57. 256
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk-0.1.tm
  58. 4
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/aliascore-0.1.0.tm
  59. 1542
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/ansi-0.1.1.tm
  60. 2
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/ansi/colourmap-0.1.0.tm
  61. 604
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/args-0.2.1.tm
  62. 246
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/args/moduledoc/tclcore-0.1.0.tm
  63. 21
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/auto_exec-0.1.0.tm
  64. 6
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/char-0.1.0.tm
  65. 20
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/console-0.1.1.tm
  66. 438
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/lib-0.1.6.tm
  67. 1
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/libunknown-0.1.tm
  68. 8
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/commandset/repo-0.1.0.tm
  69. 89
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/ns-0.1.0.tm
  70. 41
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/repl-0.1.2.tm
  71. 4
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/repo-0.1.1.tm
  72. 4
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punkcheck-0.1.0.tm
  73. 42
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/shellfilter-0.2.1.tm
  74. 39
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/textblock-0.1.3.tm
  75. 405
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/overtype-1.7.4.tm
  76. 256
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk-0.1.tm
  77. 4
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/aliascore-0.1.0.tm
  78. 1542
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/ansi-0.1.1.tm
  79. 2
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/ansi/colourmap-0.1.0.tm
  80. 604
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/args-0.2.1.tm
  81. 246
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/args/moduledoc/tclcore-0.1.0.tm
  82. 21
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/auto_exec-0.1.0.tm
  83. 6
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/char-0.1.0.tm
  84. 20
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/console-0.1.1.tm
  85. 438
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/lib-0.1.6.tm
  86. 1
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/libunknown-0.1.tm
  87. 8
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/commandset/repo-0.1.0.tm
  88. 89
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/ns-0.1.0.tm
  89. 41
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/repl-0.1.2.tm
  90. 4
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/repo-0.1.1.tm
  91. 4
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punkcheck-0.1.0.tm
  92. 42
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/shellfilter-0.2.1.tm
  93. 39
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/textblock-0.1.3.tm
  94. 1
      src/vendormodules/include_modules.config
  95. 764
      src/vendormodules/voo-1.0.0.tm
  96. 405
      src/vfs/_vfscommon.vfs/modules/overtype-1.7.4.tm
  97. 256
      src/vfs/_vfscommon.vfs/modules/punk-0.1.tm
  98. 4
      src/vfs/_vfscommon.vfs/modules/punk/aliascore-0.1.0.tm
  99. 1542
      src/vfs/_vfscommon.vfs/modules/punk/ansi-0.1.1.tm
  100. 2
      src/vfs/_vfscommon.vfs/modules/punk/ansi/colourmap-0.1.0.tm
  101. Some files were not shown because too many files have changed in this diff Show More

405
src/bootsupport/modules/overtype-1.7.4.tm

@ -90,7 +90,9 @@ package require punk::assertion
# - need to extract and replace ansi codes?
tcl::namespace::eval overtype {
namespace import ::punk::assertion::assert
if {[info commands ::overtype::assert] eq ""} {
namespace import ::punk::assertion::assert
}
punk::assertion::active true
namespace path ::punk::lib
@ -625,7 +627,7 @@ tcl::namespace::eval overtype {
#set overtext [lpop inputchunks 0] ;#could be a list 'ansisplit' or text 'plain|mixed'
lassign [lpop inputchunks 0] overtext_type overtext
#use eq test with emptystring instead of 'string length' - test for emptiness shouldn't cause shimmering if popped inputchunks member if an 'ansisplit' list
#use eq test with emptystring instead of 'string length' - test for emptiness shouldn't cause shimmering if popped inputchunks member is an 'ansisplit' list
if {$overtext eq ""} {
incr loop
continue
@ -728,7 +730,7 @@ tcl::namespace::eval overtype {
set existing_reverse_state 0
#split_codes_single is single esc sequence - but could have multiple sgr codes within one esc sequence
#e.g \x1b\[0;31;7m has a reset,colour red and reverse
set codeinfo [punk::ansi::codetype::sgr_merge [list $replay_codes_overlay] -info 1]
set codeinfo [punk::ansi::codetype::sgr_merge [list $replay_codes_overlay] -info]
set codestate_reverse [dict get $codeinfo codestate reverse]
switch -- $codestate_reverse {
7 {
@ -863,7 +865,7 @@ tcl::namespace::eval overtype {
# ----
# review
set col $post_render_col
#just because it's out of range of the renderwidth - doesn't mean a move down should jump to witin the range - 2025
#just because it's out of range of the renderwidth - doesn't mean a move down should jump to within the range - 2025
#----
#set existingdata [lindex $outputlines [expr {$post_render_row -1}]]
@ -908,7 +910,7 @@ tcl::namespace::eval overtype {
#It would perhaps be more properly handled as a queue of instructions from our initial renderline call
#we don't need to worry about overflow next call (?)- but we should carry forward our gx and ansi stacks
puts stdout ">>>[a+ red bold]overflow_right during restore_cursor[a]"
puts stdout ">>>renderspace<<<[a+ red bold]overflow_right during restore_cursor[a]"
set sub_info [overtype::renderline\
-info 1\
@ -924,7 +926,7 @@ tcl::namespace::eval overtype {
tcl::dict::set vtstate autowrap_mode [tcl::dict::get $sub_info autowrap_mode] ;#nor this..
#todo!!!
# 2025 fix - this does nothing - so what uses it?? create a test!
# 2025 fix - this does nothing - so what is the intention?? create a test!
linsert outputlines $renderedrow $foldline
#review - row & col set by restore - but not if there was no save..
}
@ -1053,7 +1055,9 @@ tcl::namespace::eval overtype {
set overflow_right ""
} else {
if {[tcl::dict::get $vtstate autowrap_mode]} {
set outputlines [linsert $outputlines $renderedrow $overflow_right]
#set outputlines [linsert $outputlines $renderedrow $overflow_right]
#ledit outputlines $renderedrow $renderedrow-1 $overflow_right
ledit outputlines $renderedrow -1 $overflow_right
set overflow_right ""
set row [expr {$renderedrow + 2}]
} else {
@ -1150,7 +1154,8 @@ tcl::namespace::eval overtype {
if {$insert_lines_above > 0} {
set row $renderedrow
#set outputlines [linsert $outputlines $renderedrow-1 {*}[lrepeat $insert_lines_above ""]]
ledit outputlines $renderedrow-1 $renderedrow-2 {*}[lrepeat $insert_lines_above ""]
#ledit outputlines $renderedrow-1 $renderedrow-2 {*}[lrepeat $insert_lines_above ""]
ledit outputlines $renderedrow-1 -1 {*}[lrepeat $insert_lines_above ""]
incr row [expr {$insert_lines_above -1}] ;#we should end up on the same line of text (at a different index), with new empties inserted above
#? set row $post_render_row #can renderline tell us?
}
@ -1461,6 +1466,7 @@ tcl::namespace::eval overtype {
set nextprefix_list $overflow_right_pt_code_pt
} else {
#merge tail and head
#ledit <list> end end <val> will work with empty list (ledit <list> end <val> does not)
ledit nextprefix_list end end "[lindex $nextprefix_list end][lindex $overflow_right_pt_code_pt 0]"
lappend nextprefix_list {*}[lrange $overflow_right_pt_code_pt 1 end]
}
@ -1476,16 +1482,17 @@ tcl::namespace::eval overtype {
}
if 0 {
if {$nextprefix ne ""} {
set nextoveridx [expr {$overidx+1}]
if {$nextoveridx >= [llength $inputchunks]} {
lappend inputchunks $nextprefix
} else {
#lset overlines $nextoveridx $nextprefix[lindex $overlines $nextoveridx]
set inputchunks [linsert $inputchunks $nextoveridx $nextprefix]
if {$nextprefix ne ""} {
set nextoveridx [expr {$overidx+1}]
if {$nextoveridx >= [llength $inputchunks]} {
lappend inputchunks $nextprefix
} else {
#lset overlines $nextoveridx $nextprefix[lindex $overlines $nextoveridx]
#set inputchunks [linsert $inputchunks $nextoveridx $nextprefix]
ledit inputchunks $nextoveridx -1 $nextprefix
}
}
}
}
if {[llength $nextprefix_list]} {
#set inputchunks [linsert $inputchunks 0 $nextprefix]
@ -1669,13 +1676,17 @@ tcl::namespace::eval overtype {
}
}
}
lappend outputlines $rendered
#JULZ
#lappend outputlines $rendered
lappend outputlines $rendered\x1b\[m
#lappend outputlines [renderline -insert_mode 0 -transparent $opt_transparent $undertext $overtext]
} else {
#background block is wider than or equal to data for this line
#lappend outputlines [renderline -insert_mode 0 -startcolumn [expr {$left_exposed + 1}] -transparent $opt_transparent -exposed1 $opt_exposed1 -exposed2 $opt_exposed2 $undertext $overtext]
set rinfo [renderline -info 1 -insert_mode 0 -startcolumn [expr {$left_exposed + 1}] -transparent $opt_transparent -exposed1 $opt_exposed1 -exposed2 $opt_exposed2 $undertext $overtext]
lappend outputlines [tcl::dict::get $rinfo result]
#JULZ
#lappend outputlines [tcl::dict::get $rinfo result]
lappend outputlines [tcl::dict::get $rinfo result]\x1b\[m
}
set replay_codes_underlay [tcl::dict::get $rinfo replay_codes_underlay]
set replay_codes_overlay [tcl::dict::get $rinfo replay_codes_overlay]
@ -1787,6 +1798,9 @@ tcl::namespace::eval overtype {
set overflowlength [expr {$overtext_datalen - $renderwidth}]
if {$overflowlength > 0} {
#raw overtext wider than undertext column
#broken:
#todo - renderline -overflow is invalid.
# we need renderline to support -expand_left ??
set rinfo [renderline\
-info 1\
-insert_mode 0\
@ -1814,13 +1828,18 @@ tcl::namespace::eval overtype {
}
}
}
lappend outputlines $rendered
#JULZ
#lappend outputlines $rendered
lappend outputlines $rendered\x1b\[m
} else {
#padded overtext
#lappend outputlines [renderline -insert_mode 0 -transparent $opt_transparent -startcolumn [expr {$left_exposed + 1}] $undertext $overtext]
#Note - we still need overflow(exapnd_right) here - as although the overtext is short - it may oveflow due to the startoffset
set rinfo [renderline -info 1 -insert_mode 0 -transparent $opt_transparent -expand_right $opt_overflow -startcolumn [expr {$left_exposed + 1 + $startoffset}] $undertext $overtext]
lappend outputlines [tcl::dict::get $rinfo result]
#JULZ
#lappend outputlines [tcl::dict::get $rinfo result]
lappend outputlines [tcl::dict::get $rinfo result]\x1b\[m
}
set replay_codes [tcl::dict::get $rinfo replay_codes]
set replay_codes_underlay [tcl::dict::get $rinfo replay_codes_underlay]
@ -2014,7 +2033,8 @@ tcl::namespace::eval overtype {
# }
#}
}
lappend outputlines $rendered
#JULZ
lappend outputlines $rendered\x1b\[m
} else {
#padded overtext
#lappend outputlines [renderline -insert_mode 0 -transparent $opt_transparent -startcolumn [expr {$left_exposed + 1}] $undertext $overtext]
@ -2023,7 +2043,9 @@ tcl::namespace::eval overtype {
#puts stderr "--> [ansistring VIEW -lf 1 -nul 1 $rinfo] <--"
set overflow_right [tcl::dict::get $rinfo overflow_right]
set unapplied [tcl::dict::get $rinfo unapplied]
lappend outputlines [tcl::dict::get $rinfo result]
#JULZ
#lappend outputlines [tcl::dict::get $rinfo result]
lappend outputlines [tcl::dict::get $rinfo result]\x1b\[m
}
set replay_codes [tcl::dict::get $rinfo replay_codes]
set replay_codes_underlay [tcl::dict::get $rinfo replay_codes_underlay]
@ -2136,6 +2158,24 @@ tcl::namespace::eval overtype {
}]
}
proc stack_eq {a b} {
#single level list equality test to avoid generating internal string representations of the lists unnecessarily.
if {[llength $a] != [llength $b]} {
return 0
}
foreach code1 $a code2 $b {
if {$code1 ne $code2} {
return 0
}
}
return 1
}
#todo: tests
#set j [overtype::renderline -transparent " " -insert_mode 0 -expand_right 1 "[a+ red underline]xxx[a+ blue][a+ nounderline]" "[a green]J" ]yyy
# yyy should be blue with no underline - and the J should be green - and the x's should be red with underline and the J should overwrite the first x
#At the moment we return a reset at the end of the renderline result instead of the replay codes.
proc renderline {args} {
#todo - fix 'unapplied' mechanism.This is particularly inefficient for long lines, or data such as binarytext which is not line-based.
#All unapplied data is re-split/reprocessed repeatedly for each line! This is very wasteful and slow.
@ -2476,7 +2516,9 @@ tcl::namespace::eval overtype {
if {$maybemouse ne "<" && [tcl::string::index $code end] eq "m"} {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set u_codestack [list "\x1b\[m"]
#will normalize all resets to the same code - including 8bit reset.
#set u_codestack [list "\x1b\[m"]
set u_codestack [list $code]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set u_codestack [list $code]
} else {
@ -2557,6 +2599,17 @@ tcl::namespace::eval overtype {
}
}
#----------------------------------------
#set test_c [showlist $undercols]
##set test_s [showlist $understacks %ansiview]
#set sview [list]
#foreach us $understacks {
# lappend sview [ansistring VIEW $us]
#}
#set test_s [showlist $sview]
#puts stderr "undercols/stacks:\n[textblock::join -- $test_c " " $test_s]"
#----------------------------------------
if {$opt_width ne "\uFFEF"} {
set renderwidth $opt_width
} else {
@ -2567,7 +2620,10 @@ tcl::namespace::eval overtype {
#trailing codes in effect for underlay
if {[llength $u_codestack]} {
#set replay_codes_underlay [join $u_codestack ""]
set replay_codes_underlay [punk::ansi::codetype::sgr_merge_list {*}$u_codestack]
#set replay_codes_underlay [punk::ansi::codetype::sgr_merge_list {*}$u_codestack]
#u_codestack was built from codes split using split_codes_single
#- so should already be simplified to single codes with no multiple SGR params in one code
set replay_codes_underlay [punk::ansi::codetype::sgr_merge_singles $u_codestack]
} else {
set replay_codes_underlay ""
}
@ -2767,13 +2823,17 @@ tcl::namespace::eval overtype {
} else {
lappend overlay_grapheme_control_stacks $o_codestack
#there will always be an empty code at end due to foreach on 2 vars with odd-sized list ending with pt (overmap coming from perlish split)
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set o_codestack [list "\x1b\[m"] ;#reset better than empty list - fixes some ansi art issues
set code_endswith_m [expr {[tcl::string::index $code end] eq "m"}] ;#skip SGR regexp testing for cases that don't end with m - as they can't be SGR
if {$code_endswith_m && [punk::ansi::codetype::is_sgr_reset $code]} {
#reset better than empty list - fixes some ansi art issues
#set o_codestack [list "\x1b\[m"]
set o_codestack [list $code]
lappend overlay_grapheme_control_list [list sgr $code]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
} elseif {$code_endswith_m && [punk::ansi::codetype::has_sgr_leadingreset $code]} {
set o_codestack [list $code]
lappend overlay_grapheme_control_list [list sgr $code]
} elseif {[priv::is_sgr $code]} {
} elseif {$code_endswith_m && [priv::is_sgr $code]} {
#basic simplification first - remove straight dupes
set dup_posns [lsearch -all -exact $o_codestack $code] ;#must be -exact because of square-bracket glob chars
set o_codestack [lremove $o_codestack {*}$dup_posns]
@ -2827,7 +2887,12 @@ tcl::namespace::eval overtype {
lappend overstacks_gx $o_gxstack
#set replay_codes_overlay [join $o_codestack ""]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}$o_codestack]
if {[llength $o_codestack]} {
#set replay_codes_overlay [join $o_codestack ""]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_singles $o_codestack]
} else {
set replay_codes_overlay [list]
}
#if {[tcl::dict::exists $overstacks $max_overlay_grapheme_index]} {
# set replay_codes_overlay [join [tcl::dict::get $overstacks $max_overlay_grapheme_index] ""]
@ -2952,7 +3017,7 @@ tcl::namespace::eval overtype {
#specials - each shoud have it's own test of what to do if it happens after overflow_idx reached
switch -- $chtest {
"<lf>" {
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
if {$idx == 0} {
#puts "---a <lf> at col 1"
#linefeed at column 1
@ -3069,8 +3134,7 @@ tcl::namespace::eval overtype {
set next_gc [lindex $overlay_grapheme_control_list $gci+1] ;#next grapheme or control
lassign $next_gc next_type next_item
if {$autowrap_mode} {
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci-1]]
#set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci-1]]
#don't incr idx beyond the overflow_idx
#idx_over already incremented - decrement so current overlay grapheme stacks go to unapplied
incr idx_over -1
@ -3087,7 +3151,7 @@ tcl::namespace::eval overtype {
#no point throwing back to caller for each grapheme that is overflowing
#without this branch - renderline would be called with overtext reducing only by one grapheme per call
#processing a potentially long overtext each time (ie - very slow)
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#JMN4
}
@ -3427,7 +3491,7 @@ tcl::namespace::eval overtype {
switch -exact -- $code_end {
A {
#Row move - up
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#todo
lassign [split $param {;}] num modifierkey
if {$modifierkey ne ""} {
@ -3452,7 +3516,7 @@ tcl::namespace::eval overtype {
#CUD - Cursor Down
#Row move - down
lassign [split $param {;}] num modifierkey
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#move down
if {$modifierkey ne ""} {
puts stderr "modifierkey:$modifierkey"
@ -3503,7 +3567,7 @@ tcl::namespace::eval overtype {
incr cursor_column $num
} else {
if {$autowrap_mode} {
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#jmn
if {$idx == $overflow_idx} {
incr num
@ -3598,7 +3662,7 @@ tcl::namespace::eval overtype {
set cursor_column 1
set idx 0
} else {
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
incr cursor_column -$num
priv::render_to_unapplied $overlay_grapheme_control_list $gci
set instruction wrapmovebackward
@ -3626,7 +3690,9 @@ tcl::namespace::eval overtype {
set cursor_column 1
set cursor_row [expr {$cursor_row + $downmove}]
set idx [expr {$cursor_column -1}]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
#sgr_merge_list
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#sgr_merge_singles ??
incr idx_over
priv::render_to_unapplied $overlay_grapheme_control_list $gci
set instruction move
@ -3647,7 +3713,7 @@ tcl::namespace::eval overtype {
set cursor_row 1
}
set idx [expr {$cursor_column - 1}]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
incr idx_over
priv::render_to_unapplied $overlay_grapheme_control_list $gci
set instruction move
@ -3656,6 +3722,7 @@ tcl::namespace::eval overtype {
}
G {
#CHA - Cursor Horizontal Absolute (move to absolute column no)
#see also HPA - Horizontal Position Absolute (same functionality)
if {$param eq ""} {
set targetcol 1
} else {
@ -3680,6 +3747,29 @@ tcl::namespace::eval overtype {
set cursor_column $targetcol
#puts stderr "renderline absolute col move ESC G (TEST)"
}
` {
#https://vt100.net/docs/vt510-rm/HPA.html
#docs don't mention that it defaults to one if $parm omitted - but it seems to do in practice
if {$param eq ""} {
set targetcol 1
} else {
set targetcol $param
if {![string is integer -strict $targetcol]} {
puts stderr "renderline HPA (Horizontal Position Absolute) error. Unrecognised parameter '$param'"
}
set targetcol [expr {$param}]
set max [llength $outcols]
if {$overflow_idx == -1} {
incr max
}
if {$targetcol > $max} {
puts stderr "renderline HPA (Horizontal Position Absolute) error. Param '$param' > max: $max"
set targetcol $max
}
}
set idx [expr {($targetcol -1) + $opt_colstart -1}]
set cursor_column $targetcol
}
H - f {
#CSI n;m H - CUP - Cursor Position
@ -3727,7 +3817,7 @@ tcl::namespace::eval overtype {
set cursor_row $target_row
set cursor_column $target_column
set idx [expr {$cursor_column -1}]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
incr idx_over
priv::render_to_unapplied $overlay_grapheme_control_list $gci
set instruction move
@ -3758,7 +3848,7 @@ tcl::namespace::eval overtype {
set cursor_row 1
set cursor_column 1
set idx [expr {$cursor_column -1}]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
incr idx_over
if {[llength $outcols]} {
priv::render_erasechar 0 [llength $outcols]
@ -4000,7 +4090,8 @@ tcl::namespace::eval overtype {
}
}
#append cursor_saved_attributes [join $sgr_stack ""]
append cursor_saved_attributes [punk::ansi::codetype::sgr_merge_list {*}$sgr_stack]
#append cursor_saved_attributes [punk::ansi::codetype::sgr_merge_list {*}$sgr_stack]
append cursor_saved_attributes [punk::ansi::codetype::sgr_merge $sgr_stack]
#as there is apparently only one cursor storage element we don't need to throw back to the calling loop for a save.
@ -4024,7 +4115,7 @@ tcl::namespace::eval overtype {
# set replay_codes_overlay $cursor_saved_attributes ;#empty - or last save if it happend in this input chunk
#} else {
#jj
#set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
#set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay ""
#}
@ -4398,7 +4489,7 @@ tcl::namespace::eval overtype {
#vt102-docs: "Moves cursor up one line in same column. If cursor is at top margin, screen performs a scroll-down"
puts stderr "overtype::renderline ESC M not fully implemented"
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#move up
incr cursor_row -1
if {$cursor_row < 1} {
@ -4743,6 +4834,9 @@ tcl::namespace::eval overtype {
#puts stderr "first_tail_null_posn: $first_tail_null_posn"
#puts stderr "colview: [ansistring VIEW $outcols]"
#NOTE understacks has been updated with data from the overlay - so it should reflect the final state of the stacks for each grapheme in outcols
foreach ch $outcols {
#puts "---- [ansistring VIEW $ch]"
@ -4766,15 +4860,58 @@ tcl::namespace::eval overtype {
if {$i < [llength $understacks]} {
#set cstack [tcl::dict::get $understacks $i]
set cstack [lindex $understacks $i]
if {$cstack ne $prevstack} {
if {[llength $prevstack] && ![llength $cstack]} {
#This reset is important e.g testfile fruit.ans - we get overhang on rhs without it. But why is cstack empty?
append sgrleader \033\[m
#use stack_eq for depth 1 comparison without generating string rep.
if {![stack_eq $cstack $prevstack]} {
#possible SGR attribute change.
if {[llength $prevstack]} {
if {![llength $cstack]} {
#why is cstack empty?
#a) no ansi in underlay and we are at a position 2 after an overlay insertion.
# (position 1 after overlay insertion should already have had a reset inserted)
#b) no ansi in overlay and we are at an overlay insertion point.
#--------------
#review
#todo? consider testing next-char's understack when applying each overlay char in the main loop.
#if empty or has no leading reset - we need to add a leading reset at that point.
#--------------
#--------
#following statement is FALSE - (historical info). Doesn't seem to apply.
#This reset is important e.g testfile fruit.ans - we get overhang on rhs without it.
#append sgrleader \033\[m
#--------
##test
#set view_prev ""
#foreach ps $prevstack {
# append view_prev [ansistring VIEW -lf 1 -vt 1 -nul 1 $ps]
#}
#puts stderr "col $i, ch: $ch - cstack empty vs prevstack $view_prev"
} else {
#without this we get extra redundant codes in some places.
#e.g a continuous string of underlay that originally had \x1b\[31m red text,
#but then when an overlay char is inserted near the start, the following underlay char (insertion index +1) codestack had a reset added.
#All subsequent underlay chars in the same run of plaintext don't have the reset and so appear 'different' but are actually part of the same run.
#check if actually different. ie if current stack actually changes anything from previous stack when merged together.
set prevmerge [punk::ansi::codetype::sgr_merge $prevstack]
set currmerge [punk::ansi::codetype::sgr_merge $cstack]
set together [punk::ansi::codetype::sgr_merge [list $prevmerge $currmerge]]
if {$together ne $prevmerge} {
#stacks are different enough that we need to output something
#if {{[punk::ansi::codetype::has_sgr_leading_reset $currmerge]}} {
#}
append sgrleader $currmerge
}
}
} else {
append sgrleader [punk::ansi::codetype::sgr_merge_list {*}$cstack]
if {[llength $cstack]} {
append sgrleader [punk::ansi::codetype::sgr_merge $cstack]
}
}
set prevstack $cstack
}
set prevstack $cstack
} else {
set prevstack [list]
}
@ -4797,7 +4934,8 @@ tcl::namespace::eval overtype {
#if {[llength $prevstack] && ![llength $cstack]} {
# append sgrleader \033\[m
#}
append sgrleader [punk::ansi::codetype::sgr_merge_list {*}$cstack]
#append sgrleader [punk::ansi::codetype::sgr_merge_list {*}$cstack]
append sgrleader [punk::ansi::codetype::sgr_merge $cstack]
append overflow_right $sgrleader
append overflow_right $ch
} else {
@ -4853,14 +4991,50 @@ tcl::namespace::eval overtype {
set replay_codes ""
if {[llength $understacks] > 0} {
if {$overflow_idx == -1} {
#set tail_idx [tcl::dict::size $understacks]
set tail_idx [llength $understacks]
} else {
set tail_idx [llength $undercols]
}
if {$tail_idx-1 < [llength $understacks]} {
if {$tail_idx == [llength $undercols]} {
#we got to the end of the original underlay
#- so we want the full stack at the end of the original underlay ie including trailing codes which are not associated with any grapheme in the underlay
#but would be in effect for any text after the underlay.
#---------------------
#REVIEW - determine if last col was overwritten by overlay?
#how best to determine if last underlay column was overwritten by overlay?
#we could track in the main loop whether each underlay column was overwritten by overlay
#This seems like the best mechanism, because the overlay ANSI can include movement codes, so the underlay can be overwritten in any order.
#We should consider that just because the last grapheme was overwritten, that doesn't necessarily mean we should disregard the trailing codes
#perhaps trailing underlay codes are never overwritten unless the overlay extends beyond the end of the underlay - in which case we can just check if overlay extends beyond end of underlay to determine whether to include trailing underlay codes in replay or not.
#if overlay extends beyond end of underlay - we use the overlay stack at the end of the underlay as the replay codes, which won't include any trailing underlay codes.
#---------------------
if {[lindex $undermap end] eq ""} {
#there were trailing codes in the underlay with no grapheme - we want to include those in the replay as they would affect any text after the underlay
#we need to backtrack from the end of the underlay to find the last grapheme with codes, and merge those codes with any trailing codes in the underlay with no grapheme
set tailcodes [list] ;#build in reverse order.
foreach {pt code} [lreverse $undermap] {
if {$pt ne ""} {
break
}
lappend tailcodes $code
}
set tailcodes [lreverse $tailcodes]
#set tailcodes [lindex $undermap end-1]
set laststack [lindex $understacks $tail_idx-1]
lappend laststack {*}$tailcodes
set replay_codes [punk::ansi::codetype::sgr_merge $laststack] ;#stack at end of underlay including trailing codes
} else {
#last part of underlay was plain text with no trailing codes - we can just use the stack at the last grapheme of the underlay
set replay_codes [punk::ansi::codetype::sgr_merge [lindex $understacks $tail_idx-1]] ;#stack at end of underlay
}
} elseif {$tail_idx-1 < [llength $understacks]} {
#set replay_codes [join [lindex $understacks $tail_idx-1] ""] ;#tail replay codes
set replay_codes [punk::ansi::codetype::sgr_merge_list {*}[lindex $understacks $tail_idx-1]] ;#tail replay codes
#set replay_codes [punk::ansi::codetype::sgr_merge_list {*}[lindex $understacks $tail_idx-1]] ;#tail replay codes
set replay_codes [punk::ansi::codetype::sgr_merge [lindex $understacks $tail_idx-1]] ;#tail replay codes
}
if {$tail_idx-1 < [llength $understacks_gx]} {
set gx0 [lindex $understacks_gx $tail_idx-1]
@ -4876,10 +5050,33 @@ tcl::namespace::eval overtype {
#pdict $understacks
if {[punk::ansi::ta::detect_sgr $outstring]} {
append outstring [punk::ansi::a] ;#without this - we would get for example, trailing backgrounds after rightmost column
#JULZ
#The caller is responsible for adding a reset at the end of returned lines depending on how they want to use it - so we don't add one here.
#<deprecated>
#append outstring [punk::ansi::a] ;#without this - we would get for example, trailing backgrounds after rightmost column
#</deprecated>
#we only want to append the replay codes if they are different to those already in effect at the end of the rendered line.
if {$overflow_idx == -1} {
set tail_idx [llength $understacks]
} else {
set tail_idx [llength $undercols]
}
set laststack [lindex $understacks $tail_idx-1]
set laststackmerge [punk::ansi::codetype::sgr_merge $laststack]
if {$replay_codes ne $laststackmerge} {
append outstring $replay_codes
}
#review
#close off any open gx?
#probably should - and overflow_right reopen?
#probably not, this is akin to adding a reset to close off open SGR codes, which we specifically don't do.
#caller will need to close off any open gx at the end of the line if they want to, and provide appropriate replay codes for the next line if they want to maintain gx state across lines.
#we just need to make sure we provide all necessary info in the result dictionary.
#todo - tests and examples.
#and overflow_right reopen?
}
if {$opt_returnextra} {
@ -4902,29 +5099,29 @@ tcl::namespace::eval overtype {
set result [tcl::dict::create\
result $outstring\
visualwidth [punk::ansi::printing_length $outstring]\
instruction $instruction\
stringlen [string length $outstring]\
overflow_right_column $overflow_right_column\
overflow_right $overflow_right\
unapplied $unapplied\
unapplied_list $unapplied_list\
unapplied_ansisplit $unapplied_ansisplit\
insert_mode $insert_mode\
autowrap_mode $autowrap_mode\
crm_mode $crm_mode\
reverse_mode $reverse_mode\
insert_lines_above $insert_lines_above\
insert_lines_below $insert_lines_below\
cursor_saved_position $cursor_saved_position\
visualwidth [punk::ansi::printing_length $outstring]\
instruction $instruction\
stringlen [string length $outstring]\
overflow_right_column $overflow_right_column\
overflow_right $overflow_right\
unapplied $unapplied\
unapplied_list $unapplied_list\
unapplied_ansisplit $unapplied_ansisplit\
insert_mode $insert_mode\
autowrap_mode $autowrap_mode\
crm_mode $crm_mode\
reverse_mode $reverse_mode\
insert_lines_above $insert_lines_above\
insert_lines_below $insert_lines_below\
cursor_saved_position $cursor_saved_position\
cursor_saved_attributes $cursor_saved_attributes\
cursor_column $cursor_column\
cursor_row $cursor_row\
expand_right $opt_expand_right\
replay_codes $replay_codes\
replay_codes_underlay $replay_codes_underlay\
replay_codes_overlay $replay_codes_overlay\
pm_list $pm_list\
cursor_column $cursor_column\
cursor_row $cursor_row\
expand_right $opt_expand_right\
replay_codes $replay_codes\
replay_codes_underlay $replay_codes_underlay\
replay_codes_overlay $replay_codes_overlay\
pm_list $pm_list\
]
if {$opt_returnextra == 1} {
#puts stderr "renderline: $result"
@ -5073,6 +5270,11 @@ tcl::namespace::eval overtype::priv {
#caching the answer saves some regex expense - possibly a few uS to lookup vs under 1uS
#todo - test if still worthwhile after a large cache is built up. (limit cache size?)
proc is_sgr {code} {
set code_endswith_m [expr {[tcl::string::index $code end] eq "m"}] ;#skip SGR regexp testing for cases that don't end with m - as they can't be SGR
if {!$code_endswith_m} {
#don't even cache.
return 0
}
variable cache_is_sgr
if {[tcl::dict::exists $cache_is_sgr $code]} {
return [tcl::dict::get $cache_is_sgr $code]
@ -5081,6 +5283,7 @@ tcl::namespace::eval overtype::priv {
tcl::dict::set cache_is_sgr $code $answer
return $answer
}
proc render_to_unapplied {overlay_grapheme_control_list gci} {
upvar idx_over idx_over
@ -5104,7 +5307,8 @@ tcl::namespace::eval overtype::priv {
set unapplied_ansisplit [list ""]
#append unapplied [join [lindex $overstacks $idx_over] ""]
#append unapplied [punk::ansi::codetype::sgr_merge_list {*}[lindex $overstacks $idx_over]]
set sgr_merged [punk::ansi::codetype::sgr_merge_list {*}[lindex $og_stacks $gci]]
#set sgr_merged [punk::ansi::codetype::sgr_merge_list {*}[lindex $og_stacks $gci]]
set sgr_merged [punk::ansi::codetype::sgr_merge [lindex $og_stacks $gci]]
if {$sgr_merged ne ""} {
lappend unapplied_list $sgr_merged
lappend unapplied_ansisplit $sgr_merged ""
@ -5167,7 +5371,8 @@ tcl::namespace::eval overtype::priv {
set unapplied_list [list]
set unapplied_ansisplit [list ""] ;#remove empty entry at end if nothing added
set sgr_merged [punk::ansi::codetype::sgr_merge_list {*}[lindex $og_stacks $gci]]
#set sgr_merged [punk::ansi::codetype::sgr_merge_list {*}[lindex $og_stacks $gci]]
set sgr_merged [punk::ansi::codetype::sgr_merge [lindex $og_stacks $gci]]
if {$sgr_merged ne ""} {
lappend unapplied_list $sgr_merged
lappend unapplied_ansisplit $sgr_merged ""
@ -5217,9 +5422,13 @@ tcl::namespace::eval overtype::priv {
upvar understacks_gx gxstacks
set nxt [llength $o]
if {$i < $nxt} {
set o [lreplace $o $i $i]
set ustacks [lreplace $ustacks $i $i]
set gxstacks [lreplace $gxstacks $i $i]
#set o [lreplace $o $i $i]
ledit o $i $i
#set ustacks [lreplace $ustacks $i $i]
ledit ustacks $i $i
#review - do we need to ensure that stack at new $i has a reset code at the start?
#set gxstacks [lreplace $gxstacks $i $i]
ledit gxstacks $i $i
} elseif {$i == 0 || $i == $nxt} {
#nothing to do
} else {
@ -5329,6 +5538,27 @@ tcl::namespace::eval overtype::priv {
}
if {$i < [llength $ustacks]} {
lset ustacks $i $sgrstack
#check if next ustacks entry has a reset.
#It will need one if it doesn't already have one because our inserted char should not affect the pre-existing ansi state of the underlay.
#we have just replaced an entry into the ustacks at position i but we are still at the same position - so the next entry is still at position i+1
if {[llength $sgrstack] && $i+1 < [llength $ustacks]} {
set next_ustack [lindex $ustacks $i+1]
#could be a reset or just empty - either way we need to add a reset if it's not already there
#(empty if underlay had no ansi)
#temporarily emit something to stderr
if {![llength $next_ustack]} {
#puts -nonewline stderr " next_ustack (empty) at position [expr {$i+1}] after replacing position $i with '$c' and sgrstack '[join $sgrstack ""]'\n"
lset ustacks $i+1 [list "\x1b\[m"]
} else {
#review - next_ustack is a list - has_sgr_leadingreset will not work as expected if called on whole next_ustack as a list.
#As the stack will need merging anyway - we can just prepend a reset without checking.
#REVIEW.
#puts -nonewline stderr "check next_ustack '$next_ustack' for reset at position [expr {$i+1}] after replacing position $i with '$c' and sgrstack '[join $sgrstack ""]'\n"
#set next_ustack [linsert $next_ustack 0 [a+ reset]]
ledit next_ustack -1 -1 "\x1b\[m"
lset ustacks $i+1 $next_ustack
}
}
lset gxstacks $i $gx0stack
} else {
lappend ustacks $sgrstack
@ -5339,7 +5569,8 @@ tcl::namespace::eval overtype::priv {
if {$i < $nxt} {
#set o [linsert $o $i $c]
#JMN insert via ledit
ledit o $i $i-1 $c
#ledit o $i $i-1 $c
ledit o $i -1 $c
} else {
lappend o $c
}
@ -5347,8 +5578,10 @@ tcl::namespace::eval overtype::priv {
#set ustacks [linsert $ustacks $i $sgrstack]
#set gxstacks [linsert $gxstacks $i $gx0stack]
#insert via ledit
ledit ustacks $i $i-1 $sgrstack
ledit gxstacks $i $i-1 $gx0stack
#ledit ustacks $i $i-1 $sgrstack
ledit ustacks $i -1 $sgrstack
#ledit gxstacks $i $i-1 $gx0stack
ledit gxstacks $i -1 $gx0stack
} else {
lappend ustacks $sgrstack
lappend gxstacks $gx0stack

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

@ -1743,7 +1743,7 @@ namespace eval punk {
append script \n {set assigned [ansistring length $leveldata]}
set level_script_complete 1
}
%str {
%str - %string {
set active_key_type "string"
if {$get_not} {
error "!%str - not string-get is not supported"
@ -1752,6 +1752,9 @@ namespace eval punk {
append script \n {# set active_key_type "" index_operation: string-get}
append script \n {set assigned $leveldata}
set level_script_complete 1
#todo - %lpad- %lpadstr- %join- etc as in punk::lib::showdict
#review - merge code shared with showdict for these operations
}
%sp {
#experimental
@ -1822,6 +1825,8 @@ namespace eval punk {
set level_script_complete 1
}
%ansiview {
#review - implemented differently in showdict.
#(showdict uses ansistring VIEW -lf 1 <str>)
set active_key_type "string"
if {$get_not} {
error "!%# not string-ansiview is not supported"
@ -2446,7 +2451,41 @@ namespace eval punk {
set index <idx>
}]
}
%split-* {
#split on one or more chars - review
#set hidekey 1
#lassign [split $key -] _ splitchars
#set thisval [split $dval $splitchars]
set active_key_type "string"
set splitchars [string range $index 7 end]
append script \n [string map [list <splitchars> $splitchars] {
# set active_key_type "string" index_operation: split-string
#e.g supports %split-"\\n"= "l1\n\nl3" -> {l1 "" l3}
set splitchars "<splitchars>"
set assigned [split $leveldata $splitchars]
}]
set level_script_complete 1
#todo %splitat- %splitn- ??
}
%lpad-* {
#moved from punk::lib::showdict patterns.
#set hidekey 1
#lassign [split $key -] _ extra
#set width [expr {[textblock::width $dval] + $extra}]
#set thisval [textblock::pad $dval -which left -width $width]
set active_key_type "string"
set extra [string range $index 6 end]
append script \n [string map [list <extra> $extra] {
# set active_key_type "string" index_operation: lpad-string
set extra "<extra>"
set width [expr {[textblock::width $leveldata] + $extra}]
set assigned [textblock::pad $leveldata -which left -width $width]
}]
set level_script_complete 1
}
%* {
#see above re %lpad- etc and synchronizing with showdict
set active_key_type "string"
set do_bounds_check 0
set index [string range $index 1 end]
@ -2827,11 +2866,21 @@ namespace eval punk {
} else {
if {$is_range} {
lappend INDEX_OPERATIONS list-range
#todo - if we know it's a contiguous range, we could use lrange here instead of lindex
#we would also need to detect if it's a reverse range such as @5..1 and handle that correctly
#- lrange doesn't support reverse ranges, but we could resolve the indexset to a list of indices
#and then use lindex with that list of indices to get the correct result.
#we don't always know at this point if the range is in reverse or not because we don't know the size of the list until
#runtime - so we will handle both cases in the same way for now.
#e.g for index 5..end-6 - this could be forward or reverse depending on the length of the list.
set assign_script {
set assigned [lmap i [punk::lib::indexset_resolve [llength $leveldata] <idx>] {lindex $leveldata $i}]
}
} else {
lappend INDEX_OPERATIONS listindex
}
set assign_script {
set assigned [lmap i [punk::lib::indexset_resolve [llength $leveldata] <idx>] {lindex $leveldata $i}]
set assign_script {
set assigned [lindex $leveldata [punk::lib::indexset_resolve [llength $leveldata] <idx>]]
}
}
}
@ -2881,6 +2930,8 @@ namespace eval punk {
}
set script [string map [list <idx> $index] $script]
} elseif {[string first "end" $index] >=0} {
#review - obsoleted by indexset syntax. prune branch?
puts stderr "index with end detected - review if this branch still reachable - prune? $index"
if {[regexp {^end([-+]{1,2}[0-9]+)$} $index _match endspec]} {
if {$get_not} {
@ -2923,6 +2974,8 @@ namespace eval punk {
}
} elseif {[regexp {^([0-9]+|end|end[-+]{1,2}[0-9]+)-([0-9]+|end|end[-+]{1,2}([0-9]+))$} $index _ start end]} {
#review - obsoleted by indexset syntax. prune branch?
puts stderr "index with range and end detected - review if this branch still reachable - prune? $index"
if {$get_not} {
lappend INDEX_OPERATIONS list-range-not
set assign_script [string map [list <s> $start <e> $end ] {
@ -3012,6 +3065,10 @@ namespace eval punk {
error $listmsg "destructure $selector" [list pipesyntax destructure selector $selector]
}
} elseif {[string first - $index] > 0} {
puts stderr "index with - detected - review if this branch still reachable - prune? $index"
#review - we changed to detect indexset above.
#syntax @m-n should be deprecated in favour of @m..n
#todo - check if this branch still reachable - prune?
#e.g @1-3 gets here
#JMN
if {$get_not} {
@ -3089,19 +3146,61 @@ namespace eval punk {
}
}
} elseif {$active_key_type eq "string"} {
if {[string match *-* $index]} {
lappend INDEX_OPERATIONS string-range
set re_idxdashidx {^([-+]{0,1}\d+|end[-+]{1}\d+|end)-([-+]{0,1}\d+|end[-+]{1}\d+|end)$}
#todo - support more complex indices: 0-end-1 etc
#changed to indexset notation m..n allowing eg 2..end-1 etc.
#if {[string match *-* $index]} {}
if {[punk::lib::is_indexset $index]} {
#review - we are assuming a single element indexset here - ie no comma separated sets.
#todo - support $get_not
#todo - consider bounds_check for string indices.
# - Tcl doesn't do bounds checking for string index, but we need to consider in the context of pattern-matching
# whether we want to support syntaxes for with and without bounds checking on string indices.
set is_range [expr {[string first ".." $index] >= 0}]
if {$is_range} {
lappend INDEX_OPERATIONS string-range
#review - not efficient for contiguous monotonically increasing ranges
#because we are retrievinng each character individually and concatenating
#- but it is more flexible because it also supports reverse ranges and could support non-contiguous ranges such as @0,2,4..6
set assign_script {
set assigned [join [lmap i [punk::lib::indexset_resolve [string length $leveldata] <idx>] {string index $leveldata $i}] ""]
}
} else {
lappend INDEX_OPERATIONS string-index
set assign_script {
set assigned [string index $leveldata [punk::lib::indexset_resolve [string length $leveldata] <idx>]]
}
}
#set assign_script {
# set assigned [lmap i [punk::lib::indexset_resolve [llength $leveldata] <idx>] {lindex $leveldata $i}]
#}
lassign [split $index -] a b
#todo - consider where/if we can support 'ansistring INDEX' for ANSI strings.
#if so - it shouldn't overload the % operator we currently use for string access.
append script \n [tstr -return string -allowcommands {
# set active_key_type "string"
set assigned [string range $leveldata ${$a} ${$b}]
if {$leveldata eq ""} {
set assigned ""
} else {
${$assign_script}
}
}]
set script [string map [list <idx> $index] $script]
#set re_idxdashidx {^([-+]{0,1}\d+|end[-+]{1}\d+|end)-([-+]{0,1}\d+|end[-+]{1}\d+|end)$}
##todo - support more complex indices: 0-end-1 etc
#lassign [split $index -] a b
#append script \n [tstr -return string -allowcommands {
# # set active_key_type "string"
# set assigned [string range $leveldata ${$a} ${$b}]
#}]
} else {
if {$index eq "*"} {
#equivalent to indexset ".."
lappend INDEX_OPERATIONS string-all
append script \n [tstr -return string -allowcommands {
# set active_key_type "string"
@ -4294,6 +4393,7 @@ namespace eval punk {
}
#todo check end-x bounds?
}
#todo - change to ledit
if {$isint} {
append script [string map [list <listvar> $listvar <idx> $index <exp> $exp <val> $data] {
set <listvar> [linsert [lindex [list $<listvar> [unset <listvar>]] 0] <idx> <exp><val>]
@ -4350,7 +4450,8 @@ namespace eval punk {
#last element has no -, so we are inserting at the final position - not replacing
append script [string map [list <listvar> $listvar <containerkeys> [lrange $parts 0 end-1] <lastkey> $last <exp> $exp <val> $data] {
set target [lindex $<listvar> <containerkeys>]
set target [linsert $target <lastkey> <exp><val>]
#set target [linsert $target <lastkey> <exp><val>]
ledit target <lastkey> -1 <exp><val>
lset <listvar> <containerkeys> $target
}]
}
@ -8564,7 +8665,7 @@ namespace eval punk {
lappend chunks [list stdout $text]
}
console - term - terminal {
set term_env_vars {TERM TERM_PROGRAM TERM_PROGRAM_VERSION}
set term_env_vars {TERM TERM_PROGRAM TERM_PROGRAM_VERSION COLORTERM}
set term_dict [dict create]
foreach e $term_env_vars {
if {[info exists ::env($e)]} {
@ -8577,6 +8678,7 @@ namespace eval punk {
append text [punk::lib::showdict $term_dict] \n
lappend chunks [list stdout $text]
set text ""
set indent [string repeat " " [string length "WARNING: "]]
if {[catch {package require punk::console} result]} {
set text "Unable to load punk::console package - cannot test\n$result"
@ -8591,7 +8693,6 @@ namespace eval punk {
}
lappend chunks [list stdout $text]
set indent [string repeat " " [string length "WARNING: "]]
lappend cstring_tests [dict create\
type "PM "\
msg "UN"\
@ -8686,10 +8787,45 @@ namespace eval punk {
}
}
}
set posn [punk::console::get_cursor_pos] ;#warmup call - and test if works
if {$posn eq ""} {
append warningblock \n "WARNING: terminal doesn't respond to cursor position query - may cause display bugs in some cases."
} else {
set timeresult [timerate {set cpos [punk::console::get_cursor_pos]}]
lassign [split $cpos {;}] row col
if {![string is integer -strict $row] || ![string is integer -strict $col]} {
append warningblock \n "WARNING: terminal returns non-integer values for cursor position query - may cause display bugs in some cases. got row:'$row' col:'$col'"
} else {
set micros [lindex $timeresult 0]
if {$micros > 2000} {
append warningblock \n "WARNING: terminal cursor position query is very slow ($micros microseconds - expect < 2000us )"
append warningblock \n $indent "- may cause display lag/bugs in some cases."
} else {
if {$micros > 1000} {
set text "\n[a+ yellow]Terminal cursor position query test passed."
append text \n $indent "Response time: ${micros} microseconds (OK, good would be <= 1000us).[a]"
} else {
set text "[a+ green]Terminal cursor position query test passed."
append text \n $indent "Response time: ${micros} microseconds (GOOD).[a]"
}
lappend chunks [list stdout $text]
}
}
}
if {![string length $warningblock]} {
set text "[a+ green]No terminal warnings[a]\n"
lappend chunks [list stdout $text]
} else {
set mode [punk::console::mode]
if {$mode eq "line"} {
append warningblock \n "Terminal appears to be in line mode. Consider switching to raw mode and re-testing (command: punk::console::mode raw)."
}
}
puts stdout [punk::ansi::move_back 200] ;#hack for some horizontal position bugs where the above tests can leave the cursor in the wrong place for the next output.
#200 is arbitrary large number to move back enough to get to start of line.
}
}
topics - help {
@ -8815,10 +8951,11 @@ namespace eval punk {
#interp alias {} c {} clear ;#external executable 'clear' may not always be available
#todo - review
interp alias {} clear {} ::punk::reset
interp alias {} c {} ::punk::reset
#interp alias {} clear {} ::punk::reset
#interp alias {} c {} ::punk::reset
interp alias {} reset {} ::punk::reset
proc reset {} {
if {[llength [info commands ::punk::repl::reset_terminal]]} {
#punk::repl::reset_terminal notifies prompt system of reset
@ -8828,6 +8965,91 @@ namespace eval punk {
}
}
namespace eval argdoc {
punk::args::define {
@id -id ::punk::ansi8
@cmd -name punk::ansi8\
-summary\
"Tell terminal to enable 8-bit ANSI codes."\
-help\
"Enable 8-bit ANSI codes in the terminal.
May not be supported by all terminals.
Some terminals may already have 8-bit ANSI enabled, but some may require an explicit command to enable it.
7-bit ANSI codes are generally preferred - and will still work on terminals with 8-bit ANSI support.
(This is nothing to do with 8-bit colors - it is about the underlying bytes used for ANSI control sequences).
The ANSI sequence sent to the terminal to enable 8-bit codes is: ESC <sp> 7
To disable 8-bit ANSI support - a reset of the terminal may be required.
"
@opts
@values -min 0 -max 0
}
}
proc ansi8 {} {
punk::console::S8C1R
}
namespace eval argdoc {
punk::args::define {
@id -id ::punk::clear
@cmd -name punk::clear\
-summary\
"Clear the terminal screen (and scrollback buffer by default)."\
-help\
"Clear the terminal screen.
By default this will also clear scrollback if supported by the terminal.
With -x option it will preserve scrollback but clear the screen.
"
@opts
-x -optional 1 -type none -mash 1 -help\
"Preserve scrollback (if supported by terminal) but clear screen."
-s -optional 1 -type none -mash 1 -help\
"Stay at the current cursor position instead of moving to top-left after clearing."
@values -min 0 -max 0
}
}
proc clear {args} {
set argd [punk::args::parse $args withid ::punk::clear]
lassign [dict values $argd] leaders opts values received
set opt_x [dict exists $received -x]
set opt_s [dict exists $received -s]
# -x preserves scrollback but clears screen
if {$opt_s} {
#set pre_move_cmd [punk::ansi::move_up 1]
#review - terminal support for save/restore.
#we can just move up one line before clearing to preserve the line we're on,
#but this won't work if we're already at the last line.
#save/restore would be better if widely supported.
#review - get_size already calls get_cursor pos - maybe we can optimize by not calling get_cursor_pos separately?
#review - consider turning off cursor updating while doing this to avoid flicker?
set cpos [punk::console::get_cursor_pos]
set row [lindex $cpos 0]
set size [punk::console::get_size]
set lastrow [dict get $size rows]
if {$row >= $lastrow} {
set pre_move_cmd [punk::ansi::cursor_save_dec]
} else {
set pre_move_cmd [punk::ansi::move_up 1][punk::ansi::cursor_save_dec]
}
set move_cmd [punk::ansi::cursor_restore_dec]
#set pre_move_cmd [punk::ansi::move_up 1]
#set move_cmd ""
} else {
set pre_move_cmd ""
set move_cmd [punk::ansi::move 1 1]
}
if {$opt_x} {
puts -nonewline stdout $pre_move_cmd[punk::ansi::clear]$move_cmd
} else {
puts -nonewline stdout $pre_move_cmd[punk::ansi::clear_all]$move_cmd
}
}
#c aliased to clear -xs
#cc aliases to clear -x
#fileutil::cat except with checking for windows illegal path names (when on windows platform)

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

@ -125,6 +125,10 @@ tcl::namespace::eval punk::aliascore {
grepstr ::punk::ansi::grepstr\
colour ::punk::console::colour\
color ::punk::console::colour\
ansi8 ::punk::ansi8\
clear ::punk::clear\
c {::punk::clear -xs}\
cc {::punk::clear -x}\
ansi ::punk::console::ansi\
a? ::punk::console::code_a?\
A? {::punk::console::code_a? forcecolor}\

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

File diff suppressed because it is too large Load Diff

2
src/bootsupport/modules/punk/ansi/colourmap-0.1.0.tm

@ -103,7 +103,7 @@ tcl::namespace::eval ::punk::ansi::colourmap {
name -type string|stringstartswith(#)
}]
proc get_rgb_using_tk {name} {
package require tk
package require Tk ;#package require tk (lowercase) doesn't always work
#assuming 'winfo depth .' is always 32 ?
set RGB [winfo rgb . $name]
set rgb [lmap n $RGB {expr {$n / 256}}]

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

@ -1250,6 +1250,11 @@ tcl::namespace::eval punk::args {
}
set optionspecs [list]
#REVIEW - whilst this is only done once for each command definition, the -help section processing is sometimes expensive,
#and isn't required for parsing of arguments, so it unnecessarily slows first use of a command that uses punk::args and is heavily documented,
#especially if it has tcl syntax highlighted examples.
#- ideally we would delay expansion of -help sections until needed for display,
#and use a different cache key for the parsing vs display versions of the resolved definition.
foreach block $normargs {
if {[string first \$\{ $block] >= 0} {
if {$defspace ne ""} {
@ -2550,7 +2555,7 @@ tcl::namespace::eval punk::args {
tcl::dict::set spec_merged -typesynopsis $specval
}
-parsekey - -group {
tcl::dict::set spec_merged -typesynopsis $specval
tcl::dict::set spec_merged $spec $specval
}
-mash {
#allow when any alt in argname is a single letter flag such s -a or -Z
@ -8535,7 +8540,7 @@ tcl::namespace::eval punk::args {
}
#todo - move block below up here.
if {!$all_mashable} {
puts stderr "Debug: flagsupplied '$flagsupplied' not a valid flagname and not a valid mash of flags - treating as value"
#puts stderr "Debug: flagsupplied '$flagsupplied' not a valid flagname and not a valid mash of flags - treating as value"
#- probably isn't a flag at all - could be a value
#treat as value
set optionset ""
@ -8668,9 +8673,8 @@ tcl::namespace::eval punk::args {
#tcl::dict::set opts $flag_ident $tdflt
if {$flag_ident_is_parsekey} {
#(shimmer - but required for ordering correctness during override)
puts stderr "Debug: flag '$mashflagname' in mash '$flagsupplied' flag_ident '$flag_ident' is the same as parsekey '$api_opt' tdflt: $tdflt - using lappend to ensure it ends up after any previous flag in the mash that had the same parsekey"
#puts stderr "Debug: flag '$mashflagname' in mash '$flagsupplied' flag_ident '$flag_ident' is the same as parsekey '$api_opt' tdflt: $tdflt - using lappend to ensure it ends up after any previous flag in the mash that had the same parsekey"
lappend opts $flag_ident $tdflt
puts stderr "opts after lappend: $opts"
} else {
tcl::dict::set opts $flag_ident $tdflt
}
@ -10241,6 +10245,128 @@ tcl::namespace::eval punk::args {
}
}
proc _synopsis_form_arg_display {formdict argname} {
#non-colour SGR such as bold/italic/strike - so we don't need to worry about NOCOLOR settings
set I "\x1b\[3m" ;#[punk::ansi::a+ italic]
set NI "\x1b\[23m" ;# [punk::ansi::a+ noitalic]
#for inner question marks marking optional type
set IS "\x1b\[3\;9m" ;#[punk::ansi::a+ italic strike]
set NIS "\x1b\[23\;29m" ;#[punk::ansi::a+ noitalic nostrike]
set RST "\x1b\[m" ;#[punk::ansi::a]
set arginfo [dict get $formdict ARG_INFO $argname]
set typelist [dict get $arginfo -type]
set ts [Dict_getdef $arginfo -typesynopsis ""]
set n [expr {[llength $typelist]-1}]
set name_tail [lrange $argname end-$n end];#if there are enough tail words in the argname to match -types
set clause ""
if {$ts ne ""} {
set tp_displaylist $ts
} else {
set tp_displaylist [lrepeat [llength $typelist] ""]
}
foreach typespec $typelist td $tp_displaylist elementname $name_tail {
#elementname will commonly be empty
if {[string match {\?*\?} $typespec]} {
set tp [string range $typespec 1 end-1]
set member_optional 1
} else {
set tp $typespec
set member_optional 0
}
if {$td ne ""} {
set c $td
} else {
#handle alternate-types e.g literal(text)|literal(binary)
set alternates [list]
set type_alternatives [_split_type_expression $tp]
foreach tp_alternative $type_alternatives {
set tp_alternative_word1 [lindex $tp_alternative 0]
set match [lindex $tp_alternative 1]
switch -exact -- $tp_alternative_word1 {
literal {
lappend alternates [list $match]
}
literalprefix {
#todo - trie styling on prefix calc
lappend alternates [list $match]
}
stringstartswith {
lappend alternates [list $match*]
}
stringendswith {
lappend alternates [list *$match]
}
default {
#we'll only take display hints from the name itself if there was no defined typesynopsis element for this position in the type,
#and if the type-alternatives don't specify a literal or string match that we can use for display
#and if there are enough tail words in the argname to match the position in the type list
#empty strings can be put in -typesynopsis positions to only override the type information for certain elements of the clause
#- e.g for a type list of {string int} we could specify a typesynopsis of {"" "count"} to get display of "FILENAME count" for an argname of "file FILENAME FILECOUNT"
if {[llength $name_tail] >= [llength $typelist]} {
#important to list protect $elementname e.g look at ::apply
#The name may contain spaces e.g "{args body ?namespace?}"
#This must not be split into multiple words - it is a single element name that happens to contain spaces.
lappend alternates $I[list $elementname]$NI
} else {
lappend alternates $I<$tp_alternative>$NI
}
}
}
}
set alternates [punk::args::lib::lunique $alternates]
set c [join $alternates |]
}
if {$member_optional} {
#append clause " " "(?$c?)"
append clause " " "\[$c\]"
} else {
append clause " " $c
}
}
set clause [string trimleft $clause]
#set ARGD [dict create argname $argname class leader]
if {[dict get $arginfo -optional] || [dict exists $arginfo -default]} {
if {[dict get $arginfo -multiple]} {
#set display "?$I$argname$NI?..."
set display "\[$clause\]..."
} else {
set display "\[$clause\]"
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "?[lindex [dict get $arginfo -choices] 0]?"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display "?$argname?"
#} else {
# set display "?$I$argname$NI?"
#}
}
} else {
if {[dict get $arginfo -multiple]} {
#set display "$I$argname$NI ?$I$argname$NI?..."
set display "$clause \[$clause\]..."
} else {
set display $clause
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "[lindex [dict get $arginfo -choices] 0]"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display $argname
#} else {
# set display "$I$argname$NI"
#}
}
}
return $display
}
lappend PUNKARGS [list {
@id -id ::punk::args::synopsis
@cmd -name punk::args::synopsis\
@ -10295,7 +10421,19 @@ tcl::namespace::eval punk::args {
if {$spec eq ""} {
return
}
set form_names [dict get $spec form_names]
set dict_idx_to_name [dict create]
set dict_name_to_idx [dict create]
set all_form_names [dict get $spec form_names]
set idx 0
#assert: form_names is ordered as defined in the command definition - so idx into it is stable.
foreach fn $all_form_names {
dict set dict_idx_to_name $idx $fn
dict set dict_name_to_idx $fn $idx
incr idx
}
set form_names $all_form_names
if {$form ne "*"} {
if {[string is integer -strict $form]} {
set f [lindex $form_names $form]
@ -10314,171 +10452,51 @@ tcl::namespace::eval punk::args {
}
set SYND [dict create]
dict set SYND cmd_info [dict get $spec cmd_info]
set c_info [dict get $spec cmd_info]
set cmd_info [dict create]
dict for {k v} $c_info {
if {[string match -* $k]} {
dict set cmd_info [string range $k 1 end] $v
}
}
dict set SYND COMMAND $cmd_info
#leading "# " required (punk::ns::synopsis will pass through)
if {![dict exists $received -noheader]} {
set syn "# [Dict_getdef $spec cmd_info -summary ""]\n"
set GRY "\x1b\[38\;5\;8m"
set RST "\x1b\[m"
}
#todo - -multiple etc
foreach f $form_names {
set SYNLIST [list]
dict set SYND FORMS $f [list]
append syn "$id"
set forminfo [dict get $spec FORMS $f]
#foreach argname [dict get $forminfo LEADER_NAMES] {
# set arginfo [dict get $forminfo ARG_INFO $argname]
# set ARGD [dict create argname $argname class leader]
# if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display [lindex [dict get $arginfo -choices] 0]
# } elseif {[dict get $arginfo -type] eq "literal"} {
# set display $argname
# } else {
# set display $I$argname$RST
# }
# if {[dict get $arginfo -optional]} {
# append syn " ?$display?"
# } else {
# append syn " $display"
# }
# dict set ARGD type [dict get $arginfo -type]
# dict set ARGD optional [dict get $arginfo -optional]
# dict set ARGD display $display
# dict lappend SYND $f $ARGD
#}
set idx [dict get $dict_name_to_idx $f]
dict set SYND FORMS $f [dict create]
if {![dict exists $received -noheader]} {
set formsummary "FORM $idx $f"
if {[dict exists $forminfo -summary]} {
append formsummary " - [dict get $forminfo -summary]"
}
append syn "## $GRY$formsummary$RST\n"
}
append syn "$id"
set FORMARGS [list]
foreach argname [dict get $forminfo LEADER_NAMES] {
set arginfo [dict get $forminfo ARG_INFO $argname]
set typelist [dict get $arginfo -type]
if {[llength $typelist] == 1} {
set tp [lindex $typelist 0]
set ts [Dict_getdef $arginfo -typesynopsis ""]
if {$ts ne ""} {
#set arg_display [dict get $arginfo -typesynopsis]
set clause $ts
} else {
#set arg_display $argname
set alternates [list];#alternate acceptable types e.g literal(yes)|literal(ok) or indexpression|literal(first)
set type_alternatives [_split_type_expression $tp]
foreach tp_alternative $type_alternatives {
set tp_alternative_word1 [lindex $tp_alternative 0]
switch -exact -- $tp_alternative_word1 {
literal {
set match [lindex $tp_alternative 1]
lappend alternates $match
}
literalprefix {
#todo - trie styling on prefix calc
set match [lindex $tp_alternative 1]
lappend alternates $match
}
stringstartswith {
set match [lindex $tp_alternative 1]
lappend alternates $match*
}
stringendswith {
set match [lindex $tp_alternative 1]
lappend alternates *$match
}
default {
lappend alternates $I$argname$NI
}
}
#if {$tp_alternative eq "literal"} {
# lappend alternates [lindex $argname end]
#} elseif {[string match literal(*) $tp_alternative]} {
# set match [string range $tp_alternative 8 end-1]
# lappend alternates $match
#} elseif {[string match literalprefix(*) $tp_alternative]} {
# set match [string range $tp_alternative 14 end-1]
# lappend alternates $match
#} else {
# lappend alternates $I$argname$NI
#}
}
#remove dupes - but keep order (e.g of dupes -type string|int when no -typesynopsis was specified)
#todo - trie prefixes display
set alternates [punk::args::lib::lunique $alternates]
set clause [join $alternates |]
}
} else {
set n [expr {[llength $typelist]-1}]
set name_tail [lrange $argname end-$n end];#if there are enough tail words in the argname to match -types
set clause ""
set ts [Dict_getdef $arginfo -typesynopsis ""]
if {$ts ne ""} {
set tp_displaylist $ts
} else {
set tp_displaylist [lrepeat [llength $typelist] ""]
}
foreach typespec $typelist td $tp_displaylist elementname $name_tail {
#elementname will commonly be empty
if {[string match {\?*\?} $typespec]} {
set tp [string range $typespec 1 end-1]
set member_optional 1
} else {
set tp $typespec
set member_optional 0
}
if {$tp eq "literal"} {
set c $elementname
} elseif {[string match literal(*) $tp]} {
set match [string range $tp 8 end-1]
set c $match
} else {
if {$td eq ""} {
set c $I$tp$NI
} else {
set c $td
}
}
if {$member_optional} {
append clause " " "(?$c?)"
} else {
append clause " " $c
}
}
set clause [string trimleft $clause]
}
foreach argname [dict get $forminfo LEADER_NAMES] {
set display [_synopsis_form_arg_display $forminfo $argname]
append syn " $display"
set arginfo [dict get $forminfo ARG_INFO $argname]
set ARGD [dict create argname $argname class leader]
if {[dict get $arginfo -optional] || [dict exists $arginfo -default]} {
if {[dict get $arginfo -multiple]} {
#set display "?$I$argname$NI?..."
set display "?$clause?..."
} else {
set display "?$clause?"
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "?[lindex [dict get $arginfo -choices] 0]?"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display "?$argname?"
#} else {
# set display "?$I$argname$NI?"
#}
}
} else {
if {[dict get $arginfo -multiple]} {
#set display "$I$argname$NI ?$I$argname$NI?..."
set display "$clause ?$clause?..."
} else {
set display $clause
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "[lindex [dict get $arginfo -choices] 0]"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display $argname
#} else {
# set display "$I$argname$NI"
#}
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
foreach k {choices choiceprefix choicerestricted choicemultiple} {
if {[dict exists $arginfo -$k]} {
dict set ARGD $k [dict get $arginfo -$k]
}
}
append syn " $display"
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
dict set ARGD display $display
#dict lappend SYND $f $ARGD
lappend FORMARGS $ARGD
}
foreach argname [dict get $forminfo OPT_NAMES] {
@ -10490,7 +10508,7 @@ tcl::namespace::eval punk::args {
#(disallowed in punk::args::define)
set argdisplay $argname
} else {
#assert [llength $tp] == 1 (multiple values for flag unspported in punk::args::define)
#assert [llength $tp] == 1 (multiple values for flag unsupported in punk::args::define)
if {[string match {\?*\?} $tp]} {
set tp [string range $tp 1 end-1]
set value_is_optional true
@ -10509,19 +10527,30 @@ tcl::namespace::eval punk::args {
} else {
set alternates [list];#alternate acceptable types e.g literal(yes)|literal(ok) or indexpression|literal(first)
foreach tp_alternative [split $tp |] {
#-type literal not valid for opt - review
if {[string match literal(*) $tp_alternative]} {
set match [string range $tp_alternative 8 end-1]
lappend alternates $match
} elseif {[string match literalprefix(*) $tp_alternative]} {
set match [string range $tp_alternative 14 end-1]
lappend alternates $match
} else {
lappend alternates <$I$tp_alternative$NI>
set type_alternatives [_split_type_expression $tp]
foreach tp_alternative $type_alternatives {
set match [lindex $tp_alternative 1]
switch -- [lindex $tp_alternative 0] {
literal {
lappend alternates [list $match]
}
literalprefix {
lappend alternates [list $match]
}
stringstartswith {
lappend alternates [list $match*]
}
stringendswith {
lappend alternates [list *$match]
}
default {
lappend alternates $I<$tp_alternative>$NI
}
}
}
#todo - trie prefixes display?
#trie prefixes display?
#we probably don't want to show prefixes in synopsis.
#AI agents should be encouraged to use full values for clarity, and human users can refer to help for the prefix info if they care.
set alternates [punk::args::lib::lunique $alternates]
set tp_display [join $alternates |]
}
@ -10529,44 +10558,102 @@ tcl::namespace::eval punk::args {
#need to bracket alternate-types to distinguish pipes delimiting flag aliases
set tp_display "($tp_display)"
}
#consider optional: -f|--file|--file= -type string|num
#we can't show this as [-f|--file|--file= string|num]
#because the pipes make visually parsing it ambiguous.
#we *could* show this as [-f|--file|--file= (string|num)]
# but it lacks clarity in descripting we can supply --file string or --file=string
#showing it as [-f (string|num)|--file (string|num)|--file=(string|num)] is not as compact as it could be, but is reasonably precise.
#we could merge the first two to avoid repeating the type info - but then we would also need brackets to clarify the pipe applicability:
#e.g
# [(-f|--file (string|num))|--file=(string|num)]
#
#we choose to only merge in the case where there are no trailing= aliases or they are all trailing= aliases.
set aliasflags [split $argname |]
#set has_longopt_inlinevalue_alias [expr {[lsearch -glob $aliasflags *=] >= 0}]
set num_longopt_inlinevalue_aliases [llength [lsearch -all -glob $aliasflags *=]] ;#count list of indices of aliasflags that end with =
set homogenous_aliases [expr {$num_longopt_inlinevalue_aliases == 0 || $num_longopt_inlinevalue_aliases == [llength $aliasflags]}]
set argdisplay ""
foreach aliasflag [split $argname |] {
if {[string match --* $aliasflag]} {
if {[string index $aliasflag end] eq "="} {
set alias [string range $aliasflag 0 end-1]
if {$value_is_optional} {
append argdisplay "$alias$IS?$NIS=$tp_display$IS?$NIS|"
if {!$homogenous_aliases} {
foreach aliasflag $aliasflags {
if {[string match --* $aliasflag]} {
if {[string index $aliasflag end] eq "="} {
set alias [string range $aliasflag 0 end-1]
if {$value_is_optional} {
#append argdisplay "$alias$IS\[$NIS=$tp_display$IS\]$NIS|"
append argdisplay "$alias$I\[$NI=$tp_display$I\]$NI|"
} else {
append argdisplay "$alias=$tp_display|"
}
} else {
append argdisplay "$alias=$tp_display|"
if {$value_is_optional} {
#double-dashed flag without trailing = can't accept optional value
#append argdisplay "$aliasflag $IS\[$NIS$tp_display$IS\]$NIS|"
append argdisplay "$aliasflag|"
} else {
append argdisplay "$aliasflag $tp_display|"
}
}
} else {
if {$value_is_optional} {
append argdisplay "$aliasflag $IS?$NIS$tp_display$IS?$NIS|"
#flag can't accept optional value
append argdisplay "$aliasflag|"
} else {
append argdisplay "$aliasflag $tp_display|"
}
}
}
set argdisplay [string trimright $argdisplay |]
} else {
if {$num_longopt_inlinevalue_aliases > 0} {
#all aliases are longopt inlinevalue aliases
#review
# --file=|--fname= -type string
# -> (--file|--fname)=type
# or
# -> (--file|--fname)[=type]
#first transform the argname to remove the trailing = and bracket the aliases if there are multiple
#review - we don't expect any arguments to be defined with inner = in the name.
#todo - enforce no inner = in argname in punk::args::define for options?
#
set argname "[string map {= ""} $argname]"
if {$num_longopt_inlinevalue_aliases > 1} {
set argname "($argname)"
}
if {$value_is_optional} {
set argdisplay "$argname$I\[$NI=$tp_display$I\]$NI"
} else {
set argdisplay "$argname=$tp_display"
}
} else {
#no longopts with trailing = aliases, so we can show the type info without ambiguity as applying to all aliases
if {$value_is_optional} {
#single dash flag can't accept optional value
append argdisplay "$aliasflag|"
set argdisplay "$argname $I\[$NI$tp_display$I\]$NI"
} else {
append argdisplay "$aliasflag $tp_display|"
set argdisplay "$argname $tp_display"
}
}
}
set argdisplay [string trimright $argdisplay |]
}
if {[dict get $arginfo -optional]} {
if {[dict get $arginfo -multiple]} {
set display "?$argdisplay?..."
#set display "?$argdisplay?..."
set display "\[$argdisplay\]..."
} else {
set display "?$argdisplay?"
#set display "?$argdisplay?"
set display "\[$argdisplay\]"
}
} else {
if {[dict get $arginfo -multiple]} {
set display "$argdisplay ?$argdisplay?..."
#set display "$argdisplay ?$argdisplay?..."
set display "$argdisplay \[$argdisplay\]..."
} else {
set display $argdisplay
}
@ -10606,136 +10693,43 @@ tcl::namespace::eval punk::args {
# }
# }
#}
#todo -mash
append syn " $display"
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
foreach k {choices choiceprefix choicerestricted choicemultiple} {
if {[dict exists $arginfo -$k]} {
dict set ARGD $k [dict get $arginfo -$k]
}
}
dict set ARGD display $display
#dict lappend SYND $f $ARGD
lappend FORMARGS $ARGD
}
foreach argname [dict get $forminfo VAL_NAMES] {
set arginfo [dict get $forminfo ARG_INFO $argname]
set typelist [dict get $arginfo -type]
if {[llength $typelist] == 1} {
set tp [lindex $typelist 0]
set ts [Dict_getdef $arginfo -typesynopsis ""]
if {$ts ne ""} {
#set arg_display [dict get $arginfo -typesynopsis]
set clause $ts
} else {
#set arg_display $argname
set alternates [list];#alternate acceptable types e.g literal(yes)|literal(ok) or indexpression|literal(first)
foreach tp_alternative [split $tp |] {
if {$tp_alternative eq "literal"} {
lappend alternates [lindex $argname end]
} elseif {[string match literal(*) $tp_alternative]} {
set match [string range $tp_alternative 8 end-1]
lappend alternates $match
} elseif {[string match literalprefix(*) $tp_alternative]} {
set match [string range $tp_alternative 14 end-1]
lappend alternates $match
} else {
lappend alternates $I$argname$NI
}
}
#remove dupes - but keep order (e.g of dupes -type string|int when no -typesynopsis was specified)
#todo - trie prefixes display
set alternates [punk::args::lib::lunique $alternates]
set clause [join $alternates |]
}
} else {
set n [expr {[llength $typelist]-1}]
set name_tail [lrange $argname end-$n end];#if there are enough tail words in the argname to match -types
set clause ""
set ts [Dict_getdef $arginfo -typesynopsis ""]
if {$ts ne ""} {
set tp_displaylist $ts
} else {
set tp_displaylist [lrepeat [llength $typelist] ""]
}
foreach typespec $typelist td $tp_displaylist elementname $name_tail {
#elementname will commonly be empty
if {[string match {\?*\?} $typespec]} {
set tp [string range $typespec 1 end-1]
set member_optional 1
} else {
set tp $typespec
set member_optional 0
}
#handle alternate-types e.g literal(text)|literal(binary)
set alternates [list]
foreach tp_alternative [split $tp |] {
if {$tp_alternative eq "literal"} {
lappend alternates $elementname
} elseif {[string match literal(*) $tp_alternative]} {
set match [string range $tp_alternative 8 end-1]
lappend alternates $match
} elseif {[string match literalprefix(*) $tp_alternative]} {
set match [string range $tp_alternative 14 end-1]
lappend alternates $match
} else {
if {$td eq ""} {
lappend alternates $I$tp$NI
} else {
lappend alternates $td
}
}
}
set alternates [punk::args::lib::lunique $alternates]
set c [join $alternates |]
if {$member_optional} {
append clause " " "(?$c?)"
} else {
append clause " " $c
}
}
set clause [string trimleft $clause]
}
set display [_synopsis_form_arg_display $forminfo $argname]
append syn " $display"
set ARGD [dict create argname $argname class value]
if {[dict get $arginfo -optional] || [dict exists $arginfo -default]} {
if {[dict get $arginfo -multiple]} {
#set display "?$I$argname$NI?..."
set display "?$clause?..."
} else {
set display "?$clause?"
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "?[lindex [dict get $arginfo -choices] 0]?"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display "?$argname?"
#} else {
# set display "?$I$argname$NI?"
#}
}
} else {
if {[dict get $arginfo -multiple]} {
#set display "$I$argname$NI ?$I$argname$NI?..."
set display "$clause ?$clause?..."
} else {
set display $clause
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "[lindex [dict get $arginfo -choices] 0]"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display $argname
#} else {
# set display "$I$argname$NI"
#}
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
foreach k {choices choiceprefix choicerestricted choicemultiple} {
if {[dict exists $arginfo -$k]} {
dict set ARGD $k [dict get $arginfo -$k]
}
}
append syn " $display"
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
dict set ARGD display $display
#dict lappend SYND $f $ARGD
lappend FORMARGS $ARGD
}
#accepts unnamed extra arguments e.g toplevel docid for ensembles and ensemble-like commands
if {[dict get $forminfo VAL_UNNAMED]} {
set display "?<unnamed>...?"
set display {[<unnamed>...]}
append syn " $display"
set ARGD [dict create argname "" class value]
dict set ARGD type any
@ -10745,7 +10739,7 @@ tcl::namespace::eval punk::args {
lappend FORMARGS $ARGD
}
append syn \n
dict set SYND FORMS $f $FORMARGS
dict set SYND FORMS $f args $FORMARGS
}
switch -- $opt_return {
full {
@ -10757,7 +10751,8 @@ tcl::namespace::eval punk::args {
set summary "# [Dict_getdef $spec cmd_info -summary ""]\n"
}
set FORMS [dict get $SYND FORMS]
dict for {form arglist} $FORMS {
dict for {form arginfo} $FORMS {
set arglist [dict get $arginfo args]
append summary $id
set class_state leader
set option_count 0
@ -10774,7 +10769,7 @@ tcl::namespace::eval punk::args {
incr value_count
if {$class_state ne "value"} {
if {$option_count > 0} {
append summary " ?options ($option_count defined)?"
append summary " \[OPTIONS ($option_count defined)\]"
}
set class_state value
}
@ -10783,7 +10778,7 @@ tcl::namespace::eval punk::args {
}
}
if {$value_count == 0 && $option_count > 0} {
append summary " ?options ($option_count defined)?"
append summary " \[OPTIONS ($option_count defined)\]"
}
append summary \n
}
@ -10803,6 +10798,7 @@ tcl::namespace::eval punk::args {
}
#REVIEW
lappend PUNKARGS [list {
@id -id ::punk::args::synopsis_summary
@cmd -name punk::args::synopsis_summary -help\
@ -10852,9 +10848,10 @@ tcl::namespace::eval punk::args {
}
}
}
if {$code ne ""} {
if {$code ne "" && [tcl::string::index $code end] eq "m"} {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set codestack [list "\x1b\[m"]
#set codestack [list "\x1b\[m"]
set codestack [list $code]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set codestack [list $code]
} elseif {[punk::ansi::codetype::is_sgr $code]} {
@ -10862,10 +10859,9 @@ tcl::namespace::eval punk::args {
set dup_posns [lsearch -all -exact $codestack $code] ;#must be -exact because of square-bracket glob chars
set codestack [lremove $codestack {*}$dup_posns]
lappend codestack $code
} else {
#? ignore other ANSI codes?
}
}
#? ignore other ANSI codes?
}
if {[string match -* $plain_s] || [string match ?- $plain_s]} {
}

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

@ -2986,6 +2986,71 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
time -type integer -optional 1
} "@doc -name Manpage: -url [manpage_tcl file]" ]
lappend PUNKARGS [list {
@id -id ::tcl::file::attributes
@cmd -name "Built-in: tcl::file::attributes"\
-summary\
"Get/Set platform-specific values associated with a file/directory."\
-help\
"This subcommand returns or sets platform-specific values associated with a file.
The first form without specificing option, returns a list of the platform-specific options and their values.
The first form with an option returns the value for the given option.
The last form sets one or more of the values. The values are as follows:
On Unix, ${$B}-group${$N} gets or sets the group name for the file. A group id can be given to the command, but it
returns a group name. ${$B}-owner${$N} gets or sets the user name of the owner of the file. The command returns the
owner name, but the numerical id can be passed when setting the owner. ${$B}-permissions${$N} retrieves or sets a
file's access permissions, using octal notation by default. This option also provides limited support for
setting permissions using the symbolic notation accepted by the chmod command, following the form
${$B}[ugo]?[[+-=][rwxst],[...]]${$N}. Multiple permission specifications may be given, separated by commas.
E.g., ${$B}u+s,go-rw${$N} would set the setuid bit for a file's owner as well as remove read and write permission for
the file's group and other users. An ls-style string of the form rwxrwxrwx is also accepted but must always
be 9 characters long. E.g., ${$B}rwxr-xr-t${$N} is equivalent to ${$B}01755${$N}. On versions of Unix supporting file flags,
${$B}-readonly${$N} returns the value of, or sets, or clears the readonly attribute of a file, i.e., the user
immutable flag (${$B}uchg${$N}) to the ${$B}chflags${$N} command.
On Windows, ${$B}-archive${$N} gives the value or sets or clears the archive attribute of the file. ${$B}-hidden${$N} gives the
value or sets or clears the hidden attribute of the file. ${$B}-longname${$N} will expand each path element to its long
version. This attribute cannot be set. ${$B}-readonly${$N} gives the value or sets or clears the readonly attribute of
the file. ${$B}-shortname${$N} gives a string where every path element is replaced with its short (8.3) version of the
name if possible. For path elements that cannot be mapped to short names, the long name is retained. This
attribute cannot be set. ${$B}-system${$N} gives or sets or clears the value of the system attribute of the file.
On macOS and Darwin, ${$B}-creator${$N} gives or sets the Finder creator type of the file. ${$B}-hidden${$N} gives or sets or
clears the hidden attribute of the file. ${$B}-readonly${$N} gives or sets or clears the readonly attribute of the file.
${$B}-rsrclength${$N} gives the length of the resource fork of the file, this attribute can only be set to the value 0,
which results in the resource fork being stripped off the file.
On all platforms, files in ${$B}zipfs${$N} mounted archives return the following attributes.
These are all read-only and cannot be directly set.
${$B}-archive${$N}
The path of the mounted ZIP archive containing the file.
${$B}-compsize${$N}
The compressed size of the file within the archive. This is 0 for directories.
${$B}-crc${$N}
The CRC of the file if present, else 0.
${$B}-mount${$N}
The path where the containing archive is mounted.
${$B}-offset${$N}
The offset of the file within the archive.
${$B}-uncompsize${$N}
The uncompressed size of the file. This is ${$B}0${$N} for directories.
Other attributes may be present in the returned list. These should be ignored."
@form -form "get"
@values -min 1 -max 2
name -type string -optional 0
option -type stringstartswith(-) -typesynopsis {-${$I}option${$NI}} -optional 1
@form -form "set"
@values -min 3 -max -1
name -type string -optional 0
option_value -type {stringstartswith(-) string} -typesynopsis {-${$I}option${$NI} ${$I}value${$NI}} -optional 0 -multiple 1
} "@doc -name Manpage: -url [manpage_tcl file]" ]
lappend PUNKARGS [list {
@id -id ::tcl::file::channels
@cmd -name "Built-in: tcl::file::channels"\
@ -3026,6 +3091,26 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
pathname -optional 1 -type string -multiple 1
} "@doc -name Manpage: -url [manpage_tcl file]" ]
lappend PUNKARGS [list {
@id -id ::tcl::file::dirname
@cmd -name "Built-in: tcl::file::dirname"\
-summary\
"Return a path excluding last element."\
-help\
"Returns a name comprised of all of the path components in name excluding the last element.
If name is a relative file name and only contains one path element, then returns “.”. If name
refers to a root directory, then the root directory is returned. For example,
${[punk::args::helpers::example {
${$B} file dirname c:/
}]}
returns ${$B}c:/${$N}.
"
@values -min 1 -max 1
name -type string
} "@doc -name Manpage: -url [manpage_tcl file]" ]
lappend PUNKARGS [list {
@id -id ::tcl::file::copy
@cmd -name "Built-in: tcl::file::copy"\
@ -3104,7 +3189,10 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
#tcl 9+
lappend PUNKARGS [list {
@id -id ::tcl::file::home
@cmd -name "Built-in: tcl::file::home" -help\
@cmd -name "Built-in: tcl::file::home"\
-summary\
"Return the home directory for a user."\
-help\
"If no argument is specified, the command returns the home directory of the current user.
This is generally the value of the ${$B}$HOME${$N} environment variable except that on Windows
platforms backslashes in the path are replaced by forward slashes. An error is raised if
@ -3134,7 +3222,29 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
} "@doc -name Manpage: -url [manpage_tcl file]" ]
#join
#link
lappend PUNKARGS [list {
@id -id ::tcl::file::join
@cmd -name "Built-in: tcl::file::join"\
-summary\
"Join directory/file components into a single path."\
-help\
"Takes one or more file names and combines them, using the correct path separator for the current platform.
If a particular name is relative, then it will be joined to the previous file name argument. Otherwise, any
earlier arguments will be discarded, and joining will proceed from the current argument. For example,
${[punk::args::helpers::example {
${$B}file join ${$N} a b /foo bar
}]}
returns ${$B}/foo/bar${$N}.
Note that any of the names can contain separators, and that the result is always canonical for the current
platform: ${$B}/${$N} for Unix and Windows.
"
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]" ]
lappend PUNKARGS [list {
@id -id ::tcl::file::link
@cmd -name "Built-in: tcl::file::link"\
@ -3242,8 +3352,33 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
#owned
#pathtype
lappend PUNKARGS [list {
@id -id ::tcl::file::owned
@cmd -name "Built-in: tcl::file::owned"\
-summary\
"Test file owned by current user."\
-help\
"Returns ${$B}1${$N} if the file ${$I}name${$NI} is owned by the current user, ${$B}0${$N} otherwise."
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
lappend PUNKARGS [list {
@id -id ::tcl::file::pathtype
@cmd -name "Built-in: tcl::file::pathtype"\
-summary\
{Return path type. Either absolute, relative or volumerelative.}\
-help\
"Returns one of ${$B}absolute${$N}, ${$B}relative${$N}, ${$B}volumerelative${$N}. If name refers to a specific file on a specific
volume, the path type will be ${$B}absolute${$N}. If name refers to a file relative to the current working
directory, then the path type will be ${$B}relative${$N}. If name refers to a file relative to the current
working directory on a specified volume, or to a specific file on the current working volume, then
the path type is ${$B}volumerelative${$N}."
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
lappend PUNKARGS [list {
@id -id ::tcl::file::readable
@cmd -name "Built-in: tcl::file::readable"\
@ -3299,9 +3434,46 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
#separator
#size
#split
lappend PUNKARGS [list {
@id -id ::tcl::file::separator
@cmd -name "Built-in: tcl::file::separator"\
-summary\
{File separator character}\
-help\
"If no argument is given, returns the character which is used to separate path segments for native
files on this platform. If a path is given, the filesystem responsible for that path is asked to
return its separator character. If no file system accepts name, an error is generated."
@values -min 0 -max 1
name -optional 1 -type string -help\
"Path to query for separator character."
} "@doc -name Manpage: -url [manpage_tcl file]"]
lappend PUNKARGS [list {
@id -id ::tcl::file::size
@cmd -name "Built-in: tcl::file::size"\
-summary\
{Size of named file in bytes.}\
-help\
"Returns a decimal string giving the size of file ${$I}name${$NI} in bytes.
If the file does not exist or its size cannot be queried then an error is generated."
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
lappend PUNKARGS [list {
@id -id ::tcl::file::split
@cmd -name "Built-in: tcl::file::split"\
-summary\
{Split a path into list of components.}\
-help\
"Returns a list whose elements are the path components in ${$I}name${$NI}. The first element of the list will have
the same path type as ${$I}name${$NI}. All other elements will be relative. Path separators will be discarded unless
they are needed to ensure that an element is unambiguously relative."
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
lappend PUNKARGS [list {
@id -id ::tcl::file::stat
@cmd -name "Built-in: tcl::file::stat"\
@ -3399,8 +3571,20 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
As such, they can be relied upon to be used with operating-system native APIs
and external programs that require a filename."
@values -min 0 -max 2
nameVar -type string -optional 1
template -type string -optional 1
nameVar -type string -optional 1 -help\
"Variable to *receive* the name of the created temporary file.
Any existing value in the variable will not be read, and is just overwritten."
template -type string -optional 1 -help\
"On some platforms, such as windows:
- file extension is ignored.
- any directory components are ignored and
the last segment is used as a prefix for the temporary file name.
- If the TMP or TEMP environment variables are set, they are used
as the directory for the temporary file, otherwise the user's home
directory is used if it can be determined. (may depend on existence
of HOME or USERPROFILE environment variables.)
On other platforms, such as unix, the template may be handled
differently."
} "@doc -name Manpage: -url [manpage_tcl file]"]
#tildeexpand
@ -4528,11 +4712,16 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
}]}
}
@values -min 1
#{args body ?namespace?} is a single argument that is a list of two or three elements,
#as opposed to a clause of separate arguments.
#we don't have a way to validate the type of each element in a list - we can only check the length of the whole list.
@values -min 1 -max -1
"{args body ?namespace?}" -optional 0 -type list -minsize 2 -maxsize 3
arg -type any -optional 1 -multiple 1
} "@doc -name Manpage: -url [manpage_tcl apply]"\
{
@examples -help {
@ -7094,7 +7283,7 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
start -type number|expr
count -type literalprefix(count)
countelements -type number|expr
"by step" -type {literalprefix(by) number|expr} -optional 1
"by step" -type {?literalprefix(by)? number|expr} -optional 1
@form -form count
@leaders -min 0 -max 0
@ -10621,15 +10810,34 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
#force all on_handlers to be together and all try_handlers to be together, and it would force
#one type of handler to be listed always before or always after the other.
handler -optional 1 -multiple 1 -type {literal(on)|literal(trap) string list string}\
-typesynopsis {"" code|pattern variableList script}
-typesynopsis {"" oncode_or_trappattern variableList script}
#in our typesynopsis we deliberately don't put a pipe symbol in oncode_or_trappattern.
# e.g code|pattern would imply either on or trap could be combined with either code or pattern, which is not the case.
#todo?
#a way to define a compound type?
#handler -optional 1 -multiple 1 -type {<on_handler>|<try_handler>}
##<on_handler> -type {literal(on) <code> <variableList> <script>}
##<code> -type int -choices {0|ok 1|error 2|return 3|break 4|continue} -choicelabels {...}
#consider bracketed forms for -type - but we would have to do more complex parsing to determine size of clauses
##handler -type {(literal(on) code variableList script)|(literal(trap) pattern variableList script)}
## in this case either possible handler has length 4 - but we could easily imagine cases where different handlers have different lengths
#this gets unwieldy in synopsis listings.
#a way to define a compound type? perhaps with arity indicators for the component types? e.g
#handler -optional 1 -multiple 1 -type {<on_handler:4>|<try_handler:4>}
##on_handler:4 -type {literal(on) code variableList script}
##code -type int -choices {0|ok 1|error 2|return 3|break 4|continue} -choicelabels {...}
#..
##<try_handler> -type {literal(trap) <pattern> <variableList> <script>}
##<pattern> -type list
##try_handler -type {literal(trap) pattern variableList script}
##pattern -type list
##etc
#how would we declare arity for a compound type that has alternate subtypes of different arity?
#e.g <generalhandler>:3..4 -type {<on_handler:4>|<other_handler:3>}
#would these types be global or per definition?
#if both allowed - what about documentation packages clashing names?
#require some kind of namespacing for types? e.g package::types::code ?)
#e.g punk::args::moduledoc::tkcore::anchor (n|ne|e|se|s|sw|w|nw|center)
#could we provide a way to import for a definition eg @typeimport -package punk::args::moduledoc::tkcore
# so that the types defined there could be used in our definitions without needing to namespace them?
#consider also RPN for compound type definitions
##<mytype1> -type {{int double OR}}
@ -12052,7 +12260,7 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
@form -form "basic"
pattern -type string -optional 1 -help "glob pattern"
@form -form "controlledglob"
@form -form "controlled"
@values -min 2 -max 2
patterntype -type string -choices {-glob -regexp} -typesynopsis -glob|-regex -optional 0
pattern -type string -optional 0

21
src/bootsupport/modules/punk/auto_exec-0.1.0.tm

@ -96,7 +96,24 @@ tcl::namespace::eval punk::auto_exec {
-summary\
"Manage the hash table of autoexec commands cached in ::auto_execs."\
-help\
{see also ::punk::auto_exec::rehash}
{Manage the cache of autoexec commands in the ::auto_execs array.
This is analogous to the 'hash' command in shells such as csh, tcsh and zsh, or 'hash' in bash.
It can be used to display the current cached ${$B}auto_execok${$N} commands, to add new commands to the cache,
to delete commands from the cache, and to clear the cache.
When adding new commands to the cache, it will attempt to find the command string associated with
the given name by calling auto_execok for that name, and if found it will add it to the cache.
If not found, it will display an error message on stderr for that name and add an empty string to
the cache for that name if the name is an absolute path or a bare word.
When displaying commands with ${$B}hash -t ${$I}name${$NI}${$N}, if only a single name is provided, then the output will
be the raw command string associated with that autoexec command in the hash table. If multiple names
are provided, then the output will be a string containing each name and its associated command string
on a separate line.
see also ::punk::auto_exec::rehash}
#---------------------
@form -form {show_or_set}
@ -125,7 +142,7 @@ tcl::namespace::eval punk::auto_exec {
If multiple names are provided, then the output will be a string containing each
name and its associated command string on a separate line."
#---------------------
@form -form {delete}
@form -form {delete} -summary "Delete autoexec commands from the hash table."
@opts
-d -type none -optional 0 -help\
"Delete specified autoexec commands from the hash table."

6
src/bootsupport/modules/punk/char-0.1.0.tm

@ -1860,8 +1860,10 @@ tcl::namespace::eval punk::char {
lappend settype_list [tcl::dict::get $charsets $setname settype]
}
set charset_names [linsert $charset_names 0 "Set Name"]
set settype_list [linsert $settype_list 0 "Set Type"]
#set charset_names [linsert $charset_names 0 "Set Name"]
ledit charset_names 0 -1 "Set Name"
#set settype_list [linsert $settype_list 0 "Set Type"]
ledit settype_list 0 -1 "Set Type"
return [textblock::join -- [list_as_lines -- $charset_names] " " [list_as_lines $settype_list]]
}

20
src/bootsupport/modules/punk/console-0.1.1.tm

@ -2608,7 +2608,7 @@ namespace eval punk::console {
error "dec_request_setting unrecognised name $name. Known values: [dict keys $DECRQSS_DICT]"
}
set str [dict get $DECRQSS_DICT $name]
set re_str [string map [list * \\* \$ \\\$ + \\+ ( \\(] $str] ;#regex escaped
set re_str [string map [list | \\| * \\* \$ \\\$ + \\+ ( \\( ) \\)] $str] ;#regex escaped
#review {[0-9;:]} - too restrictive? - what values can be returned? alnum? - we perhaps at least need to exclude ESC so we don't overmatch
set capturingregex [string map [list %s% $re_str] {(.*)(\x1bP([0-1]\$r[0-9;:]*)(?:%s%){0,1}\x1b\\)$}] ;#must capture prefix,entire-response,response-payload
#todo - handle xterm : [0-1] $ r D...D ST
@ -2938,6 +2938,13 @@ namespace eval punk::console {
proc clear_all {} {
puts -nonewline stdout [punk::ansi::clear_all]
}
proc clear_scrollback {} {
puts -nonewline stdout [punk::ansi::clear_scrollback]
}
proc S8C1R {} {
puts -nonewline stdout [punk::ansi::S8C1R]
}
proc reset {} {
puts -nonewline stdout [punk::ansi::reset]
}
@ -3073,11 +3080,12 @@ namespace eval punk::console {
proc move_emitblock_return {row col textblock} {
lassign [punk::console::get_cursor_pos_list] orig_row orig_col
set commands ""
foreach ln [split $textblock \n] {
append commands [punk::ansi::move_emit $row $col $ln]
incr row
}
set commands [punk::ansi::move_emit $row $col $textblock] ;#move_emit can handle multiple line blocks.
#set commands ""
#foreach ln [split $textblock \n] {
# append commands [punk::ansi::move_emit $row $col $ln]
# incr row
#}
append commands [punk::ansi::move $orig_row $orig_col]
puts -nonewline $commands
return

438
src/bootsupport/modules/punk/lib-0.1.6.tm

@ -2190,6 +2190,7 @@ namespace eval punk::lib {
} else {
set qry $key
}
#pipeline - use punk patterns.
% thisval.= $qry= $dval
}
@ -2219,7 +2220,7 @@ namespace eval punk::lib {
string {
set hidekey 1
switch -- $key {
"%string" {
"%string" - "%str" {
set hidekey 1
set thisval $dval
}
@ -2231,7 +2232,9 @@ namespace eval punk::lib {
}
default {
switch -glob -- $key {
*lpad-* {
%XXXlpad-* {
#todo - remove
#moved to punk patterns
set hidekey 1
lassign [split $key -] _ extra
set width [expr {[textblock::width $dval] + $extra}]
@ -2255,7 +2258,10 @@ namespace eval punk::lib {
set width [expr {[textblock::width $dval] + [tcl::string::length $extra]}]
set thisval [textblock::pad $dval -which right -width $width -padchar $extra]
}
%split-* {
%XXXsplit-* {
#todo - remove
# moved to punk patterns.
#supported here by default branch.
#split on one or more chars - review
set hidekey 1
lassign [split $key -] _ splitchars
@ -2271,7 +2277,7 @@ namespace eval punk::lib {
if {[string index $key 0] ne "%"} {
set key %$key
}
#pipeline
#pipeline - use punk patterns.
% thisval.= $key= $thisval
}
}
@ -3250,7 +3256,7 @@ namespace eval punk::lib {
We will get something like 10+1 - which can be resolved safely with expr
"
@values -min 2 -max 2
datalength -type integer
datalength -type integer -range {0 ""}
index -type indexexpression
}
proc lindex_resolve {len index {base 0}} {
@ -3280,6 +3286,7 @@ namespace eval punk::lib {
#basic forward compatibility with integers such as 1_000 for 8.6.x
set index [tcl::string::map {_ {}} $index]
set len [tcl::string::map {_ {}} $len]
set base [tcl::string::map {_ {}} $base]
}
if {![string is integer -strict $len] || $len < 0} {
@ -3339,10 +3346,10 @@ namespace eval punk::lib {
return $based_max
}
} else {
#plain +-<int> already handled above.
#plain +-<int> already handled above. (but not +-<int>+-<int> etc)
#we are trying to avoid evaluating unbraced expr of potentially insecure origin
#regexp must split a++b to a + +b (not a+ + b) ie first +/- is the op
if {[regexp {([^+-]*)([+-])(.*)} $index _match a op b]} {
if {[regexp {([+-]{0,1}[^+-]*)([+-])(.*)} $index _match a op b]} {
if {[string is integer -strict $a] && [string is integer -strict $b]} {
if {$op eq "-"} {
set index [expr {$a - $b}]
@ -3374,6 +3381,16 @@ namespace eval punk::lib {
#[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
#REVIEW - we need compat for 1_000 etc to handle things like toml even in 8.6?
#A basic string map means we aren't properly validating
#todo - be stricter about malformations such as 1000_
if {![string is integer -strict 1_0]} {
#basic forward compatibility with integers such as 1_000 for 8.6.x
set index [tcl::string::map {_ {}} $index]
set len [tcl::string::map {_ {}} $len]
set base [tcl::string::map {_ {}} $base]
}
if {![string is integer -strict $len] || $len < 0} {
error "lindex_resolve_basic len must be an integer greater than or equal to zero"
}
@ -4196,6 +4213,7 @@ namespace eval punk::lib {
# important for pipeline & match_assign
# -line trimline|trimleft|trimright -block trimhead|trimtail|triminner|trimall|trimhead1|trimtail1|collateempty -commandprefix {string length} ?
# -block trimming only trims completely empty lines. use -line trimming to remove whitespace e.g -line trimright will clear empty lines without affecting leading whitespace on other lines that aren't pure whitespace
set linelist_body {
set usage "linelist ?-ansiresets auto|<bool>? ?-ansireplays 0|1? ?-line trimline|trimleft|trimright? ?-block trimhead|trimtail|triminner|trimall|trimhead1|trimtail1|collateempty? -commandprefix <cmdlist> text"
if {[llength $args] == 0} {
@ -4487,7 +4505,8 @@ namespace eval punk::lib {
}
#set newreplay [join $codestack ""]
set newreplay [punk::ansi::codetype::sgr_merge_list {*}$codestack]
#set newreplay [punk::ansi::codetype::sgr_merge_list {*}$codestack]
set newreplay [punk::ansi::codetype::sgr_merge $codestack]
if {$line_has_sgr && $newreplay ne $replaycodes} {
#adjust if it doesn't already does a reset at start
@ -4823,7 +4842,8 @@ namespace eval punk::lib {
}
#set newreplay [join $codestack ""]
set newreplay [punk::ansi::codetype::sgr_merge_list {*}$codestack]
#set newreplay [punk::ansi::codetype::sgr_merge_list {*}$codestack]
set newreplay [punk::ansi::codetype::sgr_merge $codestack]
if {$RST ne "" && $line_has_sgr && $newreplay ne $replaycodes} {
#adjust if it doesn't already does a reset at start
@ -4868,6 +4888,406 @@ namespace eval punk::lib {
set linelist_body [string map {<require_punk_ansi> "package require punk::ansi"} $linelist_body]
}
proc linelist {args} $linelist_body
set linelist_body2 {
set usage "linelist ?-ansiresets auto|<bool>? ?-ansireplays 0|1? ?-line trimline|trimleft|trimright? ?-block trimhead|trimtail|triminner|trimall|trimhead1|trimtail1|collateempty? -commandprefix <cmdlist> text"
if {[llength $args] == 0} {
error "linelist missing textchunk argument usage:$usage"
}
set text [lindex $args end]
set text [string map {\r\n \n} $text] ;#review - option?
set arglist [lrange $args 0 end-1]
set opts [tcl::dict::create\
-block {trimhead1 trimtail1}\
-line {}\
-commandprefix ""\
-ansiresets auto\
-ansireplays 0\
]
foreach {o v} $arglist {
switch -- $o {
-block - -line - -commandprefix - -ansiresets - -ansireplays {
tcl::dict::set opts $o $v
}
default {
error "linelist: Unrecognized option '$o' usage:$usage"
}
}
}
# -- --- --- --- --- ---
set opt_block [tcl::dict::get $opts -block]
if {[llength $opt_block]} {
foreach bo $opt_block {
switch -- $bo {
trimhead - trimtail - triminner - trimall - trimhead1 - trimtail1 - collateempty {}
default {
set known_blockopts [list trimhead trimtail triminner trimall trimhead1 trimtail1 collateempty]
error "linelist: unknown -block option value: $bo known values: $known_blockopts"
}
}
}
#normalize certain combos
if {"trimhead" in $opt_block && [set posn [lsearch $opt_block trimhead1]] >=0} {
set opt_block [lreplace $opt_block $posn $posn]
}
if {"trimtail" in $opt_block && [set posn [lsearch $opt_block trimtail1]] >=0} {
set opt_block [lreplace $opt_block $posn $posn]
}
if {"trimall" in $opt_block} {
#no other block options make sense in combination with this
set opt_block [list "trimall"]
}
#TODO
if {"triminner" in $opt_block } {
error "linelist -block triminner not implemented - sorry"
}
}
# -- --- --- --- --- ---
set opt_line [tcl::dict::get $opts -line]
set tl_left 0
set tl_right 0
set tl_both 0
foreach lo $opt_line {
switch -- $lo {
trimline {
set tl_both 1
}
trimleft {
set tl_left 1
}
trimright {
set tl_right 1
}
default {
set known_lineopts [list trimline trimleft trimright]
error "linelist: unknown -line option value: $lo known values: $known_lineopts"
}
}
}
#normalize trimleft trimright combo
if {$tl_left && $tl_right} {
set opt_line [list "trimline"]
set tl_both 1
}
# -- --- --- --- --- ---
set opt_commandprefix [tcl::dict::get $opts -commandprefix]
# -- --- --- --- --- ---
set opt_ansiresets [tcl::dict::get $opts -ansiresets]
# -- --- --- --- --- ---
set opt_ansireplays [tcl::dict::get $opts -ansireplays]
if {$opt_ansireplays} {
if {$opt_ansiresets eq "auto"} {
set opt_ansiresets 1
}
} else {
if {$opt_ansiresets eq "auto"} {
set opt_ansiresets 0
}
}
# -- --- --- --- --- ---
#set linelist [list]
#set nlsplit [split $text \n]
set linelist [split $text \n]
set original_length [llength $linelist]
#---------------------------
#todo - consider applying these inline later
if {![llength $opt_line]} {
#set linelist $nlsplit
#lappend linelist {*}$nlsplit
} else {
#already normalized trimleft+trimright to trimline
set nlsplit $linelist
#set linelist [list]
if {$tl_both} {
set i 0
foreach ln $linelist {
#lappend linelist [string trim $ln]
lset linelist $i [string trim $ln]
incr i
}
} elseif {$tl_left} {
set i 0
foreach ln $linelist {
#lappend linelist [string trimleft $ln]
lset linelist $i [string trimleft $ln]
incr i
}
} elseif {$tl_right} {
set i 0
foreach ln $nlsplit {
#lappend linelist [string trimright $ln]
lset linelist $i [string trimright $ln]
incr i
}
}
}
#---------------------------
set remove_indices [list]
if {"collateempty" in $opt_block} {
set last "-"
for {set i 0} {$i < $original_length} {incr i} {
if {[lindex $linelist $i] ne ""} {
set last "-"
} else {
if {$last ne ""} {
lappend remove_indices $i
set last ""
}
}
}
}
if {"trimall" in $opt_block} {
#we have already made sure there are no other block options that would conflict with this
#set linelist [lsearch -all -inline -not -exact $linelist[set linelist {}] ""]
#set remove_indices [list]
for {set i 0} {$i < $original_length} {incr i} {
if {[lindex $linelist $i] eq ""} {
lappend remove_indices $i
}
}
} else {
if {"trimhead" in $opt_block} {
#set remove_indices [list]
for {set i 0} {$i < $original_length} {incr i} {
if {[lindex $linelist $i] ne ""} {
break
} else {
lappend remove_indices $i
}
}
}
if {"trimtail" in $opt_block} {
set remove_indices [list]
for {set i [expr {$original_length-1}]} {$i >=0} {incr i -1} {
if {[lindex $linelist $i] ne ""} {
break
} else {
lappend remove_indices $i
}
}
#set revlinelist [lreverse $linelist][set linelist {}]
#set i 0
#foreach ln $revlinelist {
# if {$ln ne ""} {
# set linelist [lreverse [lrange $revlinelist $i end]]
# break
# }
# incr i
#}
}
# --- ---
set start 0
set end "end"
if {"trimhead1" in $opt_block} {
if {[lindex $linelist 0] eq ""} {
lappend remove_indices 0
}
}
if {"trimtail1" in $opt_block} {
if {[lindex $linelist end] eq ""} {
lappend remove_indices [expr {$original_length-1}]
}
}
#set linelist [lrange $linelist $start $end]
}
#review - we need to make sure ansiresets don't accumulate/grow on any line
#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} {
<require_punk_ansi> ;#package require punk::ansi
if {$opt_ansiresets} {
set RST "\x1b\[0m"
} else {
set RST ""
}
set replaycodes $RST ;#todo - default?
#set transformed [list]
#shortcircuit common case of no ansi
#NOTE: running ta::detect on a list (or dict) as a whole can be problematic if items in the list have backslash escapes due to Tcl list quoting and escaping behaviour.
#This commonly happens if there is an unbalanced brace (which is a normal occurrence and needs to be handled)
#ta::detect on a list of ansi-containing string may appear to work for some simple inputs but is not reliable
#detect_in_list/detectcode_in_list will check at first level. (not intended for detecting ansi in deeper structures)
#we use detectcode_in_list instead of detect_in_list
#detectcode_in_list will detect unclosed (or unopened) paired sequences such as PM (privacy message)
# - but the main reason is it is slightly faster.
if {![punk::ansi::ta::detectcode_in_list $linelist]} {
if {$opt_ansiresets} {
for {set i 0} {$i < $original_length} {incr i} {
if {$i in $remove_indices} {
continue
}
lset linelist $i $RST[lindex $linelist $i]$RST
}
}
} else {
#INLINE punk::ansi::codetype::is_sgr_reset
#regexp {\x1b\[0*m$} $code
set re_is_sgr_reset {\x1b\[0*m$}
#INLINE punk::ansi::codetype::is_sgr
#regexp {\033\[[0-9;:]*m$} $code
set re_is_sgr {\x1b\[[0-9;:]*m$}
#foreach ln $linelist {}
for {set i 0} {$i < $original_length} {incr i} {
if {$i in $remove_indices} {
continue
}
#set ln [lindex $linelist $i]
#set is_replay_pure_reset [regexp {\x1b\[0*m$} $replaycodes] ;#only looks at tail code - but if tail is pure reset - any prefix is ignorable
#set ansisplits [punk::ansi::ta::split_codes_single $ln] ;#REVIEW - this split accounts for a large portion of the time taken to run this function.
#get_codes_single lists only the codes. no plaintext or empty elements
set ansisplits [punk::ansi::ta::get_codes_single [lindex $linelist $i]] ;#REVIEW - this split accounts for a large portion of the time taken to run this function.
if {[llength $ansisplits] == 0} {
#plaintext only - no ansi codes in line
#lappend transformed [string cat $replaycodes $ln $RST]
lset linelist $i $replaycodes[lindex $linelist $i]$RST
#leave replaycodes as is for next line
set nextreplay $replaycodes
} else {
set tail $RST
set lastcode [lindex $ansisplits end] ;#may or may not be SGR
set lastcodeoffset [expr {[string length $lastcode]-1}]
if {[punk::ansi::codetype::is_sgr_reset $lastcode]} {
if {[string range [lindex $linelist $i] end-$lastcodeoffset end] eq $lastcode} {
#last plaintext is empty. So the line is already suffixed with a reset
set tail ""
} else {
#trailing text has been reset within line - but no tail reset present
#we normalize by putting a tail reset on anyway
set tail $RST
}
set nextreplay $RST
} elseif {[string range [lindex $linelist $i] end-$lastcodeoffset end] eq $lastcode && [punk::ansi::codetype::has_sgr_leadingreset $lastcode]} {
#code is at tail (no trailing plaintext)
#No tail reset - and no need to examine whole line to determine stack that is in effect
set tail $RST
set nextreplay $lastcode
} else {
#last codeset doesn't reset from earlier codes or isn't SGR - so we have to look at whole line to determine codes in effect
#last codeset doesn't end in a pure-reset
#whether code was at very end or not - add a reset tail
set tail $RST
#determine effective replay for line
set codestack [list start]
foreach code $ansisplits {
if {[tcl::string::index $code end] eq "m"} {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set codestack [list] ;#different from 'start' marked - this means we've had a reset
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set codestack [list $code]
} else {
if {[punk::ansi::codetype::is_sgr $code]} {
#todo - proper test of each code - so we only take latest background/foreground etc.
#requires handling codes with varying numbers of parameters.
#basic simplification - remove straight dupes.
set dup_posns [lsearch -all -exact $codestack $code] ;#!must use -exact as codes have square brackets which are interpreted as glob chars.
set codestack [lremove $codestack {*}$dup_posns]
lappend codestack $code
}
}
}
;#else gx0 or other code - we don't want to stack it with SGR codes
}
if {[llength $codestack] == 1 && [lindex $codestack 0] eq "start"} {
#No SGRs - may have been other codes
set line_has_sgr 0
} else {
#list is either empty or begins with start - empty means it had SGR reset - so it still invalidates current state of replaycodes
set line_has_sgr 1
if {[lindex $codestack 0] eq "start"} {
#set codestack [lrange $codestack 1 end]
ledit codestack 0 0
}
}
if {$line_has_sgr} {
#set newreplay [punk::ansi::codetype::sgr_merge_list {*}$codestack]
set newreplay [punk::ansi::codetype::sgr_merge $codestack]
if {$newreplay ne $replaycodes} {
#adjust if it doesn't already does a reset at start
if {$RST ne ""} {
if {[punk::ansi::codetype::has_sgr_leadingreset $newreplay]} {
set nextreplay $newreplay
} else {
set nextreplay $RST$newreplay
}
} else {
set nextreplay $newreplay
}
} else {
set nextreplay $replaycodes
}
} else {
set nextreplay $replaycodes
}
}
if {"$replaycodes$tail" ne ""} {
if {[punk::ansi::codetype::has_sgr_leadingreset [lindex $linelist $i]]} {
#no point attaching any replay
#lappend transformed [string cat $ln $tail]
if {$tail ne ""} {
lset linelist $i [lindex $linelist $i]$tail
}
} else {
#lappend transformed [string cat $replaycodes $ln $tail]
lset linelist $i $replaycodes[lindex $linelist $i]$tail
}
}
}
set replaycodes $nextreplay
}
#jjj
#set linelist $transformed
}
}
#todo - run this before ansireplay processing and adjust indices accordingly? or just run it after as is and accept that commandprefix will be added to each line after replay processing?
if {[llength $opt_commandprefix]} {
for {set i 0} {$i < $original_length} {incr i} {
if {$i in $remove_indices} {
continue
}
lset linelist $i [{*}$opt_commandprefix [lindex $linelist $i]]
}
#set transformed [list]
#foreach ln $linelist {
# lappend transformed [{*}$opt_commandprefix $ln]
#}
#set linelist $transformed
}
if {[llength $remove_indices]} {
set linelist [lremove $linelist {*}$remove_indices]
}
return $linelist
}
if {$has_punk_ansi} {
#optimise linelist as much as possible
set linelist_body2 [string map {<require_punk_ansi> ""} $linelist_body2]
} else {
#punk ansi not avail at time of package load.
#by putting in calls to punk::ansi the user will get appropriate error messages
set linelist_body2 [string map {<require_punk_ansi> "package require punk::ansi"} $linelist_body2]
}
proc linelist {args} $linelist_body2
interp alias {} errortime {} punk::lib::errortime

1
src/bootsupport/modules/punk/libunknown-0.1.tm

@ -950,6 +950,7 @@ tcl::namespace::eval ::punk::libunknown {
}
if {$has_prefix} {
set update [linsert $update end-$offset $new]
#end based index used with linsert - so can't replace with ledit.
} else {
lappend update $new
}

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

@ -43,7 +43,7 @@ namespace eval punk::mix::commandset::repo {
lappend PUNKARGS [list {
@id -id ::punk::mix::commandset::repo::fossilize
@cmd -name punk::mix::commandset::repo::fossilize
@cmd -name punk::mix::commandset::repo::fossilize\
-summary\
"Initialise and check in a project to fossil (unimplemented)."\
-help\
@ -56,7 +56,7 @@ namespace eval punk::mix::commandset::repo {
lappend PUNKARGS [list {
@id -id ::punk::mix::commandset::repo::unfossilize
@cmd -name punk::mix::commandset::repo::unfossilize
@cmd -name punk::mix::commandset::repo::unfossilize\
-summary\
"Remove/archive .fossil (unimplemented)."\
-help\
@ -92,9 +92,9 @@ namespace eval punk::mix::commandset::repo {
#punk::args
lappend PUNKARGS [list {
@id -id ::punk::mix::commandset::repo::fossil-move-repository
@cmd -name punk::mix::commandset::repo::fossil-move-repository
@cmd -name punk::mix::commandset::repo::fossil-move-repository\
-summary\
"Move a fossil repository database file."\
"Interactively move a fossil repository database file."\
-help\
"Move the fossil repository file (usually named with .fossil extension).
This is an interactive function which will prompt for answers on stdin

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

@ -1170,22 +1170,24 @@ tcl::namespace::eval punk::ns {
#NOTE aliases may not be commands in current namespace - but we want to show them (marked red and with R)
#
set children [list]
set commands [list]
set exported [list]
set imported [list]
set aliases [list]
set procs [list]
set ensembles [list]
set ooclasses [list]
set ooobjects [list]
set children [list]
set packagetails [list]
set packageprefixes [list]
set commands [list]
set exported [list]
set imported [list]
set aliases [list]
set procs [list]
set ensembles [list]
set ooclasses [list]
set ooobjects [list]
set ooprivateobjects [list]
set ooprivateclasses [list]
set native [list]
set interps [list]
set coroutines [list]
set zlibstreams [list]
set usageinfo [list]
set native [list]
set interps [list]
set coroutines [list]
set zlibstreams [list]
set usageinfo [list]
if {![dict size $opt_nsdict]} {
set nsmatches [get_ns_dicts $fq_glob -allbelow 0]
@ -1216,6 +1218,8 @@ tcl::namespace::eval punk::ns {
package require overtype
if {"children" in $types} {
set children [dict get $contents children]
set packagetails [dict get $contents packagetails]
set packageprefixes [dict get $contents packageprefixes]
}
if {"commands" in $types} {
set commands [dict get $contents commands]
@ -1368,12 +1372,26 @@ tcl::namespace::eval punk::ns {
set c_ooC [a+ term-cornflowerblue] ;#privateClass
set c_zst [a+ term-yellow] ;#zlibstreams
set a1 [a][a+ cyan]
set a1 [a][a+ cyan] ;#child namespace SGR code.
foreach ch1 $children1 ch2 $children2 cmd1 $elements1 cmd2 $elements2 cmd3 $elements3 cmd4 $elements4 {
set c1 [a+ white]
set c2 [a+ white]
set c3 [a+ white]
set c4 [a+ white]
foreach nsvar {ch1 ch2} {
set v [set $nsvar]
if {$v in $packagetails} {
#may also be a packageprefix.
if {$v in $packageprefixes} {
set $nsvar [a+ underdouble]$v
} else {
#just a package - no prefix - we want to underline but not doubled
set $nsvar [a+ underline]$v
}
} elseif {$v in $packageprefixes} {
set $nsvar [a+ underdotted]$v
}
}
for {set i 1} {$i <= 4} {incr i} {
if {[llength [set cmd$i]]} {
@ -1441,7 +1459,7 @@ tcl::namespace::eval punk::ns {
}
#lappend displaylist $a1[overtype::left $col1 $ch1][a+]$a1[overtype::left $col2 $ch2][a+]$c1[overtype::left $col3 $cmd1][a+]$c2[overtype::left $col4 $cmd2][a+]$c3[overtype::left $col5 $cmd3][a+]$c4$cmd4[a+]
lappend displaylist $a1[overtype::left $col1 $ch1][a]$a1[overtype::left $col2 $ch2][a]$c1[overtype::left $col3 $cmd1][a]$c2[overtype::left $col4 $cmd2][a]$c3[overtype::left $col5 $cmd3][a]$c4$cmd4[a]
lappend displaylist $a1[overtype::left $col1 $ch1[a]][a]$a1[overtype::left $col2 $ch2][a]$c1[overtype::left $col3 $cmd1][a]$c2[overtype::left $col4 $cmd2][a]$c3[overtype::left $col5 $cmd3][a]$c4$cmd4[a]
}
return [list_as_lines $displaylist]
@ -3043,8 +3061,11 @@ y" {return quirkykeyscript}
set nspathcommands [dict get $opts -nspathcommands]
# -- --- --- --- --- --- --- --- --- --- --- ---
set packagetails [list] ;#child namespaces which are an exact match for a package name
set packageprefixes [list] ;#child namespaces which are a prefix match for a package name - but not an exact match
#set location [nsprefix $fq_glob]
set commands [list]
set commands [list]
set nsglob [nsprefix $fq_glob]
set glob [nstail $fq_glob]
@ -3471,10 +3492,27 @@ y" {return quirkykeyscript}
# set childtailmatches [lsort $childtailmatches]
#}
set childtailmatches [lsort -dictionary $childtailmatches]
foreach ct $childtailmatches {
set fqchild [nsjoin $location $ct]
set searchname [string trimleft $fqchild :]
foreach pkgname [lsearch -all -inline [package names] $searchname*] {
if {$pkgname eq $searchname} {
#exact match.
lappend packagetails $ct
} else {
if {[string match ${searchname}::* $pkgname]} {
#prefix match - but not exact match
lappend packageprefixes $ct
}
}
}
}
set nsdict [dict create\
location $location\
children $childtailmatches\
packagetails $packagetails\
packageprefixes $packageprefixes\
commands $commands\
procs $procs\
exported $exported\
@ -4807,7 +4845,8 @@ y" {return quirkykeyscript}
set scriptcmd [dict get $scriptinfo which]
set scriptargs [lrange $origin 1 end]
#ledit args -1 -1 {*}$scriptargs ;#prepend
set args [linsert $args 1 {*}$scriptargs]
#set args [linsert $args 1 {*}$scriptargs]
ledit args 1 -1 {*}$scriptargs ;#insert scriptargs before arg at index 1
#JJJ review
#set resolvedargs $scriptargs
punk::args::update_definitions [list [namespace qualifiers $scriptcmd]]
@ -5240,7 +5279,7 @@ y" {return quirkykeyscript}
the synopsis for that form.
"
@opts
-form -type string -default * -help\
-form -type number|name -default * -help\
"Ordinal index or name of command form."
-return -type string -default full -choices {full summary dict}
@values -min 1 -max -1
@ -5291,7 +5330,7 @@ y" {return quirkykeyscript}
full - summary {
set resultstr ""
foreach synline [split $syn \n] {
if {[string range $synline 0 1] eq "# "} {
if {[string range $synline 0 1] in {"# " "##"}} {
append resultstr $synline \n
} else {
#puts stderr [textblock::frame $syn]
@ -5447,9 +5486,9 @@ y" {return quirkykeyscript}
}
if {$opt_grepstr ne ""} {
if {[llength $opt_grepstr] == 1} {
set result [punk::ansi::grepstr --ignore-case -returnlines all [lindex $opt_grepstr 0] $result]
set result [punk::ansi::grepstr --ignore-case -return all [lindex $opt_grepstr 0] $result]
} else {
set result [punk::ansi::grepstr --ignore-case -returnlines all -highlight [lrange $opt_grepstr 1 end] [lindex $opt_grepstr 0] $result]
set result [punk::ansi::grepstr --ignore-case -return all -highlight [lrange $opt_grepstr 1 end] [lindex $opt_grepstr 0] $result]
}
}
return $result
@ -5529,9 +5568,9 @@ y" {return quirkykeyscript}
}
if {$opt_grepstr ne ""} {
if {[llength $opt_grepstr] == 1} {
set result [punk::ansi::grepstr --ignore-case -returnlines all [lindex $opt_grepstr 0] $result]
set result [punk::ansi::grepstr --ignore-case -return all [lindex $opt_grepstr 0] $result]
} else {
set result [punk::ansi::grepstr --ignore-case -returnlines all -highlight [lrange $opt_grepstr 1 end] [lindex $opt_grepstr 0] $result]
set result [punk::ansi::grepstr --ignore-case -return all -highlight [lrange $opt_grepstr 1 end] [lindex $opt_grepstr 0] $result]
}
}
return $result
@ -6674,7 +6713,7 @@ y" {return quirkykeyscript}
set body [punk::ansi::grepstr -return all -highlight term-teal {^(\{)|[^\\](\{+)} $body]
set body [punk::ansi::grepstr -return all -highlight term-teal {[^\\](\}+)} $body]
set body [punk::ansi::grepstr -return all -highlight term-orange {\[|\]} $body]
set body [punk::ansi::grepstr -return all -highlight term-orange1 {\[|\]} $body]
}
default {
set is_highlighted 0

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

@ -1078,7 +1078,8 @@ namespace eval punk::repl::class {
# incr nextrow -1
#}
#set o_rendered_lines [linsert $o_rendered_lines $cursor_row_idx ""]
ledit o_rendered_lines $cursor_row_idx $cursor_row_idx-1 ""
#ledit o_rendered_lines $cursor_row_idx $cursor_row_idx-1 ""
ledit o_rendered_lines $cursor_row_idx -1 ""
set o_cursor_col 1
}
@ -1151,7 +1152,9 @@ namespace eval punk::repl::class {
lappend o_rendered_lines ""
set activeline ""
}
lset o_rendered_lines $cursor_row_idx $result
#JULZ
#lset o_rendered_lines $cursor_row_idx $result
lset o_rendered_lines $cursor_row_idx $result\x1b[m
incr i
}
@ -1289,7 +1292,9 @@ namespace eval punk::repl::class {
set charhighlight [punk::ansi::a+ reverse]$char_at_cursor[a]
}
set cursorline [overtype::renderline -transparent 1 -insert_mode 0 -expand_right 0 $cursorline $prefix$charhighlight$suffix]
lset lines $o_cursor_row-1 $cursorline
#JULZ
#lset lines $o_cursor_row-1 $cursorline
lset lines $o_cursor_row-1 $cursorline\x1b[m
}
set numcol "$ANSI_linenum[join $nums \n][a]"
@ -1765,7 +1770,7 @@ proc punk::repl::console_debugview {editbuf consolewidth args} {
set patch_height [expr {2 + $debug_height + 2}]
set spacepatch [textblock::block $debug_width $patch_height " "]
#puts -nonewline [punk::ansi::cursor_off]
punk::console::cursor_off
#punk::console::cursor_off
#use non cursorsave versions - cursor save/restore will interfere with any concurrent ansi rendering that uses save/restore - because save/restore is a single item, not a stack.
set debug_offset [expr {$consolewidth - $debug_width - $opt_rightmargin}]
set row_clear [expr {$opt_row -2}]
@ -1773,7 +1778,7 @@ proc punk::repl::console_debugview {editbuf consolewidth args} {
punk::console::move_emitblock_return $opt_row $debug_offset $info
set topleft [list $debug_offset $opt_row] ;#col,row REVIEW
#puts -nonewline [punk::ansi::cursor_on]
punk::console::cursor_on
#punk::console::cursor_on
flush stdout
return [dict create width $debug_width height $debug_height topleft $topleft]
@ -2000,8 +2005,12 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
#if {$chunk eq "\x1b\[C"} {
#}
punk::console::cursor_off
flush stdout
$editbuf add_chunk $chunk
#--------------------------
# editbuf and debugview rhs frames
#for now disable entirely on vt52 - we can only do cursor save restore - nothing that requires responses on stdin (?)
@ -2058,7 +2067,9 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
flush stdout
#move_column is more efficient than move since it doesn't require a response on stdin to determine current column,
#but doesn't seem to be universally supported (kermit95 vt modes for example)
#the Horizontal Position Absolute sequence ESC \[ n ` seems to be a possible alternative.
set leftmargin 3
if {!$is_vt52} {
puts -nonewline stdout [a+ cyan][punk::ansi::move_column [expr {$leftmargin +1}]][punk::ansi::erase_eol][$editbuf line $cursor_row][a][punk::ansi::move_column [expr {$leftmargin + [$editbuf cursor_column]}]]
@ -2089,6 +2100,9 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
lappend input_chunks_waiting($inputchan) $waiting
}
}
punk::console::cursor_on
flush stdout
if {$editbuf_linenum_submitted == 0} {
#(there is no line 0 - lines start at 1)
if {[$editbuf last_char] eq "\n"} {
@ -2685,8 +2699,10 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
#editbuf
#----------------------------------------------------------------------------
#after any external command - raw mode as the console sees it can be disabled
#set it to match current state of the tsv
#----------------------------------------------------------------------------
if {[tsv::get console is_raw]} {
if {$::tcl_platform(platform) eq "windows"} {
#review
@ -2696,22 +2712,24 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
set sinfo [chan configure stdin]
if {[dict exists $sinfo -inputmode]} {
if {[dict get $sinfo -inputmode] ne "raw"} {
set re_enable_required 1
set re_enable_raw_required 1
} else {
set re_enable_required 0
set re_enable_raw_required 0
}
} else {
# -inputmode unavailable
#tcl 8.6 doesn't have -inputmode - meaning it has to call punk:console::enableRaw each time
#enableRaw on windows without twapi involves launching a pwsh process - which gives a noticeable lag in keyboard input.
#enableRaw on Unix involves a call to stty - which is generally fast - but still to be avoided if not required.
set re_enable_required 1
set re_enable_raw_required 1
}
#puts stderr "-here- re-enabling raw"
if {$re_enable_required} {
if {$re_enable_raw_required} {
punk::console::enableRaw
}
}
#----------------------------------------------------------------------------
} else {
#append commandstr \n
if {$::punk::repl::signal_control_c} {
@ -3801,7 +3819,8 @@ namespace eval repl {
#puts stderr [thread::id]
if {[llength $::codethread_initstatus] == 1} {
set ::codethread_initstatus [linsert $::codethread_initstatus 0 ok]
#set ::codethread_initstatus [linsert $::codethread_initstatus 0 ok]
ledit ::codethread_initstatus 0 -1 ok
}
thread::id
}

4
src/bootsupport/modules/punk/repo-0.1.1.tm

@ -249,7 +249,7 @@ namespace eval punk::repo {
@form -form "parsed"
${[punk::repo::get_fossil_subcommand_usage add]}
@form -form "raw" -synopsis "exec fossil add ?OPTIONS? FILE1 ?FILE2 ...?"
@form -form "raw" -synopsis "exec fossil add \[OPTIONS\] FILE1 \[FILE2\]..."
@formdisplay -header "fossil help add" -body {${[runout -n fossil help add]}}
} ""]
@ -263,7 +263,7 @@ namespace eval punk::repo {
@form -form "parsed"
${[punk::repo::get_fossil_subcommand_usage diff]}
@form -form "raw" -synopsis "exec fossil diff ?OPTIONS? FILE1 ?FILE2 ...?"
@form -form "raw" -synopsis "exec fossil diff \[OPTIONS\] FILE1 \[FILE2\]..."
@formdisplay -header "fossil help diff" -body {${[runout -n fossil help diff]}}
} ""]

4
src/bootsupport/modules/punkcheck-0.1.0.tm

@ -324,7 +324,7 @@ namespace eval punkcheck {
lappend record_list $o_fileset_record
} else {
#set record_list [linsert $record_list[unset record_list] $oldposition $o_fileset_record]
ledit record_list $oldposition $oldposition-1 $o_fileset_record
ledit record_list $oldposition -1 $o_fileset_record
}
if {$o_operation ne "QUERY"} {
punkcheck::save_records_to_file $record_list $punkcheck_file
@ -796,7 +796,7 @@ namespace eval punkcheck {
lappend record_list $file_record
} else {
#set record_list [linsert $record_list[unset record_list] $oldposition $file_record]
ledit record_list $oldposition $oldposition-1 $file_record
ledit record_list $oldposition -1 $file_record
}
save_records_to_file $record_list $punkcheck_file

42
src/bootsupport/modules/shellfilter-0.2.1.tm

@ -755,6 +755,8 @@ namespace eval shellfilter::chan {
#puts stdout "===[ansistring VIEW -lf 1 $o_buffered]"
set buf $o_buffered$chunk
set emit ""
#Note 8-bit csi \x9b has already been mapped in the chunk to 7-bit form \x1b\[ by the caller - so we only need to check for \x1b here
#(under review - ideally we might not want to normalize 8-bit to 7-bit in a channel transform))
if {[string last \x1b $buf] >= 0} {
#detect will detect ansi SGR and gron groff and other codes
#REVIEW - ta::detect won't detect SOS without paired ST for things like PM
@ -798,18 +800,21 @@ namespace eval shellfilter::chan {
] $c1c2] 0 3]
switch -- $leadernorm {
7CSI - 8CSI {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set o_codestack [list "\x1b\[m"]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set o_codestack [list $code]
} elseif {[punk::ansi::codetype::is_sgr $code]} {
#todo - make caching is_sgr method
set dup_posns [lsearch -all -exact $o_codestack $code]
set o_codestack [lremove $o_codestack {*}$dup_posns]
lappend o_codestack $code
} else {
set code_endswith_m [expr {[tcl::string::index $code end] eq "m"}]
if {$code_endswith_m} {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
#review this normalizing of reset to a single form.
set o_codestack [list "\x1b\[m"]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set o_codestack [list $code]
} elseif {[punk::ansi::codetype::is_sgr $code]} {
#todo - make caching is_sgr method
set dup_posns [lsearch -all -exact $o_codestack $code]
set o_codestack [lremove $o_codestack {*}$dup_posns]
lappend o_codestack $code
}
}
}
7GFX {
switch -- [tcl::string::index $code 2] {
@ -1029,6 +1034,21 @@ namespace eval shellfilter::chan {
return ""
}
}
#------------------------------------------------------
# REVIEW
#Trackcodes logic is primarily designed for 7-bit codes
#It would be complex for it to support 8-bit as well
#- we can do a simple pre-map to convert 8-bit CSI to 7-bit CSI before processing
#we already normalize things like resets to a single 7-bit form anyway.
#review - is there a need for an ansiwrap channel that preserves 8-bit codes?
#8-bit are rarely used these days - and many terminals don't support them.
#We could take the view here that we should understand them but not emit them in general.
#Nonetheless - converting them on a channel transform like this is potentially suprising in some circumstances,
#and we don't necessarily know the intent of both the producer and consumer of the stream.
set stringdata [string map [list \x9b \x1b\[ ] $stringdata]
#------------------------------------------------------
set streaminfo [my Trackcodes $stringdata]
set emit [dict get $streaminfo emit]

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

@ -2116,7 +2116,7 @@ tcl::namespace::eval textblock {
set ansibase_header [tcl::dict::get $o_opts_table -ansibase_header] ;#merged to single during configure
set ansiborder_header [tcl::dict::get $o_opts_table -ansiborder_header]
if {[tcl::dict::get $o_opts_table -frametype_header] eq "block"} {
set extrabg [punk::ansi::codetype::sgr_merge_singles [list $ansibase_header] -filter_fg 1]
set extrabg [punk::ansi::codetype::sgr_merge_singles [list $ansibase_header] -filter_fg]
set ansiborder_final $ansibase_header$ansiborder_header$extrabg
} else {
set ansiborder_final $ansibase_header$ansiborder_header
@ -2504,7 +2504,7 @@ tcl::namespace::eval textblock {
if {[tcl::dict::get $o_opts_table -frametype] eq "block"} {
#block is the only style where bg colour can fill the frame content area exactly if the L-shaped border elements are styled
#we need to only accept background ansi codes from the columndef ansibase for this
set col_bg [punk::ansi::codetype::sgr_merge_singles [list $opt_col_ansibase] -filter_fg 1] ;#special merge for block borders - don't override fg colours
set col_bg [punk::ansi::codetype::sgr_merge_singles [list $opt_col_ansibase] -filter_fg] ;#special merge for block borders - don't override fg colours
set border_ansi $body_ansibase$body_ansiborder$col_bg
} else {
set border_ansi $body_ansibase$body_ansiborder
@ -2520,7 +2520,7 @@ tcl::namespace::eval textblock {
set row_bg ""
set row_ansibase [tcl::dict::get $o_rowdefs $r -ansibase]
if {$row_ansibase ne ""} {
set row_bg [punk::ansi::codetype::sgr_merge_singles [list $row_ansibase] -filter_fg 1]
set row_bg [punk::ansi::codetype::sgr_merge_singles [list $row_ansibase] -filter_fg]
}
#todo - joinleft,joinright,joindown based on opts in args
@ -2542,8 +2542,8 @@ tcl::namespace::eval textblock {
lappend ptlens [string length $pt]
}
#set takebg [lindex $parts end-1]
#set cell_bg [punk::ansi::codetype::sgr_merge_singles [list $takebg] -filter_fg 1]
set cell_bg [punk::ansi::codetype::sgr_merge_singles $codes -filter_fg 1 -filter_reset 1]
#set cell_bg [punk::ansi::codetype::sgr_merge_singles [list $takebg] -filter_fg]
set cell_bg [punk::ansi::codetype::sgr_merge_singles $codes -filter_fg -filter_reset]
#puts --->[ansistring VIEW $codes]
if {[punk::ansi::codetype::is_sgr_reset [lindex $codes end-1]]} {
@ -2554,7 +2554,7 @@ tcl::namespace::eval textblock {
set ansibase ""
set row_ansibase ""
if {$ftblock} {
set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_body_col_row] -filter_bg 1]
set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_body_col_row] -filter_bg]
set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_final $cell_bg]]
}
set cell_ansibase $cell_ansi_tail
@ -2577,7 +2577,7 @@ tcl::namespace::eval textblock {
# set ansibase ""
# set row_ansibase ""
# if {$ftblock} {
# set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_body_col_row] -filter_bg 1]
# set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_body_col_row] -filter_bg]
# }
# set cell_ansibase $cell_ansi_tail
# } else {
@ -2643,7 +2643,7 @@ tcl::namespace::eval textblock {
}
#return empty (zero content height) row if no rows
if {![llength $cells]} {
set basebg [punk::ansi::codetype::sgr_merge_singles [list $body_ansibase] -filter_fg 1]
set basebg [punk::ansi::codetype::sgr_merge_singles [list $body_ansibase] -filter_fg]
set ansiborder_final [punk::ansi::codetype::sgr_merge [list $basebg $body_ansiborder]]
set joins [lremove $joins [lsearch $joins down*]]
@ -4497,7 +4497,7 @@ tcl::namespace::eval textblock {
foreach {pt code} [lrange $parts 2 end] {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
#set parts [linsert $parts $code_idx+1 $base]
ledit parts $code_idx+1 $code_idx $base
ledit parts $code_idx+1 -1 $base
}
incr code_idx 2
}
@ -4527,8 +4527,9 @@ tcl::namespace::eval textblock {
}
}
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set parts [linsert $parts [expr {$code_idx+1+$offset}] $base]
#set parts [linsert $parts [expr {$code_idx+1+$offset}] $base]
#ledit parts [expr {$code_idx+1+$offset}] $code_idx+$offset $base
ledit parts [expr {$code_idx+1+$offset}] -1 $base
incr offset
}
incr code_idx 2
@ -4912,7 +4913,8 @@ tcl::namespace::eval textblock {
set colour2 [tcl::string::map [list rainbow [lindex $rainbow_list $i]] $colour]
set ansi [a+ {*}$colour2]
set ansicode [punk::ansi::codetype::sgr_merge_list "" $ansi]
#set ansicode [punk::ansi::codetype::sgr_merge_list "" $ansi]
set ansicode [punk::ansi::codetype::sgr_merge [list $ansi]]
lappend clist ${ansicode}$c$RST
}
if {$noreset} {
@ -4926,8 +4928,9 @@ tcl::namespace::eval textblock {
set block ""
for {set r 0} {$r < $size} {incr r} {
set colour2 [tcl::string::map [list rainbow [lindex $rainbow_list $r]] $colour]
set ansi [a+ {*}$colour2]
set ansicode [punk::ansi::codetype::sgr_merge_list "" $ansi]
set ansi [a+ {*}$colour2] ;#not always a single SGR sequence (ESC...m) e.g when contains 'underdotted'
#set ansicode [punk::ansi::codetype::sgr_merge_list "" $ansi]
set ansicode [punk::ansi::codetype::sgr_merge [list $ansi]]
set row "$ansicode"
foreach c $charsubset {
append row $c
@ -5393,10 +5396,11 @@ tcl::namespace::eval textblock {
}
r-1 {
if {[lindex $line_chunks end] eq ""} {
#Insert so that pad *ends* up at position end-2
set line_chunks [linsert $line_chunks end-2 $pad]
#breaks layout e.g subtables in: i i
#why?
#ledit line_chunks end-2 end-3 $pad
#Note that 'ledit line_chunks end-2 -1 $pad' is not equivalent,
#because linsert behaves differently depending on whether the index is start-relative or end-relative.
#(breaks layout e.g subtables in: i i)
} else {
lappend line_chunks $pad
}
@ -5487,6 +5491,9 @@ tcl::namespace::eval textblock {
r-2 {
if {[lindex $line_chunks end] eq ""} {
set line_chunks [linsert $line_chunks end-2 $pad]
#(ledit line_chunks end-2 -1 $pad) is not equivalent to linsert
#because of the different behaviour of end-relative vs start-relative indices with linsert
#- it can break layout e.g subtables in: i i
} else {
lappend line_chunks $pad
}

405
src/modules/overtype-999999.0a1.0.tm

@ -90,7 +90,9 @@ package require punk::assertion
# - need to extract and replace ansi codes?
tcl::namespace::eval overtype {
namespace import ::punk::assertion::assert
if {[info commands ::overtype::assert] eq ""} {
namespace import ::punk::assertion::assert
}
punk::assertion::active true
namespace path ::punk::lib
@ -625,7 +627,7 @@ tcl::namespace::eval overtype {
#set overtext [lpop inputchunks 0] ;#could be a list 'ansisplit' or text 'plain|mixed'
lassign [lpop inputchunks 0] overtext_type overtext
#use eq test with emptystring instead of 'string length' - test for emptiness shouldn't cause shimmering if popped inputchunks member if an 'ansisplit' list
#use eq test with emptystring instead of 'string length' - test for emptiness shouldn't cause shimmering if popped inputchunks member is an 'ansisplit' list
if {$overtext eq ""} {
incr loop
continue
@ -728,7 +730,7 @@ tcl::namespace::eval overtype {
set existing_reverse_state 0
#split_codes_single is single esc sequence - but could have multiple sgr codes within one esc sequence
#e.g \x1b\[0;31;7m has a reset,colour red and reverse
set codeinfo [punk::ansi::codetype::sgr_merge [list $replay_codes_overlay] -info 1]
set codeinfo [punk::ansi::codetype::sgr_merge [list $replay_codes_overlay] -info]
set codestate_reverse [dict get $codeinfo codestate reverse]
switch -- $codestate_reverse {
7 {
@ -863,7 +865,7 @@ tcl::namespace::eval overtype {
# ----
# review
set col $post_render_col
#just because it's out of range of the renderwidth - doesn't mean a move down should jump to witin the range - 2025
#just because it's out of range of the renderwidth - doesn't mean a move down should jump to within the range - 2025
#----
#set existingdata [lindex $outputlines [expr {$post_render_row -1}]]
@ -908,7 +910,7 @@ tcl::namespace::eval overtype {
#It would perhaps be more properly handled as a queue of instructions from our initial renderline call
#we don't need to worry about overflow next call (?)- but we should carry forward our gx and ansi stacks
puts stdout ">>>[a+ red bold]overflow_right during restore_cursor[a]"
puts stdout ">>>renderspace<<<[a+ red bold]overflow_right during restore_cursor[a]"
set sub_info [overtype::renderline\
-info 1\
@ -924,7 +926,7 @@ tcl::namespace::eval overtype {
tcl::dict::set vtstate autowrap_mode [tcl::dict::get $sub_info autowrap_mode] ;#nor this..
#todo!!!
# 2025 fix - this does nothing - so what uses it?? create a test!
# 2025 fix - this does nothing - so what is the intention?? create a test!
linsert outputlines $renderedrow $foldline
#review - row & col set by restore - but not if there was no save..
}
@ -1053,7 +1055,9 @@ tcl::namespace::eval overtype {
set overflow_right ""
} else {
if {[tcl::dict::get $vtstate autowrap_mode]} {
set outputlines [linsert $outputlines $renderedrow $overflow_right]
#set outputlines [linsert $outputlines $renderedrow $overflow_right]
#ledit outputlines $renderedrow $renderedrow-1 $overflow_right
ledit outputlines $renderedrow -1 $overflow_right
set overflow_right ""
set row [expr {$renderedrow + 2}]
} else {
@ -1150,7 +1154,8 @@ tcl::namespace::eval overtype {
if {$insert_lines_above > 0} {
set row $renderedrow
#set outputlines [linsert $outputlines $renderedrow-1 {*}[lrepeat $insert_lines_above ""]]
ledit outputlines $renderedrow-1 $renderedrow-2 {*}[lrepeat $insert_lines_above ""]
#ledit outputlines $renderedrow-1 $renderedrow-2 {*}[lrepeat $insert_lines_above ""]
ledit outputlines $renderedrow-1 -1 {*}[lrepeat $insert_lines_above ""]
incr row [expr {$insert_lines_above -1}] ;#we should end up on the same line of text (at a different index), with new empties inserted above
#? set row $post_render_row #can renderline tell us?
}
@ -1461,6 +1466,7 @@ tcl::namespace::eval overtype {
set nextprefix_list $overflow_right_pt_code_pt
} else {
#merge tail and head
#ledit <list> end end <val> will work with empty list (ledit <list> end <val> does not)
ledit nextprefix_list end end "[lindex $nextprefix_list end][lindex $overflow_right_pt_code_pt 0]"
lappend nextprefix_list {*}[lrange $overflow_right_pt_code_pt 1 end]
}
@ -1476,16 +1482,17 @@ tcl::namespace::eval overtype {
}
if 0 {
if {$nextprefix ne ""} {
set nextoveridx [expr {$overidx+1}]
if {$nextoveridx >= [llength $inputchunks]} {
lappend inputchunks $nextprefix
} else {
#lset overlines $nextoveridx $nextprefix[lindex $overlines $nextoveridx]
set inputchunks [linsert $inputchunks $nextoveridx $nextprefix]
if {$nextprefix ne ""} {
set nextoveridx [expr {$overidx+1}]
if {$nextoveridx >= [llength $inputchunks]} {
lappend inputchunks $nextprefix
} else {
#lset overlines $nextoveridx $nextprefix[lindex $overlines $nextoveridx]
#set inputchunks [linsert $inputchunks $nextoveridx $nextprefix]
ledit inputchunks $nextoveridx -1 $nextprefix
}
}
}
}
if {[llength $nextprefix_list]} {
#set inputchunks [linsert $inputchunks 0 $nextprefix]
@ -1669,13 +1676,17 @@ tcl::namespace::eval overtype {
}
}
}
lappend outputlines $rendered
#JULZ
#lappend outputlines $rendered
lappend outputlines $rendered\x1b\[m
#lappend outputlines [renderline -insert_mode 0 -transparent $opt_transparent $undertext $overtext]
} else {
#background block is wider than or equal to data for this line
#lappend outputlines [renderline -insert_mode 0 -startcolumn [expr {$left_exposed + 1}] -transparent $opt_transparent -exposed1 $opt_exposed1 -exposed2 $opt_exposed2 $undertext $overtext]
set rinfo [renderline -info 1 -insert_mode 0 -startcolumn [expr {$left_exposed + 1}] -transparent $opt_transparent -exposed1 $opt_exposed1 -exposed2 $opt_exposed2 $undertext $overtext]
lappend outputlines [tcl::dict::get $rinfo result]
#JULZ
#lappend outputlines [tcl::dict::get $rinfo result]
lappend outputlines [tcl::dict::get $rinfo result]\x1b\[m
}
set replay_codes_underlay [tcl::dict::get $rinfo replay_codes_underlay]
set replay_codes_overlay [tcl::dict::get $rinfo replay_codes_overlay]
@ -1787,6 +1798,9 @@ tcl::namespace::eval overtype {
set overflowlength [expr {$overtext_datalen - $renderwidth}]
if {$overflowlength > 0} {
#raw overtext wider than undertext column
#broken:
#todo - renderline -overflow is invalid.
# we need renderline to support -expand_left ??
set rinfo [renderline\
-info 1\
-insert_mode 0\
@ -1814,13 +1828,18 @@ tcl::namespace::eval overtype {
}
}
}
lappend outputlines $rendered
#JULZ
#lappend outputlines $rendered
lappend outputlines $rendered\x1b\[m
} else {
#padded overtext
#lappend outputlines [renderline -insert_mode 0 -transparent $opt_transparent -startcolumn [expr {$left_exposed + 1}] $undertext $overtext]
#Note - we still need overflow(exapnd_right) here - as although the overtext is short - it may oveflow due to the startoffset
set rinfo [renderline -info 1 -insert_mode 0 -transparent $opt_transparent -expand_right $opt_overflow -startcolumn [expr {$left_exposed + 1 + $startoffset}] $undertext $overtext]
lappend outputlines [tcl::dict::get $rinfo result]
#JULZ
#lappend outputlines [tcl::dict::get $rinfo result]
lappend outputlines [tcl::dict::get $rinfo result]\x1b\[m
}
set replay_codes [tcl::dict::get $rinfo replay_codes]
set replay_codes_underlay [tcl::dict::get $rinfo replay_codes_underlay]
@ -2014,7 +2033,8 @@ tcl::namespace::eval overtype {
# }
#}
}
lappend outputlines $rendered
#JULZ
lappend outputlines $rendered\x1b\[m
} else {
#padded overtext
#lappend outputlines [renderline -insert_mode 0 -transparent $opt_transparent -startcolumn [expr {$left_exposed + 1}] $undertext $overtext]
@ -2023,7 +2043,9 @@ tcl::namespace::eval overtype {
#puts stderr "--> [ansistring VIEW -lf 1 -nul 1 $rinfo] <--"
set overflow_right [tcl::dict::get $rinfo overflow_right]
set unapplied [tcl::dict::get $rinfo unapplied]
lappend outputlines [tcl::dict::get $rinfo result]
#JULZ
#lappend outputlines [tcl::dict::get $rinfo result]
lappend outputlines [tcl::dict::get $rinfo result]\x1b\[m
}
set replay_codes [tcl::dict::get $rinfo replay_codes]
set replay_codes_underlay [tcl::dict::get $rinfo replay_codes_underlay]
@ -2136,6 +2158,24 @@ tcl::namespace::eval overtype {
}]
}
proc stack_eq {a b} {
#single level list equality test to avoid generating internal string representations of the lists unnecessarily.
if {[llength $a] != [llength $b]} {
return 0
}
foreach code1 $a code2 $b {
if {$code1 ne $code2} {
return 0
}
}
return 1
}
#todo: tests
#set j [overtype::renderline -transparent " " -insert_mode 0 -expand_right 1 "[a+ red underline]xxx[a+ blue][a+ nounderline]" "[a green]J" ]yyy
# yyy should be blue with no underline - and the J should be green - and the x's should be red with underline and the J should overwrite the first x
#At the moment we return a reset at the end of the renderline result instead of the replay codes.
proc renderline {args} {
#todo - fix 'unapplied' mechanism.This is particularly inefficient for long lines, or data such as binarytext which is not line-based.
#All unapplied data is re-split/reprocessed repeatedly for each line! This is very wasteful and slow.
@ -2476,7 +2516,9 @@ tcl::namespace::eval overtype {
if {$maybemouse ne "<" && [tcl::string::index $code end] eq "m"} {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set u_codestack [list "\x1b\[m"]
#will normalize all resets to the same code - including 8bit reset.
#set u_codestack [list "\x1b\[m"]
set u_codestack [list $code]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set u_codestack [list $code]
} else {
@ -2557,6 +2599,17 @@ tcl::namespace::eval overtype {
}
}
#----------------------------------------
#set test_c [showlist $undercols]
##set test_s [showlist $understacks %ansiview]
#set sview [list]
#foreach us $understacks {
# lappend sview [ansistring VIEW $us]
#}
#set test_s [showlist $sview]
#puts stderr "undercols/stacks:\n[textblock::join -- $test_c " " $test_s]"
#----------------------------------------
if {$opt_width ne "\uFFEF"} {
set renderwidth $opt_width
} else {
@ -2567,7 +2620,10 @@ tcl::namespace::eval overtype {
#trailing codes in effect for underlay
if {[llength $u_codestack]} {
#set replay_codes_underlay [join $u_codestack ""]
set replay_codes_underlay [punk::ansi::codetype::sgr_merge_list {*}$u_codestack]
#set replay_codes_underlay [punk::ansi::codetype::sgr_merge_list {*}$u_codestack]
#u_codestack was built from codes split using split_codes_single
#- so should already be simplified to single codes with no multiple SGR params in one code
set replay_codes_underlay [punk::ansi::codetype::sgr_merge_singles $u_codestack]
} else {
set replay_codes_underlay ""
}
@ -2767,13 +2823,17 @@ tcl::namespace::eval overtype {
} else {
lappend overlay_grapheme_control_stacks $o_codestack
#there will always be an empty code at end due to foreach on 2 vars with odd-sized list ending with pt (overmap coming from perlish split)
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set o_codestack [list "\x1b\[m"] ;#reset better than empty list - fixes some ansi art issues
set code_endswith_m [expr {[tcl::string::index $code end] eq "m"}] ;#skip SGR regexp testing for cases that don't end with m - as they can't be SGR
if {$code_endswith_m && [punk::ansi::codetype::is_sgr_reset $code]} {
#reset better than empty list - fixes some ansi art issues
#set o_codestack [list "\x1b\[m"]
set o_codestack [list $code]
lappend overlay_grapheme_control_list [list sgr $code]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
} elseif {$code_endswith_m && [punk::ansi::codetype::has_sgr_leadingreset $code]} {
set o_codestack [list $code]
lappend overlay_grapheme_control_list [list sgr $code]
} elseif {[priv::is_sgr $code]} {
} elseif {$code_endswith_m && [priv::is_sgr $code]} {
#basic simplification first - remove straight dupes
set dup_posns [lsearch -all -exact $o_codestack $code] ;#must be -exact because of square-bracket glob chars
set o_codestack [lremove $o_codestack {*}$dup_posns]
@ -2827,7 +2887,12 @@ tcl::namespace::eval overtype {
lappend overstacks_gx $o_gxstack
#set replay_codes_overlay [join $o_codestack ""]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}$o_codestack]
if {[llength $o_codestack]} {
#set replay_codes_overlay [join $o_codestack ""]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_singles $o_codestack]
} else {
set replay_codes_overlay [list]
}
#if {[tcl::dict::exists $overstacks $max_overlay_grapheme_index]} {
# set replay_codes_overlay [join [tcl::dict::get $overstacks $max_overlay_grapheme_index] ""]
@ -2952,7 +3017,7 @@ tcl::namespace::eval overtype {
#specials - each shoud have it's own test of what to do if it happens after overflow_idx reached
switch -- $chtest {
"<lf>" {
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
if {$idx == 0} {
#puts "---a <lf> at col 1"
#linefeed at column 1
@ -3069,8 +3134,7 @@ tcl::namespace::eval overtype {
set next_gc [lindex $overlay_grapheme_control_list $gci+1] ;#next grapheme or control
lassign $next_gc next_type next_item
if {$autowrap_mode} {
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci-1]]
#set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci-1]]
#don't incr idx beyond the overflow_idx
#idx_over already incremented - decrement so current overlay grapheme stacks go to unapplied
incr idx_over -1
@ -3087,7 +3151,7 @@ tcl::namespace::eval overtype {
#no point throwing back to caller for each grapheme that is overflowing
#without this branch - renderline would be called with overtext reducing only by one grapheme per call
#processing a potentially long overtext each time (ie - very slow)
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#JMN4
}
@ -3427,7 +3491,7 @@ tcl::namespace::eval overtype {
switch -exact -- $code_end {
A {
#Row move - up
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#todo
lassign [split $param {;}] num modifierkey
if {$modifierkey ne ""} {
@ -3452,7 +3516,7 @@ tcl::namespace::eval overtype {
#CUD - Cursor Down
#Row move - down
lassign [split $param {;}] num modifierkey
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#move down
if {$modifierkey ne ""} {
puts stderr "modifierkey:$modifierkey"
@ -3503,7 +3567,7 @@ tcl::namespace::eval overtype {
incr cursor_column $num
} else {
if {$autowrap_mode} {
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#jmn
if {$idx == $overflow_idx} {
incr num
@ -3598,7 +3662,7 @@ tcl::namespace::eval overtype {
set cursor_column 1
set idx 0
} else {
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
incr cursor_column -$num
priv::render_to_unapplied $overlay_grapheme_control_list $gci
set instruction wrapmovebackward
@ -3626,7 +3690,9 @@ tcl::namespace::eval overtype {
set cursor_column 1
set cursor_row [expr {$cursor_row + $downmove}]
set idx [expr {$cursor_column -1}]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
#sgr_merge_list
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#sgr_merge_singles ??
incr idx_over
priv::render_to_unapplied $overlay_grapheme_control_list $gci
set instruction move
@ -3647,7 +3713,7 @@ tcl::namespace::eval overtype {
set cursor_row 1
}
set idx [expr {$cursor_column - 1}]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
incr idx_over
priv::render_to_unapplied $overlay_grapheme_control_list $gci
set instruction move
@ -3656,6 +3722,7 @@ tcl::namespace::eval overtype {
}
G {
#CHA - Cursor Horizontal Absolute (move to absolute column no)
#see also HPA - Horizontal Position Absolute (same functionality)
if {$param eq ""} {
set targetcol 1
} else {
@ -3680,6 +3747,29 @@ tcl::namespace::eval overtype {
set cursor_column $targetcol
#puts stderr "renderline absolute col move ESC G (TEST)"
}
` {
#https://vt100.net/docs/vt510-rm/HPA.html
#docs don't mention that it defaults to one if $parm omitted - but it seems to do in practice
if {$param eq ""} {
set targetcol 1
} else {
set targetcol $param
if {![string is integer -strict $targetcol]} {
puts stderr "renderline HPA (Horizontal Position Absolute) error. Unrecognised parameter '$param'"
}
set targetcol [expr {$param}]
set max [llength $outcols]
if {$overflow_idx == -1} {
incr max
}
if {$targetcol > $max} {
puts stderr "renderline HPA (Horizontal Position Absolute) error. Param '$param' > max: $max"
set targetcol $max
}
}
set idx [expr {($targetcol -1) + $opt_colstart -1}]
set cursor_column $targetcol
}
H - f {
#CSI n;m H - CUP - Cursor Position
@ -3727,7 +3817,7 @@ tcl::namespace::eval overtype {
set cursor_row $target_row
set cursor_column $target_column
set idx [expr {$cursor_column -1}]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
incr idx_over
priv::render_to_unapplied $overlay_grapheme_control_list $gci
set instruction move
@ -3758,7 +3848,7 @@ tcl::namespace::eval overtype {
set cursor_row 1
set cursor_column 1
set idx [expr {$cursor_column -1}]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
incr idx_over
if {[llength $outcols]} {
priv::render_erasechar 0 [llength $outcols]
@ -4000,7 +4090,8 @@ tcl::namespace::eval overtype {
}
}
#append cursor_saved_attributes [join $sgr_stack ""]
append cursor_saved_attributes [punk::ansi::codetype::sgr_merge_list {*}$sgr_stack]
#append cursor_saved_attributes [punk::ansi::codetype::sgr_merge_list {*}$sgr_stack]
append cursor_saved_attributes [punk::ansi::codetype::sgr_merge $sgr_stack]
#as there is apparently only one cursor storage element we don't need to throw back to the calling loop for a save.
@ -4024,7 +4115,7 @@ tcl::namespace::eval overtype {
# set replay_codes_overlay $cursor_saved_attributes ;#empty - or last save if it happend in this input chunk
#} else {
#jj
#set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
#set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay ""
#}
@ -4398,7 +4489,7 @@ tcl::namespace::eval overtype {
#vt102-docs: "Moves cursor up one line in same column. If cursor is at top margin, screen performs a scroll-down"
puts stderr "overtype::renderline ESC M not fully implemented"
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#move up
incr cursor_row -1
if {$cursor_row < 1} {
@ -4743,6 +4834,9 @@ tcl::namespace::eval overtype {
#puts stderr "first_tail_null_posn: $first_tail_null_posn"
#puts stderr "colview: [ansistring VIEW $outcols]"
#NOTE understacks has been updated with data from the overlay - so it should reflect the final state of the stacks for each grapheme in outcols
foreach ch $outcols {
#puts "---- [ansistring VIEW $ch]"
@ -4766,15 +4860,58 @@ tcl::namespace::eval overtype {
if {$i < [llength $understacks]} {
#set cstack [tcl::dict::get $understacks $i]
set cstack [lindex $understacks $i]
if {$cstack ne $prevstack} {
if {[llength $prevstack] && ![llength $cstack]} {
#This reset is important e.g testfile fruit.ans - we get overhang on rhs without it. But why is cstack empty?
append sgrleader \033\[m
#use stack_eq for depth 1 comparison without generating string rep.
if {![stack_eq $cstack $prevstack]} {
#possible SGR attribute change.
if {[llength $prevstack]} {
if {![llength $cstack]} {
#why is cstack empty?
#a) no ansi in underlay and we are at a position 2 after an overlay insertion.
# (position 1 after overlay insertion should already have had a reset inserted)
#b) no ansi in overlay and we are at an overlay insertion point.
#--------------
#review
#todo? consider testing next-char's understack when applying each overlay char in the main loop.
#if empty or has no leading reset - we need to add a leading reset at that point.
#--------------
#--------
#following statement is FALSE - (historical info). Doesn't seem to apply.
#This reset is important e.g testfile fruit.ans - we get overhang on rhs without it.
#append sgrleader \033\[m
#--------
##test
#set view_prev ""
#foreach ps $prevstack {
# append view_prev [ansistring VIEW -lf 1 -vt 1 -nul 1 $ps]
#}
#puts stderr "col $i, ch: $ch - cstack empty vs prevstack $view_prev"
} else {
#without this we get extra redundant codes in some places.
#e.g a continuous string of underlay that originally had \x1b\[31m red text,
#but then when an overlay char is inserted near the start, the following underlay char (insertion index +1) codestack had a reset added.
#All subsequent underlay chars in the same run of plaintext don't have the reset and so appear 'different' but are actually part of the same run.
#check if actually different. ie if current stack actually changes anything from previous stack when merged together.
set prevmerge [punk::ansi::codetype::sgr_merge $prevstack]
set currmerge [punk::ansi::codetype::sgr_merge $cstack]
set together [punk::ansi::codetype::sgr_merge [list $prevmerge $currmerge]]
if {$together ne $prevmerge} {
#stacks are different enough that we need to output something
#if {{[punk::ansi::codetype::has_sgr_leading_reset $currmerge]}} {
#}
append sgrleader $currmerge
}
}
} else {
append sgrleader [punk::ansi::codetype::sgr_merge_list {*}$cstack]
if {[llength $cstack]} {
append sgrleader [punk::ansi::codetype::sgr_merge $cstack]
}
}
set prevstack $cstack
}
set prevstack $cstack
} else {
set prevstack [list]
}
@ -4797,7 +4934,8 @@ tcl::namespace::eval overtype {
#if {[llength $prevstack] && ![llength $cstack]} {
# append sgrleader \033\[m
#}
append sgrleader [punk::ansi::codetype::sgr_merge_list {*}$cstack]
#append sgrleader [punk::ansi::codetype::sgr_merge_list {*}$cstack]
append sgrleader [punk::ansi::codetype::sgr_merge $cstack]
append overflow_right $sgrleader
append overflow_right $ch
} else {
@ -4853,14 +4991,50 @@ tcl::namespace::eval overtype {
set replay_codes ""
if {[llength $understacks] > 0} {
if {$overflow_idx == -1} {
#set tail_idx [tcl::dict::size $understacks]
set tail_idx [llength $understacks]
} else {
set tail_idx [llength $undercols]
}
if {$tail_idx-1 < [llength $understacks]} {
if {$tail_idx == [llength $undercols]} {
#we got to the end of the original underlay
#- so we want the full stack at the end of the original underlay ie including trailing codes which are not associated with any grapheme in the underlay
#but would be in effect for any text after the underlay.
#---------------------
#REVIEW - determine if last col was overwritten by overlay?
#how best to determine if last underlay column was overwritten by overlay?
#we could track in the main loop whether each underlay column was overwritten by overlay
#This seems like the best mechanism, because the overlay ANSI can include movement codes, so the underlay can be overwritten in any order.
#We should consider that just because the last grapheme was overwritten, that doesn't necessarily mean we should disregard the trailing codes
#perhaps trailing underlay codes are never overwritten unless the overlay extends beyond the end of the underlay - in which case we can just check if overlay extends beyond end of underlay to determine whether to include trailing underlay codes in replay or not.
#if overlay extends beyond end of underlay - we use the overlay stack at the end of the underlay as the replay codes, which won't include any trailing underlay codes.
#---------------------
if {[lindex $undermap end] eq ""} {
#there were trailing codes in the underlay with no grapheme - we want to include those in the replay as they would affect any text after the underlay
#we need to backtrack from the end of the underlay to find the last grapheme with codes, and merge those codes with any trailing codes in the underlay with no grapheme
set tailcodes [list] ;#build in reverse order.
foreach {pt code} [lreverse $undermap] {
if {$pt ne ""} {
break
}
lappend tailcodes $code
}
set tailcodes [lreverse $tailcodes]
#set tailcodes [lindex $undermap end-1]
set laststack [lindex $understacks $tail_idx-1]
lappend laststack {*}$tailcodes
set replay_codes [punk::ansi::codetype::sgr_merge $laststack] ;#stack at end of underlay including trailing codes
} else {
#last part of underlay was plain text with no trailing codes - we can just use the stack at the last grapheme of the underlay
set replay_codes [punk::ansi::codetype::sgr_merge [lindex $understacks $tail_idx-1]] ;#stack at end of underlay
}
} elseif {$tail_idx-1 < [llength $understacks]} {
#set replay_codes [join [lindex $understacks $tail_idx-1] ""] ;#tail replay codes
set replay_codes [punk::ansi::codetype::sgr_merge_list {*}[lindex $understacks $tail_idx-1]] ;#tail replay codes
#set replay_codes [punk::ansi::codetype::sgr_merge_list {*}[lindex $understacks $tail_idx-1]] ;#tail replay codes
set replay_codes [punk::ansi::codetype::sgr_merge [lindex $understacks $tail_idx-1]] ;#tail replay codes
}
if {$tail_idx-1 < [llength $understacks_gx]} {
set gx0 [lindex $understacks_gx $tail_idx-1]
@ -4876,10 +5050,33 @@ tcl::namespace::eval overtype {
#pdict $understacks
if {[punk::ansi::ta::detect_sgr $outstring]} {
append outstring [punk::ansi::a] ;#without this - we would get for example, trailing backgrounds after rightmost column
#JULZ
#The caller is responsible for adding a reset at the end of returned lines depending on how they want to use it - so we don't add one here.
#<deprecated>
#append outstring [punk::ansi::a] ;#without this - we would get for example, trailing backgrounds after rightmost column
#</deprecated>
#we only want to append the replay codes if they are different to those already in effect at the end of the rendered line.
if {$overflow_idx == -1} {
set tail_idx [llength $understacks]
} else {
set tail_idx [llength $undercols]
}
set laststack [lindex $understacks $tail_idx-1]
set laststackmerge [punk::ansi::codetype::sgr_merge $laststack]
if {$replay_codes ne $laststackmerge} {
append outstring $replay_codes
}
#review
#close off any open gx?
#probably should - and overflow_right reopen?
#probably not, this is akin to adding a reset to close off open SGR codes, which we specifically don't do.
#caller will need to close off any open gx at the end of the line if they want to, and provide appropriate replay codes for the next line if they want to maintain gx state across lines.
#we just need to make sure we provide all necessary info in the result dictionary.
#todo - tests and examples.
#and overflow_right reopen?
}
if {$opt_returnextra} {
@ -4902,29 +5099,29 @@ tcl::namespace::eval overtype {
set result [tcl::dict::create\
result $outstring\
visualwidth [punk::ansi::printing_length $outstring]\
instruction $instruction\
stringlen [string length $outstring]\
overflow_right_column $overflow_right_column\
overflow_right $overflow_right\
unapplied $unapplied\
unapplied_list $unapplied_list\
unapplied_ansisplit $unapplied_ansisplit\
insert_mode $insert_mode\
autowrap_mode $autowrap_mode\
crm_mode $crm_mode\
reverse_mode $reverse_mode\
insert_lines_above $insert_lines_above\
insert_lines_below $insert_lines_below\
cursor_saved_position $cursor_saved_position\
visualwidth [punk::ansi::printing_length $outstring]\
instruction $instruction\
stringlen [string length $outstring]\
overflow_right_column $overflow_right_column\
overflow_right $overflow_right\
unapplied $unapplied\
unapplied_list $unapplied_list\
unapplied_ansisplit $unapplied_ansisplit\
insert_mode $insert_mode\
autowrap_mode $autowrap_mode\
crm_mode $crm_mode\
reverse_mode $reverse_mode\
insert_lines_above $insert_lines_above\
insert_lines_below $insert_lines_below\
cursor_saved_position $cursor_saved_position\
cursor_saved_attributes $cursor_saved_attributes\
cursor_column $cursor_column\
cursor_row $cursor_row\
expand_right $opt_expand_right\
replay_codes $replay_codes\
replay_codes_underlay $replay_codes_underlay\
replay_codes_overlay $replay_codes_overlay\
pm_list $pm_list\
cursor_column $cursor_column\
cursor_row $cursor_row\
expand_right $opt_expand_right\
replay_codes $replay_codes\
replay_codes_underlay $replay_codes_underlay\
replay_codes_overlay $replay_codes_overlay\
pm_list $pm_list\
]
if {$opt_returnextra == 1} {
#puts stderr "renderline: $result"
@ -5073,6 +5270,11 @@ tcl::namespace::eval overtype::priv {
#caching the answer saves some regex expense - possibly a few uS to lookup vs under 1uS
#todo - test if still worthwhile after a large cache is built up. (limit cache size?)
proc is_sgr {code} {
set code_endswith_m [expr {[tcl::string::index $code end] eq "m"}] ;#skip SGR regexp testing for cases that don't end with m - as they can't be SGR
if {!$code_endswith_m} {
#don't even cache.
return 0
}
variable cache_is_sgr
if {[tcl::dict::exists $cache_is_sgr $code]} {
return [tcl::dict::get $cache_is_sgr $code]
@ -5081,6 +5283,7 @@ tcl::namespace::eval overtype::priv {
tcl::dict::set cache_is_sgr $code $answer
return $answer
}
proc render_to_unapplied {overlay_grapheme_control_list gci} {
upvar idx_over idx_over
@ -5104,7 +5307,8 @@ tcl::namespace::eval overtype::priv {
set unapplied_ansisplit [list ""]
#append unapplied [join [lindex $overstacks $idx_over] ""]
#append unapplied [punk::ansi::codetype::sgr_merge_list {*}[lindex $overstacks $idx_over]]
set sgr_merged [punk::ansi::codetype::sgr_merge_list {*}[lindex $og_stacks $gci]]
#set sgr_merged [punk::ansi::codetype::sgr_merge_list {*}[lindex $og_stacks $gci]]
set sgr_merged [punk::ansi::codetype::sgr_merge [lindex $og_stacks $gci]]
if {$sgr_merged ne ""} {
lappend unapplied_list $sgr_merged
lappend unapplied_ansisplit $sgr_merged ""
@ -5167,7 +5371,8 @@ tcl::namespace::eval overtype::priv {
set unapplied_list [list]
set unapplied_ansisplit [list ""] ;#remove empty entry at end if nothing added
set sgr_merged [punk::ansi::codetype::sgr_merge_list {*}[lindex $og_stacks $gci]]
#set sgr_merged [punk::ansi::codetype::sgr_merge_list {*}[lindex $og_stacks $gci]]
set sgr_merged [punk::ansi::codetype::sgr_merge [lindex $og_stacks $gci]]
if {$sgr_merged ne ""} {
lappend unapplied_list $sgr_merged
lappend unapplied_ansisplit $sgr_merged ""
@ -5217,9 +5422,13 @@ tcl::namespace::eval overtype::priv {
upvar understacks_gx gxstacks
set nxt [llength $o]
if {$i < $nxt} {
set o [lreplace $o $i $i]
set ustacks [lreplace $ustacks $i $i]
set gxstacks [lreplace $gxstacks $i $i]
#set o [lreplace $o $i $i]
ledit o $i $i
#set ustacks [lreplace $ustacks $i $i]
ledit ustacks $i $i
#review - do we need to ensure that stack at new $i has a reset code at the start?
#set gxstacks [lreplace $gxstacks $i $i]
ledit gxstacks $i $i
} elseif {$i == 0 || $i == $nxt} {
#nothing to do
} else {
@ -5329,6 +5538,27 @@ tcl::namespace::eval overtype::priv {
}
if {$i < [llength $ustacks]} {
lset ustacks $i $sgrstack
#check if next ustacks entry has a reset.
#It will need one if it doesn't already have one because our inserted char should not affect the pre-existing ansi state of the underlay.
#we have just replaced an entry into the ustacks at position i but we are still at the same position - so the next entry is still at position i+1
if {[llength $sgrstack] && $i+1 < [llength $ustacks]} {
set next_ustack [lindex $ustacks $i+1]
#could be a reset or just empty - either way we need to add a reset if it's not already there
#(empty if underlay had no ansi)
#temporarily emit something to stderr
if {![llength $next_ustack]} {
#puts -nonewline stderr " next_ustack (empty) at position [expr {$i+1}] after replacing position $i with '$c' and sgrstack '[join $sgrstack ""]'\n"
lset ustacks $i+1 [list "\x1b\[m"]
} else {
#review - next_ustack is a list - has_sgr_leadingreset will not work as expected if called on whole next_ustack as a list.
#As the stack will need merging anyway - we can just prepend a reset without checking.
#REVIEW.
#puts -nonewline stderr "check next_ustack '$next_ustack' for reset at position [expr {$i+1}] after replacing position $i with '$c' and sgrstack '[join $sgrstack ""]'\n"
#set next_ustack [linsert $next_ustack 0 [a+ reset]]
ledit next_ustack -1 -1 "\x1b\[m"
lset ustacks $i+1 $next_ustack
}
}
lset gxstacks $i $gx0stack
} else {
lappend ustacks $sgrstack
@ -5339,7 +5569,8 @@ tcl::namespace::eval overtype::priv {
if {$i < $nxt} {
#set o [linsert $o $i $c]
#JMN insert via ledit
ledit o $i $i-1 $c
#ledit o $i $i-1 $c
ledit o $i -1 $c
} else {
lappend o $c
}
@ -5347,8 +5578,10 @@ tcl::namespace::eval overtype::priv {
#set ustacks [linsert $ustacks $i $sgrstack]
#set gxstacks [linsert $gxstacks $i $gx0stack]
#insert via ledit
ledit ustacks $i $i-1 $sgrstack
ledit gxstacks $i $i-1 $gx0stack
#ledit ustacks $i $i-1 $sgrstack
ledit ustacks $i -1 $sgrstack
#ledit gxstacks $i $i-1 $gx0stack
ledit gxstacks $i -1 $gx0stack
} else {
lappend ustacks $sgrstack
lappend gxstacks $gx0stack

256
src/modules/punk-0.1.tm

@ -1743,7 +1743,7 @@ namespace eval punk {
append script \n {set assigned [ansistring length $leveldata]}
set level_script_complete 1
}
%str {
%str - %string {
set active_key_type "string"
if {$get_not} {
error "!%str - not string-get is not supported"
@ -1752,6 +1752,9 @@ namespace eval punk {
append script \n {# set active_key_type "" index_operation: string-get}
append script \n {set assigned $leveldata}
set level_script_complete 1
#todo - %lpad- %lpadstr- %join- etc as in punk::lib::showdict
#review - merge code shared with showdict for these operations
}
%sp {
#experimental
@ -1822,6 +1825,8 @@ namespace eval punk {
set level_script_complete 1
}
%ansiview {
#review - implemented differently in showdict.
#(showdict uses ansistring VIEW -lf 1 <str>)
set active_key_type "string"
if {$get_not} {
error "!%# not string-ansiview is not supported"
@ -2446,7 +2451,41 @@ namespace eval punk {
set index <idx>
}]
}
%split-* {
#split on one or more chars - review
#set hidekey 1
#lassign [split $key -] _ splitchars
#set thisval [split $dval $splitchars]
set active_key_type "string"
set splitchars [string range $index 7 end]
append script \n [string map [list <splitchars> $splitchars] {
# set active_key_type "string" index_operation: split-string
#e.g supports %split-"\\n"= "l1\n\nl3" -> {l1 "" l3}
set splitchars "<splitchars>"
set assigned [split $leveldata $splitchars]
}]
set level_script_complete 1
#todo %splitat- %splitn- ??
}
%lpad-* {
#moved from punk::lib::showdict patterns.
#set hidekey 1
#lassign [split $key -] _ extra
#set width [expr {[textblock::width $dval] + $extra}]
#set thisval [textblock::pad $dval -which left -width $width]
set active_key_type "string"
set extra [string range $index 6 end]
append script \n [string map [list <extra> $extra] {
# set active_key_type "string" index_operation: lpad-string
set extra "<extra>"
set width [expr {[textblock::width $leveldata] + $extra}]
set assigned [textblock::pad $leveldata -which left -width $width]
}]
set level_script_complete 1
}
%* {
#see above re %lpad- etc and synchronizing with showdict
set active_key_type "string"
set do_bounds_check 0
set index [string range $index 1 end]
@ -2827,11 +2866,21 @@ namespace eval punk {
} else {
if {$is_range} {
lappend INDEX_OPERATIONS list-range
#todo - if we know it's a contiguous range, we could use lrange here instead of lindex
#we would also need to detect if it's a reverse range such as @5..1 and handle that correctly
#- lrange doesn't support reverse ranges, but we could resolve the indexset to a list of indices
#and then use lindex with that list of indices to get the correct result.
#we don't always know at this point if the range is in reverse or not because we don't know the size of the list until
#runtime - so we will handle both cases in the same way for now.
#e.g for index 5..end-6 - this could be forward or reverse depending on the length of the list.
set assign_script {
set assigned [lmap i [punk::lib::indexset_resolve [llength $leveldata] <idx>] {lindex $leveldata $i}]
}
} else {
lappend INDEX_OPERATIONS listindex
}
set assign_script {
set assigned [lmap i [punk::lib::indexset_resolve [llength $leveldata] <idx>] {lindex $leveldata $i}]
set assign_script {
set assigned [lindex $leveldata [punk::lib::indexset_resolve [llength $leveldata] <idx>]]
}
}
}
@ -2881,6 +2930,8 @@ namespace eval punk {
}
set script [string map [list <idx> $index] $script]
} elseif {[string first "end" $index] >=0} {
#review - obsoleted by indexset syntax. prune branch?
puts stderr "index with end detected - review if this branch still reachable - prune? $index"
if {[regexp {^end([-+]{1,2}[0-9]+)$} $index _match endspec]} {
if {$get_not} {
@ -2923,6 +2974,8 @@ namespace eval punk {
}
} elseif {[regexp {^([0-9]+|end|end[-+]{1,2}[0-9]+)-([0-9]+|end|end[-+]{1,2}([0-9]+))$} $index _ start end]} {
#review - obsoleted by indexset syntax. prune branch?
puts stderr "index with range and end detected - review if this branch still reachable - prune? $index"
if {$get_not} {
lappend INDEX_OPERATIONS list-range-not
set assign_script [string map [list <s> $start <e> $end ] {
@ -3012,6 +3065,10 @@ namespace eval punk {
error $listmsg "destructure $selector" [list pipesyntax destructure selector $selector]
}
} elseif {[string first - $index] > 0} {
puts stderr "index with - detected - review if this branch still reachable - prune? $index"
#review - we changed to detect indexset above.
#syntax @m-n should be deprecated in favour of @m..n
#todo - check if this branch still reachable - prune?
#e.g @1-3 gets here
#JMN
if {$get_not} {
@ -3089,19 +3146,61 @@ namespace eval punk {
}
}
} elseif {$active_key_type eq "string"} {
if {[string match *-* $index]} {
lappend INDEX_OPERATIONS string-range
set re_idxdashidx {^([-+]{0,1}\d+|end[-+]{1}\d+|end)-([-+]{0,1}\d+|end[-+]{1}\d+|end)$}
#todo - support more complex indices: 0-end-1 etc
#changed to indexset notation m..n allowing eg 2..end-1 etc.
#if {[string match *-* $index]} {}
if {[punk::lib::is_indexset $index]} {
#review - we are assuming a single element indexset here - ie no comma separated sets.
#todo - support $get_not
#todo - consider bounds_check for string indices.
# - Tcl doesn't do bounds checking for string index, but we need to consider in the context of pattern-matching
# whether we want to support syntaxes for with and without bounds checking on string indices.
set is_range [expr {[string first ".." $index] >= 0}]
if {$is_range} {
lappend INDEX_OPERATIONS string-range
#review - not efficient for contiguous monotonically increasing ranges
#because we are retrievinng each character individually and concatenating
#- but it is more flexible because it also supports reverse ranges and could support non-contiguous ranges such as @0,2,4..6
set assign_script {
set assigned [join [lmap i [punk::lib::indexset_resolve [string length $leveldata] <idx>] {string index $leveldata $i}] ""]
}
} else {
lappend INDEX_OPERATIONS string-index
set assign_script {
set assigned [string index $leveldata [punk::lib::indexset_resolve [string length $leveldata] <idx>]]
}
}
#set assign_script {
# set assigned [lmap i [punk::lib::indexset_resolve [llength $leveldata] <idx>] {lindex $leveldata $i}]
#}
lassign [split $index -] a b
#todo - consider where/if we can support 'ansistring INDEX' for ANSI strings.
#if so - it shouldn't overload the % operator we currently use for string access.
append script \n [tstr -return string -allowcommands {
# set active_key_type "string"
set assigned [string range $leveldata ${$a} ${$b}]
if {$leveldata eq ""} {
set assigned ""
} else {
${$assign_script}
}
}]
set script [string map [list <idx> $index] $script]
#set re_idxdashidx {^([-+]{0,1}\d+|end[-+]{1}\d+|end)-([-+]{0,1}\d+|end[-+]{1}\d+|end)$}
##todo - support more complex indices: 0-end-1 etc
#lassign [split $index -] a b
#append script \n [tstr -return string -allowcommands {
# # set active_key_type "string"
# set assigned [string range $leveldata ${$a} ${$b}]
#}]
} else {
if {$index eq "*"} {
#equivalent to indexset ".."
lappend INDEX_OPERATIONS string-all
append script \n [tstr -return string -allowcommands {
# set active_key_type "string"
@ -4294,6 +4393,7 @@ namespace eval punk {
}
#todo check end-x bounds?
}
#todo - change to ledit
if {$isint} {
append script [string map [list <listvar> $listvar <idx> $index <exp> $exp <val> $data] {
set <listvar> [linsert [lindex [list $<listvar> [unset <listvar>]] 0] <idx> <exp><val>]
@ -4350,7 +4450,8 @@ namespace eval punk {
#last element has no -, so we are inserting at the final position - not replacing
append script [string map [list <listvar> $listvar <containerkeys> [lrange $parts 0 end-1] <lastkey> $last <exp> $exp <val> $data] {
set target [lindex $<listvar> <containerkeys>]
set target [linsert $target <lastkey> <exp><val>]
#set target [linsert $target <lastkey> <exp><val>]
ledit target <lastkey> -1 <exp><val>
lset <listvar> <containerkeys> $target
}]
}
@ -8564,7 +8665,7 @@ namespace eval punk {
lappend chunks [list stdout $text]
}
console - term - terminal {
set term_env_vars {TERM TERM_PROGRAM TERM_PROGRAM_VERSION}
set term_env_vars {TERM TERM_PROGRAM TERM_PROGRAM_VERSION COLORTERM}
set term_dict [dict create]
foreach e $term_env_vars {
if {[info exists ::env($e)]} {
@ -8577,6 +8678,7 @@ namespace eval punk {
append text [punk::lib::showdict $term_dict] \n
lappend chunks [list stdout $text]
set text ""
set indent [string repeat " " [string length "WARNING: "]]
if {[catch {package require punk::console} result]} {
set text "Unable to load punk::console package - cannot test\n$result"
@ -8591,7 +8693,6 @@ namespace eval punk {
}
lappend chunks [list stdout $text]
set indent [string repeat " " [string length "WARNING: "]]
lappend cstring_tests [dict create\
type "PM "\
msg "UN"\
@ -8686,10 +8787,45 @@ namespace eval punk {
}
}
}
set posn [punk::console::get_cursor_pos] ;#warmup call - and test if works
if {$posn eq ""} {
append warningblock \n "WARNING: terminal doesn't respond to cursor position query - may cause display bugs in some cases."
} else {
set timeresult [timerate {set cpos [punk::console::get_cursor_pos]}]
lassign [split $cpos {;}] row col
if {![string is integer -strict $row] || ![string is integer -strict $col]} {
append warningblock \n "WARNING: terminal returns non-integer values for cursor position query - may cause display bugs in some cases. got row:'$row' col:'$col'"
} else {
set micros [lindex $timeresult 0]
if {$micros > 2000} {
append warningblock \n "WARNING: terminal cursor position query is very slow ($micros microseconds - expect < 2000us )"
append warningblock \n $indent "- may cause display lag/bugs in some cases."
} else {
if {$micros > 1000} {
set text "\n[a+ yellow]Terminal cursor position query test passed."
append text \n $indent "Response time: ${micros} microseconds (OK, good would be <= 1000us).[a]"
} else {
set text "[a+ green]Terminal cursor position query test passed."
append text \n $indent "Response time: ${micros} microseconds (GOOD).[a]"
}
lappend chunks [list stdout $text]
}
}
}
if {![string length $warningblock]} {
set text "[a+ green]No terminal warnings[a]\n"
lappend chunks [list stdout $text]
} else {
set mode [punk::console::mode]
if {$mode eq "line"} {
append warningblock \n "Terminal appears to be in line mode. Consider switching to raw mode and re-testing (command: punk::console::mode raw)."
}
}
puts stdout [punk::ansi::move_back 200] ;#hack for some horizontal position bugs where the above tests can leave the cursor in the wrong place for the next output.
#200 is arbitrary large number to move back enough to get to start of line.
}
}
topics - help {
@ -8815,10 +8951,11 @@ namespace eval punk {
#interp alias {} c {} clear ;#external executable 'clear' may not always be available
#todo - review
interp alias {} clear {} ::punk::reset
interp alias {} c {} ::punk::reset
#interp alias {} clear {} ::punk::reset
#interp alias {} c {} ::punk::reset
interp alias {} reset {} ::punk::reset
proc reset {} {
if {[llength [info commands ::punk::repl::reset_terminal]]} {
#punk::repl::reset_terminal notifies prompt system of reset
@ -8828,6 +8965,91 @@ namespace eval punk {
}
}
namespace eval argdoc {
punk::args::define {
@id -id ::punk::ansi8
@cmd -name punk::ansi8\
-summary\
"Tell terminal to enable 8-bit ANSI codes."\
-help\
"Enable 8-bit ANSI codes in the terminal.
May not be supported by all terminals.
Some terminals may already have 8-bit ANSI enabled, but some may require an explicit command to enable it.
7-bit ANSI codes are generally preferred - and will still work on terminals with 8-bit ANSI support.
(This is nothing to do with 8-bit colors - it is about the underlying bytes used for ANSI control sequences).
The ANSI sequence sent to the terminal to enable 8-bit codes is: ESC <sp> 7
To disable 8-bit ANSI support - a reset of the terminal may be required.
"
@opts
@values -min 0 -max 0
}
}
proc ansi8 {} {
punk::console::S8C1R
}
namespace eval argdoc {
punk::args::define {
@id -id ::punk::clear
@cmd -name punk::clear\
-summary\
"Clear the terminal screen (and scrollback buffer by default)."\
-help\
"Clear the terminal screen.
By default this will also clear scrollback if supported by the terminal.
With -x option it will preserve scrollback but clear the screen.
"
@opts
-x -optional 1 -type none -mash 1 -help\
"Preserve scrollback (if supported by terminal) but clear screen."
-s -optional 1 -type none -mash 1 -help\
"Stay at the current cursor position instead of moving to top-left after clearing."
@values -min 0 -max 0
}
}
proc clear {args} {
set argd [punk::args::parse $args withid ::punk::clear]
lassign [dict values $argd] leaders opts values received
set opt_x [dict exists $received -x]
set opt_s [dict exists $received -s]
# -x preserves scrollback but clears screen
if {$opt_s} {
#set pre_move_cmd [punk::ansi::move_up 1]
#review - terminal support for save/restore.
#we can just move up one line before clearing to preserve the line we're on,
#but this won't work if we're already at the last line.
#save/restore would be better if widely supported.
#review - get_size already calls get_cursor pos - maybe we can optimize by not calling get_cursor_pos separately?
#review - consider turning off cursor updating while doing this to avoid flicker?
set cpos [punk::console::get_cursor_pos]
set row [lindex $cpos 0]
set size [punk::console::get_size]
set lastrow [dict get $size rows]
if {$row >= $lastrow} {
set pre_move_cmd [punk::ansi::cursor_save_dec]
} else {
set pre_move_cmd [punk::ansi::move_up 1][punk::ansi::cursor_save_dec]
}
set move_cmd [punk::ansi::cursor_restore_dec]
#set pre_move_cmd [punk::ansi::move_up 1]
#set move_cmd ""
} else {
set pre_move_cmd ""
set move_cmd [punk::ansi::move 1 1]
}
if {$opt_x} {
puts -nonewline stdout $pre_move_cmd[punk::ansi::clear]$move_cmd
} else {
puts -nonewline stdout $pre_move_cmd[punk::ansi::clear_all]$move_cmd
}
}
#c aliased to clear -xs
#cc aliases to clear -x
#fileutil::cat except with checking for windows illegal path names (when on windows platform)

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

@ -125,6 +125,10 @@ tcl::namespace::eval punk::aliascore {
grepstr ::punk::ansi::grepstr\
colour ::punk::console::colour\
color ::punk::console::colour\
ansi8 ::punk::ansi8\
clear ::punk::clear\
c {::punk::clear -xs}\
cc {::punk::clear -x}\
ansi ::punk::console::ansi\
a? ::punk::console::code_a?\
A? {::punk::console::code_a? forcecolor}\

1542
src/modules/punk/ansi-999999.0a1.0.tm

File diff suppressed because it is too large Load Diff

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

@ -103,7 +103,7 @@ tcl::namespace::eval ::punk::ansi::colourmap {
name -type string|stringstartswith(#)
}]
proc get_rgb_using_tk {name} {
package require tk
package require Tk ;#package require tk (lowercase) doesn't always work
#assuming 'winfo depth .' is always 32 ?
set RGB [winfo rgb . $name]
set rgb [lmap n $RGB {expr {$n / 256}}]

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

@ -1250,6 +1250,11 @@ tcl::namespace::eval punk::args {
}
set optionspecs [list]
#REVIEW - whilst this is only done once for each command definition, the -help section processing is sometimes expensive,
#and isn't required for parsing of arguments, so it unnecessarily slows first use of a command that uses punk::args and is heavily documented,
#especially if it has tcl syntax highlighted examples.
#- ideally we would delay expansion of -help sections until needed for display,
#and use a different cache key for the parsing vs display versions of the resolved definition.
foreach block $normargs {
if {[string first \$\{ $block] >= 0} {
if {$defspace ne ""} {
@ -2550,7 +2555,7 @@ tcl::namespace::eval punk::args {
tcl::dict::set spec_merged -typesynopsis $specval
}
-parsekey - -group {
tcl::dict::set spec_merged -typesynopsis $specval
tcl::dict::set spec_merged $spec $specval
}
-mash {
#allow when any alt in argname is a single letter flag such s -a or -Z
@ -8535,7 +8540,7 @@ tcl::namespace::eval punk::args {
}
#todo - move block below up here.
if {!$all_mashable} {
puts stderr "Debug: flagsupplied '$flagsupplied' not a valid flagname and not a valid mash of flags - treating as value"
#puts stderr "Debug: flagsupplied '$flagsupplied' not a valid flagname and not a valid mash of flags - treating as value"
#- probably isn't a flag at all - could be a value
#treat as value
set optionset ""
@ -8668,9 +8673,8 @@ tcl::namespace::eval punk::args {
#tcl::dict::set opts $flag_ident $tdflt
if {$flag_ident_is_parsekey} {
#(shimmer - but required for ordering correctness during override)
puts stderr "Debug: flag '$mashflagname' in mash '$flagsupplied' flag_ident '$flag_ident' is the same as parsekey '$api_opt' tdflt: $tdflt - using lappend to ensure it ends up after any previous flag in the mash that had the same parsekey"
#puts stderr "Debug: flag '$mashflagname' in mash '$flagsupplied' flag_ident '$flag_ident' is the same as parsekey '$api_opt' tdflt: $tdflt - using lappend to ensure it ends up after any previous flag in the mash that had the same parsekey"
lappend opts $flag_ident $tdflt
puts stderr "opts after lappend: $opts"
} else {
tcl::dict::set opts $flag_ident $tdflt
}
@ -10241,6 +10245,128 @@ tcl::namespace::eval punk::args {
}
}
proc _synopsis_form_arg_display {formdict argname} {
#non-colour SGR such as bold/italic/strike - so we don't need to worry about NOCOLOR settings
set I "\x1b\[3m" ;#[punk::ansi::a+ italic]
set NI "\x1b\[23m" ;# [punk::ansi::a+ noitalic]
#for inner question marks marking optional type
set IS "\x1b\[3\;9m" ;#[punk::ansi::a+ italic strike]
set NIS "\x1b\[23\;29m" ;#[punk::ansi::a+ noitalic nostrike]
set RST "\x1b\[m" ;#[punk::ansi::a]
set arginfo [dict get $formdict ARG_INFO $argname]
set typelist [dict get $arginfo -type]
set ts [Dict_getdef $arginfo -typesynopsis ""]
set n [expr {[llength $typelist]-1}]
set name_tail [lrange $argname end-$n end];#if there are enough tail words in the argname to match -types
set clause ""
if {$ts ne ""} {
set tp_displaylist $ts
} else {
set tp_displaylist [lrepeat [llength $typelist] ""]
}
foreach typespec $typelist td $tp_displaylist elementname $name_tail {
#elementname will commonly be empty
if {[string match {\?*\?} $typespec]} {
set tp [string range $typespec 1 end-1]
set member_optional 1
} else {
set tp $typespec
set member_optional 0
}
if {$td ne ""} {
set c $td
} else {
#handle alternate-types e.g literal(text)|literal(binary)
set alternates [list]
set type_alternatives [_split_type_expression $tp]
foreach tp_alternative $type_alternatives {
set tp_alternative_word1 [lindex $tp_alternative 0]
set match [lindex $tp_alternative 1]
switch -exact -- $tp_alternative_word1 {
literal {
lappend alternates [list $match]
}
literalprefix {
#todo - trie styling on prefix calc
lappend alternates [list $match]
}
stringstartswith {
lappend alternates [list $match*]
}
stringendswith {
lappend alternates [list *$match]
}
default {
#we'll only take display hints from the name itself if there was no defined typesynopsis element for this position in the type,
#and if the type-alternatives don't specify a literal or string match that we can use for display
#and if there are enough tail words in the argname to match the position in the type list
#empty strings can be put in -typesynopsis positions to only override the type information for certain elements of the clause
#- e.g for a type list of {string int} we could specify a typesynopsis of {"" "count"} to get display of "FILENAME count" for an argname of "file FILENAME FILECOUNT"
if {[llength $name_tail] >= [llength $typelist]} {
#important to list protect $elementname e.g look at ::apply
#The name may contain spaces e.g "{args body ?namespace?}"
#This must not be split into multiple words - it is a single element name that happens to contain spaces.
lappend alternates $I[list $elementname]$NI
} else {
lappend alternates $I<$tp_alternative>$NI
}
}
}
}
set alternates [punk::args::lib::lunique $alternates]
set c [join $alternates |]
}
if {$member_optional} {
#append clause " " "(?$c?)"
append clause " " "\[$c\]"
} else {
append clause " " $c
}
}
set clause [string trimleft $clause]
#set ARGD [dict create argname $argname class leader]
if {[dict get $arginfo -optional] || [dict exists $arginfo -default]} {
if {[dict get $arginfo -multiple]} {
#set display "?$I$argname$NI?..."
set display "\[$clause\]..."
} else {
set display "\[$clause\]"
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "?[lindex [dict get $arginfo -choices] 0]?"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display "?$argname?"
#} else {
# set display "?$I$argname$NI?"
#}
}
} else {
if {[dict get $arginfo -multiple]} {
#set display "$I$argname$NI ?$I$argname$NI?..."
set display "$clause \[$clause\]..."
} else {
set display $clause
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "[lindex [dict get $arginfo -choices] 0]"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display $argname
#} else {
# set display "$I$argname$NI"
#}
}
}
return $display
}
lappend PUNKARGS [list {
@id -id ::punk::args::synopsis
@cmd -name punk::args::synopsis\
@ -10295,7 +10421,19 @@ tcl::namespace::eval punk::args {
if {$spec eq ""} {
return
}
set form_names [dict get $spec form_names]
set dict_idx_to_name [dict create]
set dict_name_to_idx [dict create]
set all_form_names [dict get $spec form_names]
set idx 0
#assert: form_names is ordered as defined in the command definition - so idx into it is stable.
foreach fn $all_form_names {
dict set dict_idx_to_name $idx $fn
dict set dict_name_to_idx $fn $idx
incr idx
}
set form_names $all_form_names
if {$form ne "*"} {
if {[string is integer -strict $form]} {
set f [lindex $form_names $form]
@ -10314,171 +10452,51 @@ tcl::namespace::eval punk::args {
}
set SYND [dict create]
dict set SYND cmd_info [dict get $spec cmd_info]
set c_info [dict get $spec cmd_info]
set cmd_info [dict create]
dict for {k v} $c_info {
if {[string match -* $k]} {
dict set cmd_info [string range $k 1 end] $v
}
}
dict set SYND COMMAND $cmd_info
#leading "# " required (punk::ns::synopsis will pass through)
if {![dict exists $received -noheader]} {
set syn "# [Dict_getdef $spec cmd_info -summary ""]\n"
set GRY "\x1b\[38\;5\;8m"
set RST "\x1b\[m"
}
#todo - -multiple etc
foreach f $form_names {
set SYNLIST [list]
dict set SYND FORMS $f [list]
append syn "$id"
set forminfo [dict get $spec FORMS $f]
#foreach argname [dict get $forminfo LEADER_NAMES] {
# set arginfo [dict get $forminfo ARG_INFO $argname]
# set ARGD [dict create argname $argname class leader]
# if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display [lindex [dict get $arginfo -choices] 0]
# } elseif {[dict get $arginfo -type] eq "literal"} {
# set display $argname
# } else {
# set display $I$argname$RST
# }
# if {[dict get $arginfo -optional]} {
# append syn " ?$display?"
# } else {
# append syn " $display"
# }
# dict set ARGD type [dict get $arginfo -type]
# dict set ARGD optional [dict get $arginfo -optional]
# dict set ARGD display $display
# dict lappend SYND $f $ARGD
#}
set idx [dict get $dict_name_to_idx $f]
dict set SYND FORMS $f [dict create]
if {![dict exists $received -noheader]} {
set formsummary "FORM $idx $f"
if {[dict exists $forminfo -summary]} {
append formsummary " - [dict get $forminfo -summary]"
}
append syn "## $GRY$formsummary$RST\n"
}
append syn "$id"
set FORMARGS [list]
foreach argname [dict get $forminfo LEADER_NAMES] {
set arginfo [dict get $forminfo ARG_INFO $argname]
set typelist [dict get $arginfo -type]
if {[llength $typelist] == 1} {
set tp [lindex $typelist 0]
set ts [Dict_getdef $arginfo -typesynopsis ""]
if {$ts ne ""} {
#set arg_display [dict get $arginfo -typesynopsis]
set clause $ts
} else {
#set arg_display $argname
set alternates [list];#alternate acceptable types e.g literal(yes)|literal(ok) or indexpression|literal(first)
set type_alternatives [_split_type_expression $tp]
foreach tp_alternative $type_alternatives {
set tp_alternative_word1 [lindex $tp_alternative 0]
switch -exact -- $tp_alternative_word1 {
literal {
set match [lindex $tp_alternative 1]
lappend alternates $match
}
literalprefix {
#todo - trie styling on prefix calc
set match [lindex $tp_alternative 1]
lappend alternates $match
}
stringstartswith {
set match [lindex $tp_alternative 1]
lappend alternates $match*
}
stringendswith {
set match [lindex $tp_alternative 1]
lappend alternates *$match
}
default {
lappend alternates $I$argname$NI
}
}
#if {$tp_alternative eq "literal"} {
# lappend alternates [lindex $argname end]
#} elseif {[string match literal(*) $tp_alternative]} {
# set match [string range $tp_alternative 8 end-1]
# lappend alternates $match
#} elseif {[string match literalprefix(*) $tp_alternative]} {
# set match [string range $tp_alternative 14 end-1]
# lappend alternates $match
#} else {
# lappend alternates $I$argname$NI
#}
}
#remove dupes - but keep order (e.g of dupes -type string|int when no -typesynopsis was specified)
#todo - trie prefixes display
set alternates [punk::args::lib::lunique $alternates]
set clause [join $alternates |]
}
} else {
set n [expr {[llength $typelist]-1}]
set name_tail [lrange $argname end-$n end];#if there are enough tail words in the argname to match -types
set clause ""
set ts [Dict_getdef $arginfo -typesynopsis ""]
if {$ts ne ""} {
set tp_displaylist $ts
} else {
set tp_displaylist [lrepeat [llength $typelist] ""]
}
foreach typespec $typelist td $tp_displaylist elementname $name_tail {
#elementname will commonly be empty
if {[string match {\?*\?} $typespec]} {
set tp [string range $typespec 1 end-1]
set member_optional 1
} else {
set tp $typespec
set member_optional 0
}
if {$tp eq "literal"} {
set c $elementname
} elseif {[string match literal(*) $tp]} {
set match [string range $tp 8 end-1]
set c $match
} else {
if {$td eq ""} {
set c $I$tp$NI
} else {
set c $td
}
}
if {$member_optional} {
append clause " " "(?$c?)"
} else {
append clause " " $c
}
}
set clause [string trimleft $clause]
}
foreach argname [dict get $forminfo LEADER_NAMES] {
set display [_synopsis_form_arg_display $forminfo $argname]
append syn " $display"
set arginfo [dict get $forminfo ARG_INFO $argname]
set ARGD [dict create argname $argname class leader]
if {[dict get $arginfo -optional] || [dict exists $arginfo -default]} {
if {[dict get $arginfo -multiple]} {
#set display "?$I$argname$NI?..."
set display "?$clause?..."
} else {
set display "?$clause?"
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "?[lindex [dict get $arginfo -choices] 0]?"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display "?$argname?"
#} else {
# set display "?$I$argname$NI?"
#}
}
} else {
if {[dict get $arginfo -multiple]} {
#set display "$I$argname$NI ?$I$argname$NI?..."
set display "$clause ?$clause?..."
} else {
set display $clause
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "[lindex [dict get $arginfo -choices] 0]"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display $argname
#} else {
# set display "$I$argname$NI"
#}
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
foreach k {choices choiceprefix choicerestricted choicemultiple} {
if {[dict exists $arginfo -$k]} {
dict set ARGD $k [dict get $arginfo -$k]
}
}
append syn " $display"
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
dict set ARGD display $display
#dict lappend SYND $f $ARGD
lappend FORMARGS $ARGD
}
foreach argname [dict get $forminfo OPT_NAMES] {
@ -10490,7 +10508,7 @@ tcl::namespace::eval punk::args {
#(disallowed in punk::args::define)
set argdisplay $argname
} else {
#assert [llength $tp] == 1 (multiple values for flag unspported in punk::args::define)
#assert [llength $tp] == 1 (multiple values for flag unsupported in punk::args::define)
if {[string match {\?*\?} $tp]} {
set tp [string range $tp 1 end-1]
set value_is_optional true
@ -10509,19 +10527,30 @@ tcl::namespace::eval punk::args {
} else {
set alternates [list];#alternate acceptable types e.g literal(yes)|literal(ok) or indexpression|literal(first)
foreach tp_alternative [split $tp |] {
#-type literal not valid for opt - review
if {[string match literal(*) $tp_alternative]} {
set match [string range $tp_alternative 8 end-1]
lappend alternates $match
} elseif {[string match literalprefix(*) $tp_alternative]} {
set match [string range $tp_alternative 14 end-1]
lappend alternates $match
} else {
lappend alternates <$I$tp_alternative$NI>
set type_alternatives [_split_type_expression $tp]
foreach tp_alternative $type_alternatives {
set match [lindex $tp_alternative 1]
switch -- [lindex $tp_alternative 0] {
literal {
lappend alternates [list $match]
}
literalprefix {
lappend alternates [list $match]
}
stringstartswith {
lappend alternates [list $match*]
}
stringendswith {
lappend alternates [list *$match]
}
default {
lappend alternates $I<$tp_alternative>$NI
}
}
}
#todo - trie prefixes display?
#trie prefixes display?
#we probably don't want to show prefixes in synopsis.
#AI agents should be encouraged to use full values for clarity, and human users can refer to help for the prefix info if they care.
set alternates [punk::args::lib::lunique $alternates]
set tp_display [join $alternates |]
}
@ -10529,44 +10558,102 @@ tcl::namespace::eval punk::args {
#need to bracket alternate-types to distinguish pipes delimiting flag aliases
set tp_display "($tp_display)"
}
#consider optional: -f|--file|--file= -type string|num
#we can't show this as [-f|--file|--file= string|num]
#because the pipes make visually parsing it ambiguous.
#we *could* show this as [-f|--file|--file= (string|num)]
# but it lacks clarity in descripting we can supply --file string or --file=string
#showing it as [-f (string|num)|--file (string|num)|--file=(string|num)] is not as compact as it could be, but is reasonably precise.
#we could merge the first two to avoid repeating the type info - but then we would also need brackets to clarify the pipe applicability:
#e.g
# [(-f|--file (string|num))|--file=(string|num)]
#
#we choose to only merge in the case where there are no trailing= aliases or they are all trailing= aliases.
set aliasflags [split $argname |]
#set has_longopt_inlinevalue_alias [expr {[lsearch -glob $aliasflags *=] >= 0}]
set num_longopt_inlinevalue_aliases [llength [lsearch -all -glob $aliasflags *=]] ;#count list of indices of aliasflags that end with =
set homogenous_aliases [expr {$num_longopt_inlinevalue_aliases == 0 || $num_longopt_inlinevalue_aliases == [llength $aliasflags]}]
set argdisplay ""
foreach aliasflag [split $argname |] {
if {[string match --* $aliasflag]} {
if {[string index $aliasflag end] eq "="} {
set alias [string range $aliasflag 0 end-1]
if {$value_is_optional} {
append argdisplay "$alias$IS?$NIS=$tp_display$IS?$NIS|"
if {!$homogenous_aliases} {
foreach aliasflag $aliasflags {
if {[string match --* $aliasflag]} {
if {[string index $aliasflag end] eq "="} {
set alias [string range $aliasflag 0 end-1]
if {$value_is_optional} {
#append argdisplay "$alias$IS\[$NIS=$tp_display$IS\]$NIS|"
append argdisplay "$alias$I\[$NI=$tp_display$I\]$NI|"
} else {
append argdisplay "$alias=$tp_display|"
}
} else {
append argdisplay "$alias=$tp_display|"
if {$value_is_optional} {
#double-dashed flag without trailing = can't accept optional value
#append argdisplay "$aliasflag $IS\[$NIS$tp_display$IS\]$NIS|"
append argdisplay "$aliasflag|"
} else {
append argdisplay "$aliasflag $tp_display|"
}
}
} else {
if {$value_is_optional} {
append argdisplay "$aliasflag $IS?$NIS$tp_display$IS?$NIS|"
#flag can't accept optional value
append argdisplay "$aliasflag|"
} else {
append argdisplay "$aliasflag $tp_display|"
}
}
}
set argdisplay [string trimright $argdisplay |]
} else {
if {$num_longopt_inlinevalue_aliases > 0} {
#all aliases are longopt inlinevalue aliases
#review
# --file=|--fname= -type string
# -> (--file|--fname)=type
# or
# -> (--file|--fname)[=type]
#first transform the argname to remove the trailing = and bracket the aliases if there are multiple
#review - we don't expect any arguments to be defined with inner = in the name.
#todo - enforce no inner = in argname in punk::args::define for options?
#
set argname "[string map {= ""} $argname]"
if {$num_longopt_inlinevalue_aliases > 1} {
set argname "($argname)"
}
if {$value_is_optional} {
set argdisplay "$argname$I\[$NI=$tp_display$I\]$NI"
} else {
set argdisplay "$argname=$tp_display"
}
} else {
#no longopts with trailing = aliases, so we can show the type info without ambiguity as applying to all aliases
if {$value_is_optional} {
#single dash flag can't accept optional value
append argdisplay "$aliasflag|"
set argdisplay "$argname $I\[$NI$tp_display$I\]$NI"
} else {
append argdisplay "$aliasflag $tp_display|"
set argdisplay "$argname $tp_display"
}
}
}
set argdisplay [string trimright $argdisplay |]
}
if {[dict get $arginfo -optional]} {
if {[dict get $arginfo -multiple]} {
set display "?$argdisplay?..."
#set display "?$argdisplay?..."
set display "\[$argdisplay\]..."
} else {
set display "?$argdisplay?"
#set display "?$argdisplay?"
set display "\[$argdisplay\]"
}
} else {
if {[dict get $arginfo -multiple]} {
set display "$argdisplay ?$argdisplay?..."
#set display "$argdisplay ?$argdisplay?..."
set display "$argdisplay \[$argdisplay\]..."
} else {
set display $argdisplay
}
@ -10606,136 +10693,43 @@ tcl::namespace::eval punk::args {
# }
# }
#}
#todo -mash
append syn " $display"
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
foreach k {choices choiceprefix choicerestricted choicemultiple} {
if {[dict exists $arginfo -$k]} {
dict set ARGD $k [dict get $arginfo -$k]
}
}
dict set ARGD display $display
#dict lappend SYND $f $ARGD
lappend FORMARGS $ARGD
}
foreach argname [dict get $forminfo VAL_NAMES] {
set arginfo [dict get $forminfo ARG_INFO $argname]
set typelist [dict get $arginfo -type]
if {[llength $typelist] == 1} {
set tp [lindex $typelist 0]
set ts [Dict_getdef $arginfo -typesynopsis ""]
if {$ts ne ""} {
#set arg_display [dict get $arginfo -typesynopsis]
set clause $ts
} else {
#set arg_display $argname
set alternates [list];#alternate acceptable types e.g literal(yes)|literal(ok) or indexpression|literal(first)
foreach tp_alternative [split $tp |] {
if {$tp_alternative eq "literal"} {
lappend alternates [lindex $argname end]
} elseif {[string match literal(*) $tp_alternative]} {
set match [string range $tp_alternative 8 end-1]
lappend alternates $match
} elseif {[string match literalprefix(*) $tp_alternative]} {
set match [string range $tp_alternative 14 end-1]
lappend alternates $match
} else {
lappend alternates $I$argname$NI
}
}
#remove dupes - but keep order (e.g of dupes -type string|int when no -typesynopsis was specified)
#todo - trie prefixes display
set alternates [punk::args::lib::lunique $alternates]
set clause [join $alternates |]
}
} else {
set n [expr {[llength $typelist]-1}]
set name_tail [lrange $argname end-$n end];#if there are enough tail words in the argname to match -types
set clause ""
set ts [Dict_getdef $arginfo -typesynopsis ""]
if {$ts ne ""} {
set tp_displaylist $ts
} else {
set tp_displaylist [lrepeat [llength $typelist] ""]
}
foreach typespec $typelist td $tp_displaylist elementname $name_tail {
#elementname will commonly be empty
if {[string match {\?*\?} $typespec]} {
set tp [string range $typespec 1 end-1]
set member_optional 1
} else {
set tp $typespec
set member_optional 0
}
#handle alternate-types e.g literal(text)|literal(binary)
set alternates [list]
foreach tp_alternative [split $tp |] {
if {$tp_alternative eq "literal"} {
lappend alternates $elementname
} elseif {[string match literal(*) $tp_alternative]} {
set match [string range $tp_alternative 8 end-1]
lappend alternates $match
} elseif {[string match literalprefix(*) $tp_alternative]} {
set match [string range $tp_alternative 14 end-1]
lappend alternates $match
} else {
if {$td eq ""} {
lappend alternates $I$tp$NI
} else {
lappend alternates $td
}
}
}
set alternates [punk::args::lib::lunique $alternates]
set c [join $alternates |]
if {$member_optional} {
append clause " " "(?$c?)"
} else {
append clause " " $c
}
}
set clause [string trimleft $clause]
}
set display [_synopsis_form_arg_display $forminfo $argname]
append syn " $display"
set ARGD [dict create argname $argname class value]
if {[dict get $arginfo -optional] || [dict exists $arginfo -default]} {
if {[dict get $arginfo -multiple]} {
#set display "?$I$argname$NI?..."
set display "?$clause?..."
} else {
set display "?$clause?"
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "?[lindex [dict get $arginfo -choices] 0]?"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display "?$argname?"
#} else {
# set display "?$I$argname$NI?"
#}
}
} else {
if {[dict get $arginfo -multiple]} {
#set display "$I$argname$NI ?$I$argname$NI?..."
set display "$clause ?$clause?..."
} else {
set display $clause
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "[lindex [dict get $arginfo -choices] 0]"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display $argname
#} else {
# set display "$I$argname$NI"
#}
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
foreach k {choices choiceprefix choicerestricted choicemultiple} {
if {[dict exists $arginfo -$k]} {
dict set ARGD $k [dict get $arginfo -$k]
}
}
append syn " $display"
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
dict set ARGD display $display
#dict lappend SYND $f $ARGD
lappend FORMARGS $ARGD
}
#accepts unnamed extra arguments e.g toplevel docid for ensembles and ensemble-like commands
if {[dict get $forminfo VAL_UNNAMED]} {
set display "?<unnamed>...?"
set display {[<unnamed>...]}
append syn " $display"
set ARGD [dict create argname "" class value]
dict set ARGD type any
@ -10745,7 +10739,7 @@ tcl::namespace::eval punk::args {
lappend FORMARGS $ARGD
}
append syn \n
dict set SYND FORMS $f $FORMARGS
dict set SYND FORMS $f args $FORMARGS
}
switch -- $opt_return {
full {
@ -10757,7 +10751,8 @@ tcl::namespace::eval punk::args {
set summary "# [Dict_getdef $spec cmd_info -summary ""]\n"
}
set FORMS [dict get $SYND FORMS]
dict for {form arglist} $FORMS {
dict for {form arginfo} $FORMS {
set arglist [dict get $arginfo args]
append summary $id
set class_state leader
set option_count 0
@ -10774,7 +10769,7 @@ tcl::namespace::eval punk::args {
incr value_count
if {$class_state ne "value"} {
if {$option_count > 0} {
append summary " ?options ($option_count defined)?"
append summary " \[OPTIONS ($option_count defined)\]"
}
set class_state value
}
@ -10783,7 +10778,7 @@ tcl::namespace::eval punk::args {
}
}
if {$value_count == 0 && $option_count > 0} {
append summary " ?options ($option_count defined)?"
append summary " \[OPTIONS ($option_count defined)\]"
}
append summary \n
}
@ -10803,6 +10798,7 @@ tcl::namespace::eval punk::args {
}
#REVIEW
lappend PUNKARGS [list {
@id -id ::punk::args::synopsis_summary
@cmd -name punk::args::synopsis_summary -help\
@ -10852,9 +10848,10 @@ tcl::namespace::eval punk::args {
}
}
}
if {$code ne ""} {
if {$code ne "" && [tcl::string::index $code end] eq "m"} {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set codestack [list "\x1b\[m"]
#set codestack [list "\x1b\[m"]
set codestack [list $code]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set codestack [list $code]
} elseif {[punk::ansi::codetype::is_sgr $code]} {
@ -10862,10 +10859,9 @@ tcl::namespace::eval punk::args {
set dup_posns [lsearch -all -exact $codestack $code] ;#must be -exact because of square-bracket glob chars
set codestack [lremove $codestack {*}$dup_posns]
lappend codestack $code
} else {
#? ignore other ANSI codes?
}
}
#? ignore other ANSI codes?
}
if {[string match -* $plain_s] || [string match ?- $plain_s]} {
}

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

@ -2986,6 +2986,71 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
time -type integer -optional 1
} "@doc -name Manpage: -url [manpage_tcl file]" ]
lappend PUNKARGS [list {
@id -id ::tcl::file::attributes
@cmd -name "Built-in: tcl::file::attributes"\
-summary\
"Get/Set platform-specific values associated with a file/directory."\
-help\
"This subcommand returns or sets platform-specific values associated with a file.
The first form without specificing option, returns a list of the platform-specific options and their values.
The first form with an option returns the value for the given option.
The last form sets one or more of the values. The values are as follows:
On Unix, ${$B}-group${$N} gets or sets the group name for the file. A group id can be given to the command, but it
returns a group name. ${$B}-owner${$N} gets or sets the user name of the owner of the file. The command returns the
owner name, but the numerical id can be passed when setting the owner. ${$B}-permissions${$N} retrieves or sets a
file's access permissions, using octal notation by default. This option also provides limited support for
setting permissions using the symbolic notation accepted by the chmod command, following the form
${$B}[ugo]?[[+-=][rwxst],[...]]${$N}. Multiple permission specifications may be given, separated by commas.
E.g., ${$B}u+s,go-rw${$N} would set the setuid bit for a file's owner as well as remove read and write permission for
the file's group and other users. An ls-style string of the form rwxrwxrwx is also accepted but must always
be 9 characters long. E.g., ${$B}rwxr-xr-t${$N} is equivalent to ${$B}01755${$N}. On versions of Unix supporting file flags,
${$B}-readonly${$N} returns the value of, or sets, or clears the readonly attribute of a file, i.e., the user
immutable flag (${$B}uchg${$N}) to the ${$B}chflags${$N} command.
On Windows, ${$B}-archive${$N} gives the value or sets or clears the archive attribute of the file. ${$B}-hidden${$N} gives the
value or sets or clears the hidden attribute of the file. ${$B}-longname${$N} will expand each path element to its long
version. This attribute cannot be set. ${$B}-readonly${$N} gives the value or sets or clears the readonly attribute of
the file. ${$B}-shortname${$N} gives a string where every path element is replaced with its short (8.3) version of the
name if possible. For path elements that cannot be mapped to short names, the long name is retained. This
attribute cannot be set. ${$B}-system${$N} gives or sets or clears the value of the system attribute of the file.
On macOS and Darwin, ${$B}-creator${$N} gives or sets the Finder creator type of the file. ${$B}-hidden${$N} gives or sets or
clears the hidden attribute of the file. ${$B}-readonly${$N} gives or sets or clears the readonly attribute of the file.
${$B}-rsrclength${$N} gives the length of the resource fork of the file, this attribute can only be set to the value 0,
which results in the resource fork being stripped off the file.
On all platforms, files in ${$B}zipfs${$N} mounted archives return the following attributes.
These are all read-only and cannot be directly set.
${$B}-archive${$N}
The path of the mounted ZIP archive containing the file.
${$B}-compsize${$N}
The compressed size of the file within the archive. This is 0 for directories.
${$B}-crc${$N}
The CRC of the file if present, else 0.
${$B}-mount${$N}
The path where the containing archive is mounted.
${$B}-offset${$N}
The offset of the file within the archive.
${$B}-uncompsize${$N}
The uncompressed size of the file. This is ${$B}0${$N} for directories.
Other attributes may be present in the returned list. These should be ignored."
@form -form "get"
@values -min 1 -max 2
name -type string -optional 0
option -type stringstartswith(-) -typesynopsis {-${$I}option${$NI}} -optional 1
@form -form "set"
@values -min 3 -max -1
name -type string -optional 0
option_value -type {stringstartswith(-) string} -typesynopsis {-${$I}option${$NI} ${$I}value${$NI}} -optional 0 -multiple 1
} "@doc -name Manpage: -url [manpage_tcl file]" ]
lappend PUNKARGS [list {
@id -id ::tcl::file::channels
@cmd -name "Built-in: tcl::file::channels"\
@ -3026,6 +3091,26 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
pathname -optional 1 -type string -multiple 1
} "@doc -name Manpage: -url [manpage_tcl file]" ]
lappend PUNKARGS [list {
@id -id ::tcl::file::dirname
@cmd -name "Built-in: tcl::file::dirname"\
-summary\
"Return a path excluding last element."\
-help\
"Returns a name comprised of all of the path components in name excluding the last element.
If name is a relative file name and only contains one path element, then returns “.”. If name
refers to a root directory, then the root directory is returned. For example,
${[punk::args::helpers::example {
${$B} file dirname c:/
}]}
returns ${$B}c:/${$N}.
"
@values -min 1 -max 1
name -type string
} "@doc -name Manpage: -url [manpage_tcl file]" ]
lappend PUNKARGS [list {
@id -id ::tcl::file::copy
@cmd -name "Built-in: tcl::file::copy"\
@ -3104,7 +3189,10 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
#tcl 9+
lappend PUNKARGS [list {
@id -id ::tcl::file::home
@cmd -name "Built-in: tcl::file::home" -help\
@cmd -name "Built-in: tcl::file::home"\
-summary\
"Return the home directory for a user."\
-help\
"If no argument is specified, the command returns the home directory of the current user.
This is generally the value of the ${$B}$HOME${$N} environment variable except that on Windows
platforms backslashes in the path are replaced by forward slashes. An error is raised if
@ -3134,7 +3222,29 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
} "@doc -name Manpage: -url [manpage_tcl file]" ]
#join
#link
lappend PUNKARGS [list {
@id -id ::tcl::file::join
@cmd -name "Built-in: tcl::file::join"\
-summary\
"Join directory/file components into a single path."\
-help\
"Takes one or more file names and combines them, using the correct path separator for the current platform.
If a particular name is relative, then it will be joined to the previous file name argument. Otherwise, any
earlier arguments will be discarded, and joining will proceed from the current argument. For example,
${[punk::args::helpers::example {
${$B}file join ${$N} a b /foo bar
}]}
returns ${$B}/foo/bar${$N}.
Note that any of the names can contain separators, and that the result is always canonical for the current
platform: ${$B}/${$N} for Unix and Windows.
"
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]" ]
lappend PUNKARGS [list {
@id -id ::tcl::file::link
@cmd -name "Built-in: tcl::file::link"\
@ -3242,8 +3352,33 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
#owned
#pathtype
lappend PUNKARGS [list {
@id -id ::tcl::file::owned
@cmd -name "Built-in: tcl::file::owned"\
-summary\
"Test file owned by current user."\
-help\
"Returns ${$B}1${$N} if the file ${$I}name${$NI} is owned by the current user, ${$B}0${$N} otherwise."
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
lappend PUNKARGS [list {
@id -id ::tcl::file::pathtype
@cmd -name "Built-in: tcl::file::pathtype"\
-summary\
{Return path type. Either absolute, relative or volumerelative.}\
-help\
"Returns one of ${$B}absolute${$N}, ${$B}relative${$N}, ${$B}volumerelative${$N}. If name refers to a specific file on a specific
volume, the path type will be ${$B}absolute${$N}. If name refers to a file relative to the current working
directory, then the path type will be ${$B}relative${$N}. If name refers to a file relative to the current
working directory on a specified volume, or to a specific file on the current working volume, then
the path type is ${$B}volumerelative${$N}."
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
lappend PUNKARGS [list {
@id -id ::tcl::file::readable
@cmd -name "Built-in: tcl::file::readable"\
@ -3299,9 +3434,46 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
#separator
#size
#split
lappend PUNKARGS [list {
@id -id ::tcl::file::separator
@cmd -name "Built-in: tcl::file::separator"\
-summary\
{File separator character}\
-help\
"If no argument is given, returns the character which is used to separate path segments for native
files on this platform. If a path is given, the filesystem responsible for that path is asked to
return its separator character. If no file system accepts name, an error is generated."
@values -min 0 -max 1
name -optional 1 -type string -help\
"Path to query for separator character."
} "@doc -name Manpage: -url [manpage_tcl file]"]
lappend PUNKARGS [list {
@id -id ::tcl::file::size
@cmd -name "Built-in: tcl::file::size"\
-summary\
{Size of named file in bytes.}\
-help\
"Returns a decimal string giving the size of file ${$I}name${$NI} in bytes.
If the file does not exist or its size cannot be queried then an error is generated."
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
lappend PUNKARGS [list {
@id -id ::tcl::file::split
@cmd -name "Built-in: tcl::file::split"\
-summary\
{Split a path into list of components.}\
-help\
"Returns a list whose elements are the path components in ${$I}name${$NI}. The first element of the list will have
the same path type as ${$I}name${$NI}. All other elements will be relative. Path separators will be discarded unless
they are needed to ensure that an element is unambiguously relative."
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
lappend PUNKARGS [list {
@id -id ::tcl::file::stat
@cmd -name "Built-in: tcl::file::stat"\
@ -3399,8 +3571,20 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
As such, they can be relied upon to be used with operating-system native APIs
and external programs that require a filename."
@values -min 0 -max 2
nameVar -type string -optional 1
template -type string -optional 1
nameVar -type string -optional 1 -help\
"Variable to *receive* the name of the created temporary file.
Any existing value in the variable will not be read, and is just overwritten."
template -type string -optional 1 -help\
"On some platforms, such as windows:
- file extension is ignored.
- any directory components are ignored and
the last segment is used as a prefix for the temporary file name.
- If the TMP or TEMP environment variables are set, they are used
as the directory for the temporary file, otherwise the user's home
directory is used if it can be determined. (may depend on existence
of HOME or USERPROFILE environment variables.)
On other platforms, such as unix, the template may be handled
differently."
} "@doc -name Manpage: -url [manpage_tcl file]"]
#tildeexpand
@ -4528,11 +4712,16 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
}]}
}
@values -min 1
#{args body ?namespace?} is a single argument that is a list of two or three elements,
#as opposed to a clause of separate arguments.
#we don't have a way to validate the type of each element in a list - we can only check the length of the whole list.
@values -min 1 -max -1
"{args body ?namespace?}" -optional 0 -type list -minsize 2 -maxsize 3
arg -type any -optional 1 -multiple 1
} "@doc -name Manpage: -url [manpage_tcl apply]"\
{
@examples -help {
@ -7094,7 +7283,7 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
start -type number|expr
count -type literalprefix(count)
countelements -type number|expr
"by step" -type {literalprefix(by) number|expr} -optional 1
"by step" -type {?literalprefix(by)? number|expr} -optional 1
@form -form count
@leaders -min 0 -max 0
@ -10621,15 +10810,34 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
#force all on_handlers to be together and all try_handlers to be together, and it would force
#one type of handler to be listed always before or always after the other.
handler -optional 1 -multiple 1 -type {literal(on)|literal(trap) string list string}\
-typesynopsis {"" code|pattern variableList script}
-typesynopsis {"" oncode_or_trappattern variableList script}
#in our typesynopsis we deliberately don't put a pipe symbol in oncode_or_trappattern.
# e.g code|pattern would imply either on or trap could be combined with either code or pattern, which is not the case.
#todo?
#a way to define a compound type?
#handler -optional 1 -multiple 1 -type {<on_handler>|<try_handler>}
##<on_handler> -type {literal(on) <code> <variableList> <script>}
##<code> -type int -choices {0|ok 1|error 2|return 3|break 4|continue} -choicelabels {...}
#consider bracketed forms for -type - but we would have to do more complex parsing to determine size of clauses
##handler -type {(literal(on) code variableList script)|(literal(trap) pattern variableList script)}
## in this case either possible handler has length 4 - but we could easily imagine cases where different handlers have different lengths
#this gets unwieldy in synopsis listings.
#a way to define a compound type? perhaps with arity indicators for the component types? e.g
#handler -optional 1 -multiple 1 -type {<on_handler:4>|<try_handler:4>}
##on_handler:4 -type {literal(on) code variableList script}
##code -type int -choices {0|ok 1|error 2|return 3|break 4|continue} -choicelabels {...}
#..
##<try_handler> -type {literal(trap) <pattern> <variableList> <script>}
##<pattern> -type list
##try_handler -type {literal(trap) pattern variableList script}
##pattern -type list
##etc
#how would we declare arity for a compound type that has alternate subtypes of different arity?
#e.g <generalhandler>:3..4 -type {<on_handler:4>|<other_handler:3>}
#would these types be global or per definition?
#if both allowed - what about documentation packages clashing names?
#require some kind of namespacing for types? e.g package::types::code ?)
#e.g punk::args::moduledoc::tkcore::anchor (n|ne|e|se|s|sw|w|nw|center)
#could we provide a way to import for a definition eg @typeimport -package punk::args::moduledoc::tkcore
# so that the types defined there could be used in our definitions without needing to namespace them?
#consider also RPN for compound type definitions
##<mytype1> -type {{int double OR}}
@ -12052,7 +12260,7 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
@form -form "basic"
pattern -type string -optional 1 -help "glob pattern"
@form -form "controlledglob"
@form -form "controlled"
@values -min 2 -max 2
patterntype -type string -choices {-glob -regexp} -typesynopsis -glob|-regex -optional 0
pattern -type string -optional 0

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

@ -96,7 +96,24 @@ tcl::namespace::eval punk::auto_exec {
-summary\
"Manage the hash table of autoexec commands cached in ::auto_execs."\
-help\
{see also ::punk::auto_exec::rehash}
{Manage the cache of autoexec commands in the ::auto_execs array.
This is analogous to the 'hash' command in shells such as csh, tcsh and zsh, or 'hash' in bash.
It can be used to display the current cached ${$B}auto_execok${$N} commands, to add new commands to the cache,
to delete commands from the cache, and to clear the cache.
When adding new commands to the cache, it will attempt to find the command string associated with
the given name by calling auto_execok for that name, and if found it will add it to the cache.
If not found, it will display an error message on stderr for that name and add an empty string to
the cache for that name if the name is an absolute path or a bare word.
When displaying commands with ${$B}hash -t ${$I}name${$NI}${$N}, if only a single name is provided, then the output will
be the raw command string associated with that autoexec command in the hash table. If multiple names
are provided, then the output will be a string containing each name and its associated command string
on a separate line.
see also ::punk::auto_exec::rehash}
#---------------------
@form -form {show_or_set}
@ -125,7 +142,7 @@ tcl::namespace::eval punk::auto_exec {
If multiple names are provided, then the output will be a string containing each
name and its associated command string on a separate line."
#---------------------
@form -form {delete}
@form -form {delete} -summary "Delete autoexec commands from the hash table."
@opts
-d -type none -optional 0 -help\
"Delete specified autoexec commands from the hash table."

88
src/modules/punk/basictelnet-999999.0a1.0.tm

@ -465,6 +465,8 @@ namespace eval punk::basictelnet {
-mode -choices {line raw} -default line
-mouse -type boolean -default 0 -help\
"Whether to enable mouse events"
-windowsize -type string -default "80x25" -help\
"Specify the initial window size to report to the server as colsxrows, e.g 80x25"
@values -min 1 -max 2
server -type string -help\
"Hostname or IP address"
@ -477,6 +479,20 @@ namespace eval punk::basictelnet {
set port [dict get $argd values port]
set tmode [dict get $argd opts -mode]
set mouse [dict get $argd opts -mouse]
set windowsize [dict get $argd opts -windowsize]
if {![string match {*x*} $windowsize]} {
puts stderr "Invalid windowsize format - must be colsxrows e.g 80x25"
return
}
lassign [split $windowsize x] test_c test_r
if {![string is integer -strict $test_c] || ![string is integer -strict $test_r]} {
puts stderr "Invalid windowsize format '$windowsize' - cols and rows must be integers e.g 80x25"
return
}
variable window_cols
variable window_rows
set window_cols $test_c
set window_rows $test_r
if {[info commands ::colour] ne ""} {
#The ansiwrap filter on stdout/stderr slows rendering significantly e.g on max headroom ansi vid at server: 1984.ws
@ -526,16 +542,38 @@ namespace eval punk::basictelnet {
if {$debug && $consolewidth-$::punk::basictelnet::window_cols < 80} {
puts stderr "Terminal width '$consolewidth' not wide enough for debug_window width: 80 + telnet window_cols:$::punk::basictelnet::window_cols"
puts stderr "Turn off debug, or make terminal window wider"
if {[info commands ::colour] ne ""} {
::colour $priorcolourstate
}
if {[info commands ::mode] ne ""} {
::mode $priormode
}
return
} elseif {$consolewidth < $::punk::basictelnet::window_cols} {
puts stderr "Terminal width '$consolewidth' is less than telnet window_cols:$::punk::basictelnet::window_cols"
puts stderr "Ensure terminal is greater than or equal to punk::basictelnet::window_cols"
if {[info commands ::colour] ne ""} {
::colour $priorcolourstate
}
if {[info commands ::mode] ne ""} {
::mode $priormode
}
return
}
#todo - allow telnet with channels other than stdin/stdout - and multiple sessions - per session option_states
reset_option_states
set sock [socket $server $port]
if {[catch {set sock [socket $server $port]} errM]} {
puts stderr "Failed to connect to $server:$port - $errM"
if {[info commands ::colour] ne ""} {
::colour $priorcolourstate
}
if {[info commands ::mode] ne ""} {
::mode $priormode
}
return
}
#set sock [socket $server $port]
#chan configure $sock -buffering none -blocking 0 -encoding binary -translation crlf -eofchar {}
#chan configure $sock -buffering none -blocking 0 -encoding binary -translation binary -eofchar {}
chan configure $sock -buffering none -blocking 0 -encoding iso8859-1 -translation binary -eofchar {}
@ -552,7 +590,6 @@ namespace eval punk::basictelnet {
if {[info commands ::colour] ne ""} {
::colour $priorcolourstate
}
if {[info commands ::mode] ne ""} {
::mode $priormode
}
@ -657,7 +694,23 @@ namespace eval punk::basictelnet {
variable fromserver_unprocessed
chan event $sock readable {}
variable in_sb
set chunksize 4096 ;#No choice of chunksize can avoid the possibility of splitting a token such as a Telnet protocol command or an ANSI sequence.
#--------------------------------
#review - the possibility of splitting ANSI or telnet tokens is handled reasonably well in the code below
#- but the choice of chunksize still has impacts on smoothness of the display for streaming ANSI such as from 1984.ws
#- todo - investigate further.
#--------------------------------
#set chunksize 4096
#set chunksize 8192
#set chunksize 16384
#set chunksize 32768
set chunksize 65536 ;#pretty reasonable for 160x50 stream from 1984.ws, but still visible tearing? - review - maybe make this adaptive based on how much data is coming in and how long it's taking to process? - but be careful not to starve the output processing by waiting too long to read more data in.
#set chunksize 131072
#set chunksize 262144
#set chunksize 524288 ;#clear lag for 160x50 stream from 1984.ws.
#--------------------------------
#No choice of chunksize can avoid the possibility of splitting a token such as a Telnet protocol command or an ANSI sequence.
#in theory, a split ANSI sequence won't cause a problem - except if we have debug on which could emit a request on stdout (e.g get_cursor_pos)
#as a byte oriented supposedly ascii-by-default protocol - we shouldn't expect to get utf-8 without having negotiated it - but it looks suspiciously like this is the sort of thing that happens (2024) review? Examples? mapscii.me 1984.ws? Test.
#randomly chosen chunk boundaries - whether due to size or a combination of network speed and event scheduling can mean we get some utf8 characters split too.
@ -705,14 +758,18 @@ namespace eval punk::basictelnet {
append debug_info "------raw data [string length $data]---prev unprocessed:[string length $last_unprocessed]---" \n
#append debug_info [ansistring VIEW -lf 1 -vt 1 [encoding convertfrom utf-8 $data]] \n
#set rawview [ansistring VIEW -lf 1 -vt 1 [encoding convertfrom $encoding_guess $data]]
set rawview [ansistring VIEW -lf 1 -vt 1 $data]
#set viewblock [overtype::left -wrap 1 -width 78 -height 4 "" $rawview]
set viewblock [overtype::renderspace -cp437 1 -wrap 1 -width 78 -height 4 "" $rawview]
set lines [split $viewblock \n]
if {[llength $lines] > 4} {
append debug_info [join [list {*}[lrange $lines 0 1] "...<[expr {[llength $lines] -4}] lines undisplayed>..." {*}[lrange $lines end-1 end]] \n]
} else {
append debug_info $viewblock
if 1 {
set rawview_end end
set rawview [ansistring VIEWSTYLE -lf 1 -vt 1 [string range $data 0 $rawview_end]]
#set viewblock [overtype::left -wrap 1 -width 78 -height 4 "" $rawview]
set viewblock [overtype::renderspace -cp437 1 -wrap 1 -width 78 -height 4 "" $rawview]
set lines [split $viewblock \n]
if {[llength $lines] > 4} {
append debug_info [join [list {*}[lrange $lines 0 1] "...<[expr {[llength $lines] -4}] lines undisplayed>..." {*}[lrange $lines end-1 end]] \n]
} else {
append debug_info $viewblock
}
}
append debug_info "\n------------------------------------------" \n
}
@ -869,15 +926,14 @@ namespace eval punk::basictelnet {
#todo - consider impact of full tracking of ansi SGR stack on stream.. (only when in debug?)
set sgrstack [list]
foreach {pt ansicode} $ansisplits {
if {$ansicode ne ""} {
if {[punk::ansi::codetype::is_sgr $ansicode]} {
lappend sgrstack $ansicode
}
if {[tcl::string::index $ansicode end] eq "m" && [punk::ansi::codetype::is_sgr $ansicode]} {
lappend sgrstack $ansicode
}
}
if {[llength $sgrstack]} {
#replay the SGR stack (only goes back within current chunk - often all that's needed - but not ideal)
puts -nonewline stdout [punk::ansi::codetype::sgr_merge_list {*}$sgrstack]
#puts -nonewline stdout [punk::ansi::codetype::sgr_merge_list {*}$sgrstack]
puts -nonewline stdout [punk::ansi::codetype::sgr_merge $sgrstack]
flush stdout
}
}

6
src/modules/punk/char-999999.0a1.0.tm

@ -1860,8 +1860,10 @@ tcl::namespace::eval punk::char {
lappend settype_list [tcl::dict::get $charsets $setname settype]
}
set charset_names [linsert $charset_names 0 "Set Name"]
set settype_list [linsert $settype_list 0 "Set Type"]
#set charset_names [linsert $charset_names 0 "Set Name"]
ledit charset_names 0 -1 "Set Name"
#set settype_list [linsert $settype_list 0 "Set Type"]
ledit settype_list 0 -1 "Set Type"
return [textblock::join -- [list_as_lines -- $charset_names] " " [list_as_lines $settype_list]]
}

20
src/modules/punk/console-999999.0a1.0.tm

@ -2608,7 +2608,7 @@ namespace eval punk::console {
error "dec_request_setting unrecognised name $name. Known values: [dict keys $DECRQSS_DICT]"
}
set str [dict get $DECRQSS_DICT $name]
set re_str [string map [list * \\* \$ \\\$ + \\+ ( \\(] $str] ;#regex escaped
set re_str [string map [list | \\| * \\* \$ \\\$ + \\+ ( \\( ) \\)] $str] ;#regex escaped
#review {[0-9;:]} - too restrictive? - what values can be returned? alnum? - we perhaps at least need to exclude ESC so we don't overmatch
set capturingregex [string map [list %s% $re_str] {(.*)(\x1bP([0-1]\$r[0-9;:]*)(?:%s%){0,1}\x1b\\)$}] ;#must capture prefix,entire-response,response-payload
#todo - handle xterm : [0-1] $ r D...D ST
@ -2938,6 +2938,13 @@ namespace eval punk::console {
proc clear_all {} {
puts -nonewline stdout [punk::ansi::clear_all]
}
proc clear_scrollback {} {
puts -nonewline stdout [punk::ansi::clear_scrollback]
}
proc S8C1R {} {
puts -nonewline stdout [punk::ansi::S8C1R]
}
proc reset {} {
puts -nonewline stdout [punk::ansi::reset]
}
@ -3073,11 +3080,12 @@ namespace eval punk::console {
proc move_emitblock_return {row col textblock} {
lassign [punk::console::get_cursor_pos_list] orig_row orig_col
set commands ""
foreach ln [split $textblock \n] {
append commands [punk::ansi::move_emit $row $col $ln]
incr row
}
set commands [punk::ansi::move_emit $row $col $textblock] ;#move_emit can handle multiple line blocks.
#set commands ""
#foreach ln [split $textblock \n] {
# append commands [punk::ansi::move_emit $row $col $ln]
# incr row
#}
append commands [punk::ansi::move $orig_row $orig_col]
puts -nonewline $commands
return

71
src/modules/punk/imap4-999999.0a1.0.tm

@ -1489,7 +1489,10 @@ tcl::namespace::eval punk::imap4 {
namespace eval argdoc {
lappend PUNKARGS [list {
@id -id ::punk::imap4::CONNECT
@cmd -name punk::imap4::CONNECT -help\
@cmd -name punk::imap4::CONNECT\
-summary\
"Connect to an IMAP server and initialise the handler."\
-help\
"Open a new IMAP connection and initialise the handler.
Returns the Tcl channel to use in subsequent calls to
the API. Other API commands will return zero on success.
@ -1946,7 +1949,10 @@ tcl::namespace::eval punk::imap4 {
lappend PUNKARGS [list {
@id -id ::punk::imap4::AUTH_PLAIN
@cmd -name punk::imap4::AUTH_PLAIN -help\
@cmd -name punk::imap4::AUTH_PLAIN\
-summary\
"Authenticate using the PLAIN SASL mechanism."\
-help\
"PLAIN SASL Authentication mechanism.
This uses the 'initial response' to send
@ -2001,7 +2007,10 @@ tcl::namespace::eval punk::imap4 {
lappend PUNKARGS [list {
@id -id ::punk::imap4::MYRIGHTS
@cmd -name punk::imap4::MYRIGHTS -help\
@cmd -name punk::imap4::MYRIGHTS\
-summary\
"Get the set of rights that the current user has to the mailbox."\
-help\
"Get the set of rights that the current user
has to the mailbox.
@ -2026,7 +2035,10 @@ tcl::namespace::eval punk::imap4 {
}
lappend PUNKARGS [list {
@id -id ::punk::imap4::GETACL
@cmd -name punk::imap4::GETACL -help\
@cmd -name punk::imap4::GETACL\
-summary\
"Get ACL for a mailbox."\
-help\
"Get ACL for a mailbox.
The current user must have permission to administer
the mailbox (the \"a\" right) to perform ACL commands
@ -2057,7 +2069,10 @@ tcl::namespace::eval punk::imap4 {
}
lappend PUNKARGS [list {
@id -id ::punk::imap4::SETACL
@cmd -name punk::imap4::SETACL -help\
@cmd -name punk::imap4::SETACL\
-summary\
"Set ACL for a specified user on a mailbox."\
-help\
"Set ACL for a specified user on a mailbox.
The current user must have permission to administer
the mailbox (the \"a\" right) to perform ACL commands
@ -2134,7 +2149,10 @@ tcl::namespace::eval punk::imap4 {
lappend PUNKARGS [list {
@id -id ::punk::imap4::SELECT
@cmd -name punk::imap4::SELECT -help\
@cmd -name punk::imap4::SELECT\
-summary\
"Select a mailbox to access messages in it."\
-help\
{Selects a mailbox so that messages in the mailbox can be
accessed.
@ -2171,7 +2189,10 @@ tcl::namespace::eval punk::imap4 {
lappend PUNKARGS [list {
@id -id ::punk::imap4::EXAMINE
@cmd -name punk::imap4::EXAMINE -help\
@cmd -name punk::imap4::EXAMINE\
-summary\
"Select a mailbox in read-only mode."\
-help\
{The EXAMINE command is identical to SELECT and returns the
same output; however, the selected mailbox is identified as
read-only. No changes to the permanent state of the mailbox,
@ -2322,7 +2343,10 @@ tcl::namespace::eval punk::imap4 {
lappend PUNKARGS [list {
@id -id ::punk::imap4::FETCH
@cmd -name punk::imap4::FETCH -help\
@cmd -name punk::imap4::FETCH\
-summary\
"Fetch attributes of messages in the currently selected mailbox."\
-help\
"Fetch a number of attributes from messages.
A mailbox must be SELECTed first and an appropriate
sequence-set supplied for the message(s) of interest."
@ -2756,7 +2780,10 @@ tcl::namespace::eval punk::imap4 {
lappend PUNKARGS [list {
@id -id ::punk::imap4::CAPABILITY
@cmd -name punk::imap4::CAPABILITY -help\
@cmd -name punk::imap4::CAPABILITY\
-summary\
"Send CAPABILITY command to the server to get its capabilities."\
-help\
"send CAPABILITY command to the server.
The cached results can be checked with the punk::imap4::has_capability command.
With no arguments has_capability will list all capabilities of the server.
@ -2780,7 +2807,10 @@ tcl::namespace::eval punk::imap4 {
lappend PUNKARGS [list {
@id -id ::punk::imap4::NOOP
@cmd -name punk::imap4::NOOP -help\
@cmd -name punk::imap4::NOOP\
-summary\
"Send NOOP command to the server."\
-help\
"NOOP command. May get information as untagged data.
The NOOP command always succeeds. It does nothing.
@ -2807,7 +2837,10 @@ tcl::namespace::eval punk::imap4 {
# CHECK. Flush to disk.
lappend PUNKARGS [list {
@id -id ::punk::imap4::CHECK
@cmd -name punk::imap4::CHECK -help\
@cmd -name punk::imap4::CHECK\
-summary\
"Request a checkpoint of the currently selected mailbox.(OBSOLETED)"\
-help\
"OBSOLETED in RFC9051.
NOOP should generally be used instead.
@ -2831,7 +2864,10 @@ tcl::namespace::eval punk::imap4 {
# the AUTH state.
lappend PUNKARGS [list {
@id -id ::punk::imap4::CLOSE
@cmd -name punk::imap4::CLOSE -help\
@cmd -name punk::imap4::CLOSE\
-summary\
"Close the currently selected mailbox, permanently removing messages with the \Deleted flag set."\
-help\
{The CLOSE command permanently removes all messages that have the
\Deleted flag set from the currently selected mailbox, and it returns
to the authenticated state from the selected state. No untagged
@ -2851,12 +2887,19 @@ tcl::namespace::eval punk::imap4 {
@leaders -min 1 -max 1
chan -optional 0
@opts
-force -type none -help\
"CLOSE is a dangerous command that will permanently remove messages with the \Deleted flag set.
Use -force to override this warning and proceed with the CLOSE command."
@values -min 0 -max 0
}]
proc CLOSE {args} {
set argd [punk::args::parse $args withid ::punk::imap4::CLOSE]
lassign [dict values $argd] leaders opts values received
set chan [dict get $leaders chan]
set chan [dict get $leaders chan]
set opt_force [dict exists $received -force]
if {!$opt_force} {
error "CLOSE is a dangerous command that will permanently remove messages with the \Deleted flag set.\n Use -force 1 to override this warning and proceed with the CLOSE command."
}
upvar ::punk::imap4::proto::info info
variable mboxinfo
@ -3298,7 +3341,7 @@ tcl::namespace::eval punk::imap4 {
# Search command.
proc SEARCH {chan args} {
if {![llength $args]} {
error "missing arguments. Usage: search chan arg ?arg ...?"
error "missing arguments. Usage: search chan arg ?arg?..."
}
punk::imap4::proto::requirestate $chan {SELECT EXAMINE}

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

@ -2190,6 +2190,7 @@ namespace eval punk::lib {
} else {
set qry $key
}
#pipeline - use punk patterns.
% thisval.= $qry= $dval
}
@ -2219,7 +2220,7 @@ namespace eval punk::lib {
string {
set hidekey 1
switch -- $key {
"%string" {
"%string" - "%str" {
set hidekey 1
set thisval $dval
}
@ -2231,7 +2232,9 @@ namespace eval punk::lib {
}
default {
switch -glob -- $key {
*lpad-* {
%XXXlpad-* {
#todo - remove
#moved to punk patterns
set hidekey 1
lassign [split $key -] _ extra
set width [expr {[textblock::width $dval] + $extra}]
@ -2255,7 +2258,10 @@ namespace eval punk::lib {
set width [expr {[textblock::width $dval] + [tcl::string::length $extra]}]
set thisval [textblock::pad $dval -which right -width $width -padchar $extra]
}
%split-* {
%XXXsplit-* {
#todo - remove
# moved to punk patterns.
#supported here by default branch.
#split on one or more chars - review
set hidekey 1
lassign [split $key -] _ splitchars
@ -2271,7 +2277,7 @@ namespace eval punk::lib {
if {[string index $key 0] ne "%"} {
set key %$key
}
#pipeline
#pipeline - use punk patterns.
% thisval.= $key= $thisval
}
}
@ -3250,7 +3256,7 @@ namespace eval punk::lib {
We will get something like 10+1 - which can be resolved safely with expr
"
@values -min 2 -max 2
datalength -type integer
datalength -type integer -range {0 ""}
index -type indexexpression
}
proc lindex_resolve {len index {base 0}} {
@ -3280,6 +3286,7 @@ namespace eval punk::lib {
#basic forward compatibility with integers such as 1_000 for 8.6.x
set index [tcl::string::map {_ {}} $index]
set len [tcl::string::map {_ {}} $len]
set base [tcl::string::map {_ {}} $base]
}
if {![string is integer -strict $len] || $len < 0} {
@ -3339,10 +3346,10 @@ namespace eval punk::lib {
return $based_max
}
} else {
#plain +-<int> already handled above.
#plain +-<int> already handled above. (but not +-<int>+-<int> etc)
#we are trying to avoid evaluating unbraced expr of potentially insecure origin
#regexp must split a++b to a + +b (not a+ + b) ie first +/- is the op
if {[regexp {([^+-]*)([+-])(.*)} $index _match a op b]} {
if {[regexp {([+-]{0,1}[^+-]*)([+-])(.*)} $index _match a op b]} {
if {[string is integer -strict $a] && [string is integer -strict $b]} {
if {$op eq "-"} {
set index [expr {$a - $b}]
@ -3374,6 +3381,16 @@ namespace eval punk::lib {
#[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
#REVIEW - we need compat for 1_000 etc to handle things like toml even in 8.6?
#A basic string map means we aren't properly validating
#todo - be stricter about malformations such as 1000_
if {![string is integer -strict 1_0]} {
#basic forward compatibility with integers such as 1_000 for 8.6.x
set index [tcl::string::map {_ {}} $index]
set len [tcl::string::map {_ {}} $len]
set base [tcl::string::map {_ {}} $base]
}
if {![string is integer -strict $len] || $len < 0} {
error "lindex_resolve_basic len must be an integer greater than or equal to zero"
}
@ -4196,6 +4213,7 @@ namespace eval punk::lib {
# important for pipeline & match_assign
# -line trimline|trimleft|trimright -block trimhead|trimtail|triminner|trimall|trimhead1|trimtail1|collateempty -commandprefix {string length} ?
# -block trimming only trims completely empty lines. use -line trimming to remove whitespace e.g -line trimright will clear empty lines without affecting leading whitespace on other lines that aren't pure whitespace
set linelist_body {
set usage "linelist ?-ansiresets auto|<bool>? ?-ansireplays 0|1? ?-line trimline|trimleft|trimright? ?-block trimhead|trimtail|triminner|trimall|trimhead1|trimtail1|collateempty? -commandprefix <cmdlist> text"
if {[llength $args] == 0} {
@ -4487,7 +4505,8 @@ namespace eval punk::lib {
}
#set newreplay [join $codestack ""]
set newreplay [punk::ansi::codetype::sgr_merge_list {*}$codestack]
#set newreplay [punk::ansi::codetype::sgr_merge_list {*}$codestack]
set newreplay [punk::ansi::codetype::sgr_merge $codestack]
if {$line_has_sgr && $newreplay ne $replaycodes} {
#adjust if it doesn't already does a reset at start
@ -4823,7 +4842,8 @@ namespace eval punk::lib {
}
#set newreplay [join $codestack ""]
set newreplay [punk::ansi::codetype::sgr_merge_list {*}$codestack]
#set newreplay [punk::ansi::codetype::sgr_merge_list {*}$codestack]
set newreplay [punk::ansi::codetype::sgr_merge $codestack]
if {$RST ne "" && $line_has_sgr && $newreplay ne $replaycodes} {
#adjust if it doesn't already does a reset at start
@ -4868,6 +4888,406 @@ namespace eval punk::lib {
set linelist_body [string map {<require_punk_ansi> "package require punk::ansi"} $linelist_body]
}
proc linelist {args} $linelist_body
set linelist_body2 {
set usage "linelist ?-ansiresets auto|<bool>? ?-ansireplays 0|1? ?-line trimline|trimleft|trimright? ?-block trimhead|trimtail|triminner|trimall|trimhead1|trimtail1|collateempty? -commandprefix <cmdlist> text"
if {[llength $args] == 0} {
error "linelist missing textchunk argument usage:$usage"
}
set text [lindex $args end]
set text [string map {\r\n \n} $text] ;#review - option?
set arglist [lrange $args 0 end-1]
set opts [tcl::dict::create\
-block {trimhead1 trimtail1}\
-line {}\
-commandprefix ""\
-ansiresets auto\
-ansireplays 0\
]
foreach {o v} $arglist {
switch -- $o {
-block - -line - -commandprefix - -ansiresets - -ansireplays {
tcl::dict::set opts $o $v
}
default {
error "linelist: Unrecognized option '$o' usage:$usage"
}
}
}
# -- --- --- --- --- ---
set opt_block [tcl::dict::get $opts -block]
if {[llength $opt_block]} {
foreach bo $opt_block {
switch -- $bo {
trimhead - trimtail - triminner - trimall - trimhead1 - trimtail1 - collateempty {}
default {
set known_blockopts [list trimhead trimtail triminner trimall trimhead1 trimtail1 collateempty]
error "linelist: unknown -block option value: $bo known values: $known_blockopts"
}
}
}
#normalize certain combos
if {"trimhead" in $opt_block && [set posn [lsearch $opt_block trimhead1]] >=0} {
set opt_block [lreplace $opt_block $posn $posn]
}
if {"trimtail" in $opt_block && [set posn [lsearch $opt_block trimtail1]] >=0} {
set opt_block [lreplace $opt_block $posn $posn]
}
if {"trimall" in $opt_block} {
#no other block options make sense in combination with this
set opt_block [list "trimall"]
}
#TODO
if {"triminner" in $opt_block } {
error "linelist -block triminner not implemented - sorry"
}
}
# -- --- --- --- --- ---
set opt_line [tcl::dict::get $opts -line]
set tl_left 0
set tl_right 0
set tl_both 0
foreach lo $opt_line {
switch -- $lo {
trimline {
set tl_both 1
}
trimleft {
set tl_left 1
}
trimright {
set tl_right 1
}
default {
set known_lineopts [list trimline trimleft trimright]
error "linelist: unknown -line option value: $lo known values: $known_lineopts"
}
}
}
#normalize trimleft trimright combo
if {$tl_left && $tl_right} {
set opt_line [list "trimline"]
set tl_both 1
}
# -- --- --- --- --- ---
set opt_commandprefix [tcl::dict::get $opts -commandprefix]
# -- --- --- --- --- ---
set opt_ansiresets [tcl::dict::get $opts -ansiresets]
# -- --- --- --- --- ---
set opt_ansireplays [tcl::dict::get $opts -ansireplays]
if {$opt_ansireplays} {
if {$opt_ansiresets eq "auto"} {
set opt_ansiresets 1
}
} else {
if {$opt_ansiresets eq "auto"} {
set opt_ansiresets 0
}
}
# -- --- --- --- --- ---
#set linelist [list]
#set nlsplit [split $text \n]
set linelist [split $text \n]
set original_length [llength $linelist]
#---------------------------
#todo - consider applying these inline later
if {![llength $opt_line]} {
#set linelist $nlsplit
#lappend linelist {*}$nlsplit
} else {
#already normalized trimleft+trimright to trimline
set nlsplit $linelist
#set linelist [list]
if {$tl_both} {
set i 0
foreach ln $linelist {
#lappend linelist [string trim $ln]
lset linelist $i [string trim $ln]
incr i
}
} elseif {$tl_left} {
set i 0
foreach ln $linelist {
#lappend linelist [string trimleft $ln]
lset linelist $i [string trimleft $ln]
incr i
}
} elseif {$tl_right} {
set i 0
foreach ln $nlsplit {
#lappend linelist [string trimright $ln]
lset linelist $i [string trimright $ln]
incr i
}
}
}
#---------------------------
set remove_indices [list]
if {"collateempty" in $opt_block} {
set last "-"
for {set i 0} {$i < $original_length} {incr i} {
if {[lindex $linelist $i] ne ""} {
set last "-"
} else {
if {$last ne ""} {
lappend remove_indices $i
set last ""
}
}
}
}
if {"trimall" in $opt_block} {
#we have already made sure there are no other block options that would conflict with this
#set linelist [lsearch -all -inline -not -exact $linelist[set linelist {}] ""]
#set remove_indices [list]
for {set i 0} {$i < $original_length} {incr i} {
if {[lindex $linelist $i] eq ""} {
lappend remove_indices $i
}
}
} else {
if {"trimhead" in $opt_block} {
#set remove_indices [list]
for {set i 0} {$i < $original_length} {incr i} {
if {[lindex $linelist $i] ne ""} {
break
} else {
lappend remove_indices $i
}
}
}
if {"trimtail" in $opt_block} {
set remove_indices [list]
for {set i [expr {$original_length-1}]} {$i >=0} {incr i -1} {
if {[lindex $linelist $i] ne ""} {
break
} else {
lappend remove_indices $i
}
}
#set revlinelist [lreverse $linelist][set linelist {}]
#set i 0
#foreach ln $revlinelist {
# if {$ln ne ""} {
# set linelist [lreverse [lrange $revlinelist $i end]]
# break
# }
# incr i
#}
}
# --- ---
set start 0
set end "end"
if {"trimhead1" in $opt_block} {
if {[lindex $linelist 0] eq ""} {
lappend remove_indices 0
}
}
if {"trimtail1" in $opt_block} {
if {[lindex $linelist end] eq ""} {
lappend remove_indices [expr {$original_length-1}]
}
}
#set linelist [lrange $linelist $start $end]
}
#review - we need to make sure ansiresets don't accumulate/grow on any line
#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} {
<require_punk_ansi> ;#package require punk::ansi
if {$opt_ansiresets} {
set RST "\x1b\[0m"
} else {
set RST ""
}
set replaycodes $RST ;#todo - default?
#set transformed [list]
#shortcircuit common case of no ansi
#NOTE: running ta::detect on a list (or dict) as a whole can be problematic if items in the list have backslash escapes due to Tcl list quoting and escaping behaviour.
#This commonly happens if there is an unbalanced brace (which is a normal occurrence and needs to be handled)
#ta::detect on a list of ansi-containing string may appear to work for some simple inputs but is not reliable
#detect_in_list/detectcode_in_list will check at first level. (not intended for detecting ansi in deeper structures)
#we use detectcode_in_list instead of detect_in_list
#detectcode_in_list will detect unclosed (or unopened) paired sequences such as PM (privacy message)
# - but the main reason is it is slightly faster.
if {![punk::ansi::ta::detectcode_in_list $linelist]} {
if {$opt_ansiresets} {
for {set i 0} {$i < $original_length} {incr i} {
if {$i in $remove_indices} {
continue
}
lset linelist $i $RST[lindex $linelist $i]$RST
}
}
} else {
#INLINE punk::ansi::codetype::is_sgr_reset
#regexp {\x1b\[0*m$} $code
set re_is_sgr_reset {\x1b\[0*m$}
#INLINE punk::ansi::codetype::is_sgr
#regexp {\033\[[0-9;:]*m$} $code
set re_is_sgr {\x1b\[[0-9;:]*m$}
#foreach ln $linelist {}
for {set i 0} {$i < $original_length} {incr i} {
if {$i in $remove_indices} {
continue
}
#set ln [lindex $linelist $i]
#set is_replay_pure_reset [regexp {\x1b\[0*m$} $replaycodes] ;#only looks at tail code - but if tail is pure reset - any prefix is ignorable
#set ansisplits [punk::ansi::ta::split_codes_single $ln] ;#REVIEW - this split accounts for a large portion of the time taken to run this function.
#get_codes_single lists only the codes. no plaintext or empty elements
set ansisplits [punk::ansi::ta::get_codes_single [lindex $linelist $i]] ;#REVIEW - this split accounts for a large portion of the time taken to run this function.
if {[llength $ansisplits] == 0} {
#plaintext only - no ansi codes in line
#lappend transformed [string cat $replaycodes $ln $RST]
lset linelist $i $replaycodes[lindex $linelist $i]$RST
#leave replaycodes as is for next line
set nextreplay $replaycodes
} else {
set tail $RST
set lastcode [lindex $ansisplits end] ;#may or may not be SGR
set lastcodeoffset [expr {[string length $lastcode]-1}]
if {[punk::ansi::codetype::is_sgr_reset $lastcode]} {
if {[string range [lindex $linelist $i] end-$lastcodeoffset end] eq $lastcode} {
#last plaintext is empty. So the line is already suffixed with a reset
set tail ""
} else {
#trailing text has been reset within line - but no tail reset present
#we normalize by putting a tail reset on anyway
set tail $RST
}
set nextreplay $RST
} elseif {[string range [lindex $linelist $i] end-$lastcodeoffset end] eq $lastcode && [punk::ansi::codetype::has_sgr_leadingreset $lastcode]} {
#code is at tail (no trailing plaintext)
#No tail reset - and no need to examine whole line to determine stack that is in effect
set tail $RST
set nextreplay $lastcode
} else {
#last codeset doesn't reset from earlier codes or isn't SGR - so we have to look at whole line to determine codes in effect
#last codeset doesn't end in a pure-reset
#whether code was at very end or not - add a reset tail
set tail $RST
#determine effective replay for line
set codestack [list start]
foreach code $ansisplits {
if {[tcl::string::index $code end] eq "m"} {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set codestack [list] ;#different from 'start' marked - this means we've had a reset
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set codestack [list $code]
} else {
if {[punk::ansi::codetype::is_sgr $code]} {
#todo - proper test of each code - so we only take latest background/foreground etc.
#requires handling codes with varying numbers of parameters.
#basic simplification - remove straight dupes.
set dup_posns [lsearch -all -exact $codestack $code] ;#!must use -exact as codes have square brackets which are interpreted as glob chars.
set codestack [lremove $codestack {*}$dup_posns]
lappend codestack $code
}
}
}
;#else gx0 or other code - we don't want to stack it with SGR codes
}
if {[llength $codestack] == 1 && [lindex $codestack 0] eq "start"} {
#No SGRs - may have been other codes
set line_has_sgr 0
} else {
#list is either empty or begins with start - empty means it had SGR reset - so it still invalidates current state of replaycodes
set line_has_sgr 1
if {[lindex $codestack 0] eq "start"} {
#set codestack [lrange $codestack 1 end]
ledit codestack 0 0
}
}
if {$line_has_sgr} {
#set newreplay [punk::ansi::codetype::sgr_merge_list {*}$codestack]
set newreplay [punk::ansi::codetype::sgr_merge $codestack]
if {$newreplay ne $replaycodes} {
#adjust if it doesn't already does a reset at start
if {$RST ne ""} {
if {[punk::ansi::codetype::has_sgr_leadingreset $newreplay]} {
set nextreplay $newreplay
} else {
set nextreplay $RST$newreplay
}
} else {
set nextreplay $newreplay
}
} else {
set nextreplay $replaycodes
}
} else {
set nextreplay $replaycodes
}
}
if {"$replaycodes$tail" ne ""} {
if {[punk::ansi::codetype::has_sgr_leadingreset [lindex $linelist $i]]} {
#no point attaching any replay
#lappend transformed [string cat $ln $tail]
if {$tail ne ""} {
lset linelist $i [lindex $linelist $i]$tail
}
} else {
#lappend transformed [string cat $replaycodes $ln $tail]
lset linelist $i $replaycodes[lindex $linelist $i]$tail
}
}
}
set replaycodes $nextreplay
}
#jjj
#set linelist $transformed
}
}
#todo - run this before ansireplay processing and adjust indices accordingly? or just run it after as is and accept that commandprefix will be added to each line after replay processing?
if {[llength $opt_commandprefix]} {
for {set i 0} {$i < $original_length} {incr i} {
if {$i in $remove_indices} {
continue
}
lset linelist $i [{*}$opt_commandprefix [lindex $linelist $i]]
}
#set transformed [list]
#foreach ln $linelist {
# lappend transformed [{*}$opt_commandprefix $ln]
#}
#set linelist $transformed
}
if {[llength $remove_indices]} {
set linelist [lremove $linelist {*}$remove_indices]
}
return $linelist
}
if {$has_punk_ansi} {
#optimise linelist as much as possible
set linelist_body2 [string map {<require_punk_ansi> ""} $linelist_body2]
} else {
#punk ansi not avail at time of package load.
#by putting in calls to punk::ansi the user will get appropriate error messages
set linelist_body2 [string map {<require_punk_ansi> "package require punk::ansi"} $linelist_body2]
}
proc linelist {args} $linelist_body2
interp alias {} errortime {} punk::lib::errortime

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

@ -950,6 +950,7 @@ tcl::namespace::eval ::punk::libunknown {
}
if {$has_prefix} {
set update [linsert $update end-$offset $new]
#end based index used with linsert - so can't replace with ledit.
} else {
lappend update $new
}

2
src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/modules/template_test-0.0.1.tm

@ -1,4 +1,4 @@
-*- tcl -*-
# -*- 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
#

8
src/modules/punk/mix/commandset/repo-999999.0a1.0.tm

@ -43,7 +43,7 @@ namespace eval punk::mix::commandset::repo {
lappend PUNKARGS [list {
@id -id ::punk::mix::commandset::repo::fossilize
@cmd -name punk::mix::commandset::repo::fossilize
@cmd -name punk::mix::commandset::repo::fossilize\
-summary\
"Initialise and check in a project to fossil (unimplemented)."\
-help\
@ -56,7 +56,7 @@ namespace eval punk::mix::commandset::repo {
lappend PUNKARGS [list {
@id -id ::punk::mix::commandset::repo::unfossilize
@cmd -name punk::mix::commandset::repo::unfossilize
@cmd -name punk::mix::commandset::repo::unfossilize\
-summary\
"Remove/archive .fossil (unimplemented)."\
-help\
@ -92,9 +92,9 @@ namespace eval punk::mix::commandset::repo {
#punk::args
lappend PUNKARGS [list {
@id -id ::punk::mix::commandset::repo::fossil-move-repository
@cmd -name punk::mix::commandset::repo::fossil-move-repository
@cmd -name punk::mix::commandset::repo::fossil-move-repository\
-summary\
"Move a fossil repository database file."\
"Interactively move a fossil repository database file."\
-help\
"Move the fossil repository file (usually named with .fossil extension).
This is an interactive function which will prompt for answers on stdin

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

@ -1170,22 +1170,24 @@ tcl::namespace::eval punk::ns {
#NOTE aliases may not be commands in current namespace - but we want to show them (marked red and with R)
#
set children [list]
set commands [list]
set exported [list]
set imported [list]
set aliases [list]
set procs [list]
set ensembles [list]
set ooclasses [list]
set ooobjects [list]
set children [list]
set packagetails [list]
set packageprefixes [list]
set commands [list]
set exported [list]
set imported [list]
set aliases [list]
set procs [list]
set ensembles [list]
set ooclasses [list]
set ooobjects [list]
set ooprivateobjects [list]
set ooprivateclasses [list]
set native [list]
set interps [list]
set coroutines [list]
set zlibstreams [list]
set usageinfo [list]
set native [list]
set interps [list]
set coroutines [list]
set zlibstreams [list]
set usageinfo [list]
if {![dict size $opt_nsdict]} {
set nsmatches [get_ns_dicts $fq_glob -allbelow 0]
@ -1216,6 +1218,8 @@ tcl::namespace::eval punk::ns {
package require overtype
if {"children" in $types} {
set children [dict get $contents children]
set packagetails [dict get $contents packagetails]
set packageprefixes [dict get $contents packageprefixes]
}
if {"commands" in $types} {
set commands [dict get $contents commands]
@ -1368,12 +1372,26 @@ tcl::namespace::eval punk::ns {
set c_ooC [a+ term-cornflowerblue] ;#privateClass
set c_zst [a+ term-yellow] ;#zlibstreams
set a1 [a][a+ cyan]
set a1 [a][a+ cyan] ;#child namespace SGR code.
foreach ch1 $children1 ch2 $children2 cmd1 $elements1 cmd2 $elements2 cmd3 $elements3 cmd4 $elements4 {
set c1 [a+ white]
set c2 [a+ white]
set c3 [a+ white]
set c4 [a+ white]
foreach nsvar {ch1 ch2} {
set v [set $nsvar]
if {$v in $packagetails} {
#may also be a packageprefix.
if {$v in $packageprefixes} {
set $nsvar [a+ underdouble]$v
} else {
#just a package - no prefix - we want to underline but not doubled
set $nsvar [a+ underline]$v
}
} elseif {$v in $packageprefixes} {
set $nsvar [a+ underdotted]$v
}
}
for {set i 1} {$i <= 4} {incr i} {
if {[llength [set cmd$i]]} {
@ -1441,7 +1459,7 @@ tcl::namespace::eval punk::ns {
}
#lappend displaylist $a1[overtype::left $col1 $ch1][a+]$a1[overtype::left $col2 $ch2][a+]$c1[overtype::left $col3 $cmd1][a+]$c2[overtype::left $col4 $cmd2][a+]$c3[overtype::left $col5 $cmd3][a+]$c4$cmd4[a+]
lappend displaylist $a1[overtype::left $col1 $ch1][a]$a1[overtype::left $col2 $ch2][a]$c1[overtype::left $col3 $cmd1][a]$c2[overtype::left $col4 $cmd2][a]$c3[overtype::left $col5 $cmd3][a]$c4$cmd4[a]
lappend displaylist $a1[overtype::left $col1 $ch1[a]][a]$a1[overtype::left $col2 $ch2][a]$c1[overtype::left $col3 $cmd1][a]$c2[overtype::left $col4 $cmd2][a]$c3[overtype::left $col5 $cmd3][a]$c4$cmd4[a]
}
return [list_as_lines $displaylist]
@ -3043,8 +3061,11 @@ y" {return quirkykeyscript}
set nspathcommands [dict get $opts -nspathcommands]
# -- --- --- --- --- --- --- --- --- --- --- ---
set packagetails [list] ;#child namespaces which are an exact match for a package name
set packageprefixes [list] ;#child namespaces which are a prefix match for a package name - but not an exact match
#set location [nsprefix $fq_glob]
set commands [list]
set commands [list]
set nsglob [nsprefix $fq_glob]
set glob [nstail $fq_glob]
@ -3471,10 +3492,27 @@ y" {return quirkykeyscript}
# set childtailmatches [lsort $childtailmatches]
#}
set childtailmatches [lsort -dictionary $childtailmatches]
foreach ct $childtailmatches {
set fqchild [nsjoin $location $ct]
set searchname [string trimleft $fqchild :]
foreach pkgname [lsearch -all -inline [package names] $searchname*] {
if {$pkgname eq $searchname} {
#exact match.
lappend packagetails $ct
} else {
if {[string match ${searchname}::* $pkgname]} {
#prefix match - but not exact match
lappend packageprefixes $ct
}
}
}
}
set nsdict [dict create\
location $location\
children $childtailmatches\
packagetails $packagetails\
packageprefixes $packageprefixes\
commands $commands\
procs $procs\
exported $exported\
@ -4807,7 +4845,8 @@ y" {return quirkykeyscript}
set scriptcmd [dict get $scriptinfo which]
set scriptargs [lrange $origin 1 end]
#ledit args -1 -1 {*}$scriptargs ;#prepend
set args [linsert $args 1 {*}$scriptargs]
#set args [linsert $args 1 {*}$scriptargs]
ledit args 1 -1 {*}$scriptargs ;#insert scriptargs before arg at index 1
#JJJ review
#set resolvedargs $scriptargs
punk::args::update_definitions [list [namespace qualifiers $scriptcmd]]
@ -5240,7 +5279,7 @@ y" {return quirkykeyscript}
the synopsis for that form.
"
@opts
-form -type string -default * -help\
-form -type number|name -default * -help\
"Ordinal index or name of command form."
-return -type string -default full -choices {full summary dict}
@values -min 1 -max -1
@ -5291,7 +5330,7 @@ y" {return quirkykeyscript}
full - summary {
set resultstr ""
foreach synline [split $syn \n] {
if {[string range $synline 0 1] eq "# "} {
if {[string range $synline 0 1] in {"# " "##"}} {
append resultstr $synline \n
} else {
#puts stderr [textblock::frame $syn]
@ -5447,9 +5486,9 @@ y" {return quirkykeyscript}
}
if {$opt_grepstr ne ""} {
if {[llength $opt_grepstr] == 1} {
set result [punk::ansi::grepstr --ignore-case -returnlines all [lindex $opt_grepstr 0] $result]
set result [punk::ansi::grepstr --ignore-case -return all [lindex $opt_grepstr 0] $result]
} else {
set result [punk::ansi::grepstr --ignore-case -returnlines all -highlight [lrange $opt_grepstr 1 end] [lindex $opt_grepstr 0] $result]
set result [punk::ansi::grepstr --ignore-case -return all -highlight [lrange $opt_grepstr 1 end] [lindex $opt_grepstr 0] $result]
}
}
return $result
@ -5529,9 +5568,9 @@ y" {return quirkykeyscript}
}
if {$opt_grepstr ne ""} {
if {[llength $opt_grepstr] == 1} {
set result [punk::ansi::grepstr --ignore-case -returnlines all [lindex $opt_grepstr 0] $result]
set result [punk::ansi::grepstr --ignore-case -return all [lindex $opt_grepstr 0] $result]
} else {
set result [punk::ansi::grepstr --ignore-case -returnlines all -highlight [lrange $opt_grepstr 1 end] [lindex $opt_grepstr 0] $result]
set result [punk::ansi::grepstr --ignore-case -return all -highlight [lrange $opt_grepstr 1 end] [lindex $opt_grepstr 0] $result]
}
}
return $result
@ -6674,7 +6713,7 @@ y" {return quirkykeyscript}
set body [punk::ansi::grepstr -return all -highlight term-teal {^(\{)|[^\\](\{+)} $body]
set body [punk::ansi::grepstr -return all -highlight term-teal {[^\\](\}+)} $body]
set body [punk::ansi::grepstr -return all -highlight term-orange {\[|\]} $body]
set body [punk::ansi::grepstr -return all -highlight term-orange1 {\[|\]} $body]
}
default {
set is_highlighted 0

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

@ -1078,7 +1078,8 @@ namespace eval punk::repl::class {
# incr nextrow -1
#}
#set o_rendered_lines [linsert $o_rendered_lines $cursor_row_idx ""]
ledit o_rendered_lines $cursor_row_idx $cursor_row_idx-1 ""
#ledit o_rendered_lines $cursor_row_idx $cursor_row_idx-1 ""
ledit o_rendered_lines $cursor_row_idx -1 ""
set o_cursor_col 1
}
@ -1151,7 +1152,9 @@ namespace eval punk::repl::class {
lappend o_rendered_lines ""
set activeline ""
}
lset o_rendered_lines $cursor_row_idx $result
#JULZ
#lset o_rendered_lines $cursor_row_idx $result
lset o_rendered_lines $cursor_row_idx $result\x1b[m
incr i
}
@ -1289,7 +1292,9 @@ namespace eval punk::repl::class {
set charhighlight [punk::ansi::a+ reverse]$char_at_cursor[a]
}
set cursorline [overtype::renderline -transparent 1 -insert_mode 0 -expand_right 0 $cursorline $prefix$charhighlight$suffix]
lset lines $o_cursor_row-1 $cursorline
#JULZ
#lset lines $o_cursor_row-1 $cursorline
lset lines $o_cursor_row-1 $cursorline\x1b[m
}
set numcol "$ANSI_linenum[join $nums \n][a]"
@ -1765,7 +1770,7 @@ proc punk::repl::console_debugview {editbuf consolewidth args} {
set patch_height [expr {2 + $debug_height + 2}]
set spacepatch [textblock::block $debug_width $patch_height " "]
#puts -nonewline [punk::ansi::cursor_off]
punk::console::cursor_off
#punk::console::cursor_off
#use non cursorsave versions - cursor save/restore will interfere with any concurrent ansi rendering that uses save/restore - because save/restore is a single item, not a stack.
set debug_offset [expr {$consolewidth - $debug_width - $opt_rightmargin}]
set row_clear [expr {$opt_row -2}]
@ -1773,7 +1778,7 @@ proc punk::repl::console_debugview {editbuf consolewidth args} {
punk::console::move_emitblock_return $opt_row $debug_offset $info
set topleft [list $debug_offset $opt_row] ;#col,row REVIEW
#puts -nonewline [punk::ansi::cursor_on]
punk::console::cursor_on
#punk::console::cursor_on
flush stdout
return [dict create width $debug_width height $debug_height topleft $topleft]
@ -2000,8 +2005,12 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
#if {$chunk eq "\x1b\[C"} {
#}
punk::console::cursor_off
flush stdout
$editbuf add_chunk $chunk
#--------------------------
# editbuf and debugview rhs frames
#for now disable entirely on vt52 - we can only do cursor save restore - nothing that requires responses on stdin (?)
@ -2058,7 +2067,9 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
flush stdout
#move_column is more efficient than move since it doesn't require a response on stdin to determine current column,
#but doesn't seem to be universally supported (kermit95 vt modes for example)
#the Horizontal Position Absolute sequence ESC \[ n ` seems to be a possible alternative.
set leftmargin 3
if {!$is_vt52} {
puts -nonewline stdout [a+ cyan][punk::ansi::move_column [expr {$leftmargin +1}]][punk::ansi::erase_eol][$editbuf line $cursor_row][a][punk::ansi::move_column [expr {$leftmargin + [$editbuf cursor_column]}]]
@ -2089,6 +2100,9 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
lappend input_chunks_waiting($inputchan) $waiting
}
}
punk::console::cursor_on
flush stdout
if {$editbuf_linenum_submitted == 0} {
#(there is no line 0 - lines start at 1)
if {[$editbuf last_char] eq "\n"} {
@ -2685,8 +2699,10 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
#editbuf
#----------------------------------------------------------------------------
#after any external command - raw mode as the console sees it can be disabled
#set it to match current state of the tsv
#----------------------------------------------------------------------------
if {[tsv::get console is_raw]} {
if {$::tcl_platform(platform) eq "windows"} {
#review
@ -2696,22 +2712,24 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
set sinfo [chan configure stdin]
if {[dict exists $sinfo -inputmode]} {
if {[dict get $sinfo -inputmode] ne "raw"} {
set re_enable_required 1
set re_enable_raw_required 1
} else {
set re_enable_required 0
set re_enable_raw_required 0
}
} else {
# -inputmode unavailable
#tcl 8.6 doesn't have -inputmode - meaning it has to call punk:console::enableRaw each time
#enableRaw on windows without twapi involves launching a pwsh process - which gives a noticeable lag in keyboard input.
#enableRaw on Unix involves a call to stty - which is generally fast - but still to be avoided if not required.
set re_enable_required 1
set re_enable_raw_required 1
}
#puts stderr "-here- re-enabling raw"
if {$re_enable_required} {
if {$re_enable_raw_required} {
punk::console::enableRaw
}
}
#----------------------------------------------------------------------------
} else {
#append commandstr \n
if {$::punk::repl::signal_control_c} {
@ -3801,7 +3819,8 @@ namespace eval repl {
#puts stderr [thread::id]
if {[llength $::codethread_initstatus] == 1} {
set ::codethread_initstatus [linsert $::codethread_initstatus 0 ok]
#set ::codethread_initstatus [linsert $::codethread_initstatus 0 ok]
ledit ::codethread_initstatus 0 -1 ok
}
thread::id
}

4
src/modules/punk/repo-999999.0a1.0.tm

@ -249,7 +249,7 @@ namespace eval punk::repo {
@form -form "parsed"
${[punk::repo::get_fossil_subcommand_usage add]}
@form -form "raw" -synopsis "exec fossil add ?OPTIONS? FILE1 ?FILE2 ...?"
@form -form "raw" -synopsis "exec fossil add \[OPTIONS\] FILE1 \[FILE2\]..."
@formdisplay -header "fossil help add" -body {${[runout -n fossil help add]}}
} ""]
@ -263,7 +263,7 @@ namespace eval punk::repo {
@form -form "parsed"
${[punk::repo::get_fossil_subcommand_usage diff]}
@form -form "raw" -synopsis "exec fossil diff ?OPTIONS? FILE1 ?FILE2 ...?"
@form -form "raw" -synopsis "exec fossil diff \[OPTIONS\] FILE1 \[FILE2\]..."
@formdisplay -header "fossil help diff" -body {${[runout -n fossil help diff]}}
} ""]

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

@ -1386,7 +1386,7 @@ tcl::namespace::eval punk::safe::system {
return -code error "permission denied"
}
if {$got(--)} {
set cmd [linsert $cmd end-1 -directory $dir]
set cmd [linsert $cmd end-1 -directory $dir];# end-relative insert - cannot replace with ledit
} else {
lappend cmd -directory $dir
}

4
src/modules/punkcheck-0.1.0.tm

@ -324,7 +324,7 @@ namespace eval punkcheck {
lappend record_list $o_fileset_record
} else {
#set record_list [linsert $record_list[unset record_list] $oldposition $o_fileset_record]
ledit record_list $oldposition $oldposition-1 $o_fileset_record
ledit record_list $oldposition -1 $o_fileset_record
}
if {$o_operation ne "QUERY"} {
punkcheck::save_records_to_file $record_list $punkcheck_file
@ -796,7 +796,7 @@ namespace eval punkcheck {
lappend record_list $file_record
} else {
#set record_list [linsert $record_list[unset record_list] $oldposition $file_record]
ledit record_list $oldposition $oldposition-1 $file_record
ledit record_list $oldposition -1 $file_record
}
save_records_to_file $record_list $punkcheck_file

42
src/modules/shellfilter-999999.0a1.0.tm

@ -755,6 +755,8 @@ namespace eval shellfilter::chan {
#puts stdout "===[ansistring VIEW -lf 1 $o_buffered]"
set buf $o_buffered$chunk
set emit ""
#Note 8-bit csi \x9b has already been mapped in the chunk to 7-bit form \x1b\[ by the caller - so we only need to check for \x1b here
#(under review - ideally we might not want to normalize 8-bit to 7-bit in a channel transform))
if {[string last \x1b $buf] >= 0} {
#detect will detect ansi SGR and gron groff and other codes
#REVIEW - ta::detect won't detect SOS without paired ST for things like PM
@ -798,18 +800,21 @@ namespace eval shellfilter::chan {
] $c1c2] 0 3]
switch -- $leadernorm {
7CSI - 8CSI {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set o_codestack [list "\x1b\[m"]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set o_codestack [list $code]
} elseif {[punk::ansi::codetype::is_sgr $code]} {
#todo - make caching is_sgr method
set dup_posns [lsearch -all -exact $o_codestack $code]
set o_codestack [lremove $o_codestack {*}$dup_posns]
lappend o_codestack $code
} else {
set code_endswith_m [expr {[tcl::string::index $code end] eq "m"}]
if {$code_endswith_m} {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
#review this normalizing of reset to a single form.
set o_codestack [list "\x1b\[m"]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set o_codestack [list $code]
} elseif {[punk::ansi::codetype::is_sgr $code]} {
#todo - make caching is_sgr method
set dup_posns [lsearch -all -exact $o_codestack $code]
set o_codestack [lremove $o_codestack {*}$dup_posns]
lappend o_codestack $code
}
}
}
7GFX {
switch -- [tcl::string::index $code 2] {
@ -1029,6 +1034,21 @@ namespace eval shellfilter::chan {
return ""
}
}
#------------------------------------------------------
# REVIEW
#Trackcodes logic is primarily designed for 7-bit codes
#It would be complex for it to support 8-bit as well
#- we can do a simple pre-map to convert 8-bit CSI to 7-bit CSI before processing
#we already normalize things like resets to a single 7-bit form anyway.
#review - is there a need for an ansiwrap channel that preserves 8-bit codes?
#8-bit are rarely used these days - and many terminals don't support them.
#We could take the view here that we should understand them but not emit them in general.
#Nonetheless - converting them on a channel transform like this is potentially suprising in some circumstances,
#and we don't necessarily know the intent of both the producer and consumer of the stream.
set stringdata [string map [list \x9b \x1b\[ ] $stringdata]
#------------------------------------------------------
set streaminfo [my Trackcodes $stringdata]
set emit [dict get $streaminfo emit]

35
src/modules/test/#modpod-overtype-999999.0a1.0/overtype-1.7.4_testsuites/overtype/renderline.test

@ -0,0 +1,35 @@
package require tcltest
namespace eval ::testspace {
namespace import ::tcltest::*
variable common {
set result ""
}
test renderline_basic_noansi {basic renderline calls with no ansi in underlay or overlay}\
-setup $common -body {
set undertext "abcdefghij"
#there must be no ansi codes in the output (e.g no resets introduced by renderline))
set editedline [overtype::renderline -insert_mode 0 $undertext ABCDE]
#set lineview [ansistring VIEW $editedline]
lappend result $editedline
set editedline [overtype::renderline -insert_mode 1 $undertext ABCDE]
lappend result $editedline
}\
-cleanup {
}\
-result [list\
ABCDEfghij ABCDEabcde
]
#todo - test
#P% overtype::left -transparent 1 [textblock::block 10 2 -] " [a+ underline yellow].\n [a+ underline yellow]yyy"
#- --.-------
#- --yyy-----
#we expect the dot to be yellow and underlined and the yyy to be yellow and underlined - but not the dashes.
}

0
src/modules/test/#modpod-overtype-999999.0a1.0/overtype-1.7.4_testsuites/tests/renderline.test#..+overtype+renderline.test.fauxlink

139
src/modules/test/#modpod-overtype-999999.0a1.0/overtype-999999.0a1.0.tm

@ -0,0 +1,139 @@
# -*- 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) 2025
#
# @@ Meta Begin
# Application test::overtype 999999.0a1.0
# Meta platform tcl
# Meta license MIT
# @@ Meta End
package require Tcl 8.6-
tcl::namespace::eval test::overtype {
variable PUNKARGS
variable pkg test::overtype
variable version
set version 999999.0a1.0
package require packagetest
packagetest::makeAPI test::overtype $version overtype; #will package provide test::overtype $version
package forget overtype
package require overtype
}
# == === === === === === === === === === === === === === ===
# Sample 'about' function with punk::args documentation
# == === === === === === === === === === === === === === ===
tcl::namespace::eval test::overtype {
tcl::namespace::export {[a-z]*} ;# Convention: export all lowercase
variable PUNKARGS
variable PUNKARGS_aliases
lappend PUNKARGS [list {
@id -id "(package)test::overtype"
@package -name "test::overtype" -help\
"Test suites for overtype module"
}]
namespace eval argdoc {
#namespace for custom argument documentation
proc package_name {} {
return test::overtype
}
proc about_topics {} {
#info commands results are returned in an arbitrary order (like array keys)
set topic_funs [info commands [namespace current]::get_topic_*]
set about_topics [list]
foreach f $topic_funs {
set tail [namespace tail $f]
lappend about_topics [string range $tail [string length get_topic_] end]
}
#Adjust this function or 'default_topics' if a different order is required
return [lsort $about_topics]
}
proc default_topics {} {return [list Description *]}
# -------------------------------------------------------------
# get_topic_ functions add more to auto-include in about topics
# -------------------------------------------------------------
proc get_topic_Description {} {
punk::args::lib::tstr [string trim {
package test::overtype
test suite for overtype module
} \n]
}
proc get_topic_License {} {
return "MIT"
}
proc get_topic_Version {} {
return "$::test::overtype::version"
}
proc get_topic_Contributors {} {
set authors {{<julian@precisium.com> Julian Noble}}
set contributors ""
foreach a $authors {
append contributors $a \n
}
if {[string index $contributors end] eq "\n"} {
set contributors [string range $contributors 0 end-1]
}
return $contributors
}
proc get_topic_custom-topic {} {
punk::args::lib::tstr -return string {
A custom
topic
etc
}
}
# -------------------------------------------------------------
}
# we re-use the argument definition from punk::args::standard_about and override some items
set overrides [dict create]
dict set overrides @id -id "::test::overtype::about"
dict set overrides @cmd -name "test::overtype::about"
dict set overrides @cmd -help [string trim [punk::args::lib::tstr {
About test::overtype module
}] \n]
dict set overrides topic -choices [list {*}[test::overtype::argdoc::about_topics] *]
dict set overrides topic -choicerestricted 1
dict set overrides topic -default [test::overtype::argdoc::default_topics] ;#if -default is present 'topic' will always appear in parsed 'values' dict
set newdef [punk::args::resolved_def -antiglobs -package_about_namespace -override $overrides ::punk::args::package::standard_about *]
lappend PUNKARGS [list $newdef]
proc about {args} {
package require punk::args
#standard_about accepts additional choices for topic - but we need to normalize any abbreviations to full topic name before passing on
set argd [punk::args::parse $args withid ::test::overtype::about]
lassign [dict values $argd] _leaders opts values _received
punk::args::package::standard_about -package_about_namespace ::test::overtype::argdoc {*}$opts {*}[dict get $values topic]
}
}
# end of sample 'about' function
# == === === === === === === === === === === === === === ===
# -----------------------------------------------------------------------------
# register namespace(s) to have PUNKARGS,PUNKARGS_aliases variables checked
# -----------------------------------------------------------------------------
# variable PUNKARGS
# variable PUNKARGS_aliases
namespace eval ::punk::args::register {
#use fully qualified so 8.6 doesn't find existing var in global namespace
lappend ::punk::args::register::NAMESPACES ::test::overtype
}
# -----------------------------------------------------------------------------
package provide test::overtype [tcl::namespace::eval test::overtype {
variable pkg test::overtype
variable version
set version 999999.0a1.0
}]
## Ready
return

3
src/modules/test/overtype-buildversion.txt

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

158
src/modules/test/punk/#modpod-ansi-999999.0a1.0/ansi-0.1.1_testsuites/ansi/ansistrip.test

@ -1,80 +1,80 @@
package require tcltest
namespace eval ::testspace {
namespace import ::tcltest::*
variable common {
set result ""
}
test ansistrip_basic_sgr_strip {test ansistrip on basic SGR colour code and reset}\
-setup $common -body {
set a "\x1b\[31mxxx\x1b\[myyy" ;# set a [a+ red]xxx[a]yyy
lappend result [punk::ansi::ansistrip $a]
}\
-cleanup {
}\
-result [list\
xxxyyy
]
test ansistrip_nonansi_escape {test ansistrip on non-ansi ESC}\
-setup $common -body {
set a \x1bxxx ;#not an SGR or other known ansi sequence - should pass through
set b [punk::ansi::ansistrip $a]
lappend result [string equal $a $b]
}\
-cleanup {
}\
-result [list\
1
]
test ansistrip_movement {test ansistrip on ANSI move}\
-setup $common -body {
set a X\x1b\[2\;2HY ;#not an SGR or other known ansi sequence - should pass through
#equivalent to : set a X[move 2 2]Y
lappend result [punk::ansi::ansistrip $a]
}\
-cleanup {
}\
-result [list\
XY
]
test ansistrip_privacymessage_7bit {test ansistrip on a 7bit privacymessage strips entire pm}\
-setup $common -body {
#regardless of whether various terminals display the PM contents or not - this is required to be stripped here.
set a "7bit secret \x1b^UN\x1b\\safe"
#equivalent to : set a X[move 2 2]Y
lappend result [punk::ansi::ansistrip $a]
}\
-cleanup {
}\
-result [list\
"7bit secret safe"
]
test ansistrip_privacymessage_8bit {test ansistrip on a 8bit privacymessage strips entire pm}\
-setup $common -body {
#regardless of whether various terminals display the PM contents or not - this is required to be stripped here.
set a "8bit secret \x9eUN\x9csafe"
#equivalent to : set a X[move 2 2]Y
lappend result [punk::ansi::ansistrip $a]
}\
-cleanup {
}\
-result [list\
"8bit secret safe"
]
test ansistrip_converts_vt100_gx {test ansistrip converts vt100 graphical symbols to unicode equivalent}\
-setup $common -body {
set a "\x1b(0|\x1b(B" ;#equivalent [punk::ansi::g0 |]
lappend result [punk::ansi::ansistrip $a] ;#unicode not-equal symbol \u2260
}\
-cleanup {
}\
-result [list\
\u2260
]
package require tcltest
namespace eval ::testspace {
namespace import ::tcltest::*
variable common {
set result ""
}
test ansistrip_basic_sgr_strip {test ansistrip on basic SGR colour code and reset}\
-setup $common -body {
set a "\x1b\[31mxxx\x1b\[myyy" ;# set a [a+ red]xxx[a]yyy
lappend result [punk::ansi::ansistrip $a]
}\
-cleanup {
}\
-result [list\
xxxyyy
]
test ansistrip_nonansi_escape {test ansistrip on non-ansi ESC}\
-setup $common -body {
set a \x1bxxx ;#not an SGR or other known ansi sequence - should pass through
set b [punk::ansi::ansistrip $a]
lappend result [string equal $a $b]
}\
-cleanup {
}\
-result [list\
1
]
test ansistrip_movement {test ansistrip on ANSI move}\
-setup $common -body {
set a X\x1b\[2\;2HY ;#not an SGR or other known ansi sequence - should pass through
#equivalent to : set a X[move 2 2]Y
lappend result [punk::ansi::ansistrip $a]
}\
-cleanup {
}\
-result [list\
XY
]
test ansistrip_privacymessage_7bit {test ansistrip on a 7bit privacymessage strips entire pm}\
-setup $common -body {
#regardless of whether various terminals display the PM contents or not - this is required to be stripped here.
set a "7bit secret \x1b^UN\x1b\\safe"
#equivalent to : set a X[move 2 2]Y
lappend result [punk::ansi::ansistrip $a]
}\
-cleanup {
}\
-result [list\
"7bit secret safe"
]
test ansistrip_privacymessage_8bit {test ansistrip on a 8bit privacymessage strips entire pm}\
-setup $common -body {
#regardless of whether various terminals display the PM contents or not - this is required to be stripped here.
set a "8bit secret \x9eUN\x9csafe"
#equivalent to : set a X[move 2 2]Y
lappend result [punk::ansi::ansistrip $a]
}\
-cleanup {
}\
-result [list\
"8bit secret safe"
]
test ansistrip_converts_vt100_gx {test ansistrip converts vt100 graphical symbols to unicode equivalent}\
-setup $common -body {
set a "\x1b(0|\x1b(B" ;#equivalent [punk::ansi::g0 |]
lappend result [punk::ansi::ansistrip $a] ;#unicode not-equal symbol \u2260
}\
-cleanup {
}\
-result [list\
\u2260
]
}

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

File diff suppressed because it is too large Load Diff

388
src/modules/test/punk/#modpod-args-999999.0a1.0/args-0.1.5_testsuites/args/choices.test

@ -1,195 +1,195 @@
package require tcltest
namespace eval ::testspace {
namespace import ::tcltest::*
variable common {
set result ""
}
test choices_typeignored_when_choice_in_list {Test that -type is not validated for a value that matches a choice}\
-setup $common -body {
#1 abbreviated choice
set argd [punk::args::parse {li} withdef @values {frametype -type dict -choices {heavy light arc}}]
lappend result [dict get $argd values]
#2 exact match for a choice
set argd [punk::args::parse {light} withdef @values {frametype -type dict -choices {heavy light arc}}]
lappend result [dict get $argd values]
}\
-cleanup {
}\
-result [list\
{frametype light}\
{frametype light}\
]
test choices_type_validation_choicerestricted1 {Test that -type is validated for value outside of choicelist based on -choicerestricted}\
-setup $common -body {
set argd [punk::args::parse {11} withdef @values {frametype -type int -choicerestricted 0 -choices {heavy light arc}}]
lappend result [dict get $argd values]
if {[catch {
punk::args::parse {z} withdef @values {frametype -type int -choicerestricted 0 -choices {heavy light arc}}
}]} {
lappend result "ok_got_expected_error1"
} else {
lappend result "missing_required_error_when_type_mismatch_for_choice_outside_list"
}
#when -choicerestricted - value matching -type still shouldn't pass
if {[catch {
set argd [punk::args::parse {11} withdef @values {frametype -type int -choicerestricted 1 -choices {heavy light arc}}]
}]} {
lappend result "ok_got_expected_error2"
} else {
lappend result "missing_required_error_when_choicerestricted_and_choice_outside_list"
}
}\
-cleanup {
}\
-result [list\
{frametype 11}\
ok_got_expected_error1\
ok_got_expected_error2\
]
test choices_type_validation_choicerestricted2 {Test that -type dict is validated for value outside of choicelist based on -choicerestricted}\
-setup $common -body {
#same as choices_type_validation_choicrestricted1 - but with a more complex type 'dict' - tests list protection is correct
set argd [punk::args::parse {{hl -}} withdef @values {frametype -type dict -choicerestricted 0 -choices {heavy light arc}}]
lappend result [dict get $argd values]
if {[catch {
punk::args::parse {z} withdef @values {frametype -type dict -choicerestricted 0 -choices {heavy light arc}}
}]} {
lappend result "ok_got_expected_error1"
} else {
lappend result "missing_required_error_when_type_mismatch_for_choice_outside_list"
}
#when -choicerestricted - value matching -type dict still shouldn't pass
if {[catch {
set argd [punk::args::parse {{hl -}} withdef @values {frametype -type dict -choicerestricted 1 -choices {heavy light arc}}]
}]} {
lappend result "ok_got_expected_error2"
} else {
lappend result "missing_required_error_when_choicerestricted_and_choice_outside_list"
}
}\
-cleanup {
}\
-result [list\
{frametype {hl -}}\
ok_got_expected_error1\
ok_got_expected_error2\
]
test choice_multiple_with_choiceprefix {test -choices with both -multiple and -choiceprefix}\
-setup $common -body {
#test with full value choices.
set argd [punk::args::parse {license description} withdef @values {topic -choices {license contributors description} -choiceprefix 1 -multiple 1 }]
lappend result [dict get $argd values]
#test with prefixes of choice.
set argd [punk::args::parse {lic desc} withdef @values {topic -choices {license contributors description} -choiceprefix 1 -multiple 1 }]
lappend result [dict get $argd values]
#test with mixes of full value and prefix of choice.
set argd [punk::args::parse {license desc} withdef @values {topic -choices {license contributors description} -choiceprefix 1 -multiple 1 }]
lappend result [dict get $argd values]
set argd [punk::args::parse {desc license} withdef @values {topic -choices {license contributors description} -choiceprefix 1 -multiple 1 }]
lappend result [dict get $argd values]
}\
-cleanup {
}\
-result [list\
{topic {license description}}\
{topic {license description}}\
{topic {license description}}\
{topic {description license}}\
]
#todo -nocase tests
test choice_multiple_multiple {test -choices with both -multiple and -choicemultiple}\
-setup $common -body {
set argd [punk::args::parse {a {c a} {a b c}} withdef @values {X -type string -choices {aa bb cc} -multiple 1 -choicemultiple {1 3} -optional 1}]
lappend result [dict get $argd values]
}\
-cleanup {
}\
-result [list\
{X {aa {cc aa} {aa bb cc}}}
]
# -choicemultiple allows duplicates in result by default (default for -choicemultipleunique 0)
test choicemultiple_list {test -choices with both -multiple and -choicemultiple}\
-setup $common -body {
set argd [punk::args::parse {{read write w}} withdef @values {mode -type list -choices {read write} -choicemultiple {1 -1}}]
lappend result [dict get $argd values]
}\
-cleanup {
}\
-result [list\
{mode {read write write}}
]
test choice_multielement_clause {test -choice with a clause-length greater than 1}\
-setup $common -body {
#The same -choices list always applies to each member of -type - which isn't always ideal for a multi-element clause
#for a clause where each element has a different choiceset - we would need to introduce a more complex -typechoices option
#(or use a -parsekey mechanism on leaders/values to group them)
#test all combinations of prefix and complete for 2 entries
set argd [punk::args::parse {light heavy} withdef @values {leftright -type {any any} -choices {light heavy} -choicerestricted 1}]
lappend result [dict get $argd values]
set argd [punk::args::parse {li heavy} withdef @values {leftright -type {any any} -choices {light heavy} -choicerestricted 1}]
lappend result [dict get $argd values]
set argd [punk::args::parse {li he} withdef @values {leftright -type {any any} -choices {light heavy} -choicerestricted 1}]
lappend result [dict get $argd values]
set argd [punk::args::parse {light he} withdef @values {leftright -type {any any} -choices {light heavy} -choicerestricted 1}]
lappend result [dict get $argd values]
}\
-cleanup {
}\
-result [list\
{leftright {light heavy}}\
{leftright {light heavy}}\
{leftright {light heavy}}\
{leftright {light heavy}}\
]
test choice_multielement_clause_unrestricted {test -choice with a clause-length greater than 1 and values outside of choicelist}\
-setup $common -body {
#1 both values outside of -choices
set argd [punk::args::parse {11 x} withdef @values {leftright -type {int char} -choices {light heavy arc} -choicerestricted 0}]
lappend result [dict get $argd values]
#
set argd [punk::args::parse {11 arc} withdef @values {leftright -type {int char} -choices {light heavy arc} -choicerestricted 0}]
lappend result [dict get $argd values]
#
set argd [punk::args::parse {11 a} withdef @values {leftright -type {int char} -choices {light heavy arc} -choicerestricted 0}]
lappend result [dict get $argd values]
#
set argd [punk::args::parse {heavy x} withdef @values {leftright -type {int char} -choices {light heavy arc} -choicerestricted 0}]
lappend result [dict get $argd values]
#
set argd [punk::args::parse {h x} withdef @values {leftright -type {int char} -choices {light heavy arc} -choicerestricted 0}]
lappend result [dict get $argd values]
#
set argd [punk::args::parse {a h} withdef @values {leftright -type {int char} -choices {light heavy arc} -choicerestricted 0}]
lappend result [dict get $argd values]
}\
-cleanup {
}\
-result [list\
{leftright {11 x}}\
{leftright {11 arc}}\
{leftright {11 arc}}\
{leftright {heavy x}}\
{leftright {heavy x}}\
{leftright {arc heavy}}\
]
package require tcltest
namespace eval ::testspace {
namespace import ::tcltest::*
variable common {
set result ""
}
test choices_typeignored_when_choice_in_list {Test that -type is not validated for a value that matches a choice}\
-setup $common -body {
#1 abbreviated choice
set argd [punk::args::parse {li} withdef @values {frametype -type dict -choices {heavy light arc}}]
lappend result [dict get $argd values]
#2 exact match for a choice
set argd [punk::args::parse {light} withdef @values {frametype -type dict -choices {heavy light arc}}]
lappend result [dict get $argd values]
}\
-cleanup {
}\
-result [list\
{frametype light}\
{frametype light}\
]
test choices_type_validation_choicerestricted1 {Test that -type is validated for value outside of choicelist based on -choicerestricted}\
-setup $common -body {
set argd [punk::args::parse {11} withdef @values {frametype -type int -choicerestricted 0 -choices {heavy light arc}}]
lappend result [dict get $argd values]
if {[catch {
punk::args::parse {z} withdef @values {frametype -type int -choicerestricted 0 -choices {heavy light arc}}
}]} {
lappend result "ok_got_expected_error1"
} else {
lappend result "missing_required_error_when_type_mismatch_for_choice_outside_list"
}
#when -choicerestricted - value matching -type still shouldn't pass
if {[catch {
set argd [punk::args::parse {11} withdef @values {frametype -type int -choicerestricted 1 -choices {heavy light arc}}]
}]} {
lappend result "ok_got_expected_error2"
} else {
lappend result "missing_required_error_when_choicerestricted_and_choice_outside_list"
}
}\
-cleanup {
}\
-result [list\
{frametype 11}\
ok_got_expected_error1\
ok_got_expected_error2\
]
test choices_type_validation_choicerestricted2 {Test that -type dict is validated for value outside of choicelist based on -choicerestricted}\
-setup $common -body {
#same as choices_type_validation_choicrestricted1 - but with a more complex type 'dict' - tests list protection is correct
set argd [punk::args::parse {{hl -}} withdef @values {frametype -type dict -choicerestricted 0 -choices {heavy light arc}}]
lappend result [dict get $argd values]
if {[catch {
punk::args::parse {z} withdef @values {frametype -type dict -choicerestricted 0 -choices {heavy light arc}}
}]} {
lappend result "ok_got_expected_error1"
} else {
lappend result "missing_required_error_when_type_mismatch_for_choice_outside_list"
}
#when -choicerestricted - value matching -type dict still shouldn't pass
if {[catch {
set argd [punk::args::parse {{hl -}} withdef @values {frametype -type dict -choicerestricted 1 -choices {heavy light arc}}]
}]} {
lappend result "ok_got_expected_error2"
} else {
lappend result "missing_required_error_when_choicerestricted_and_choice_outside_list"
}
}\
-cleanup {
}\
-result [list\
{frametype {hl -}}\
ok_got_expected_error1\
ok_got_expected_error2\
]
test choice_multiple_with_choiceprefix {test -choices with both -multiple and -choiceprefix}\
-setup $common -body {
#test with full value choices.
set argd [punk::args::parse {license description} withdef @values {topic -choices {license contributors description} -choiceprefix 1 -multiple 1 }]
lappend result [dict get $argd values]
#test with prefixes of choice.
set argd [punk::args::parse {lic desc} withdef @values {topic -choices {license contributors description} -choiceprefix 1 -multiple 1 }]
lappend result [dict get $argd values]
#test with mixes of full value and prefix of choice.
set argd [punk::args::parse {license desc} withdef @values {topic -choices {license contributors description} -choiceprefix 1 -multiple 1 }]
lappend result [dict get $argd values]
set argd [punk::args::parse {desc license} withdef @values {topic -choices {license contributors description} -choiceprefix 1 -multiple 1 }]
lappend result [dict get $argd values]
}\
-cleanup {
}\
-result [list\
{topic {license description}}\
{topic {license description}}\
{topic {license description}}\
{topic {description license}}\
]
#todo -nocase tests
test choice_multiple_multiple {test -choices with both -multiple and -choicemultiple}\
-setup $common -body {
set argd [punk::args::parse {a {c a} {a b c}} withdef @values {X -type string -choices {aa bb cc} -multiple 1 -choicemultiple {1 3} -optional 1}]
lappend result [dict get $argd values]
}\
-cleanup {
}\
-result [list\
{X {aa {cc aa} {aa bb cc}}}
]
# -choicemultiple allows duplicates in result by default (default for -choicemultipleunique 0)
test choicemultiple_list {test -choices with both -multiple and -choicemultiple}\
-setup $common -body {
set argd [punk::args::parse {{read write w}} withdef @values {mode -type list -choices {read write} -choicemultiple {1 -1}}]
lappend result [dict get $argd values]
}\
-cleanup {
}\
-result [list\
{mode {read write write}}
]
test choice_multielement_clause {test -choice with a clause-length greater than 1}\
-setup $common -body {
#The same -choices list always applies to each member of -type - which isn't always ideal for a multi-element clause
#for a clause where each element has a different choiceset - we would need to introduce a more complex -typechoices option
#(or use a -parsekey mechanism on leaders/values to group them)
#test all combinations of prefix and complete for 2 entries
set argd [punk::args::parse {light heavy} withdef @values {leftright -type {any any} -choices {light heavy} -choicerestricted 1}]
lappend result [dict get $argd values]
set argd [punk::args::parse {li heavy} withdef @values {leftright -type {any any} -choices {light heavy} -choicerestricted 1}]
lappend result [dict get $argd values]
set argd [punk::args::parse {li he} withdef @values {leftright -type {any any} -choices {light heavy} -choicerestricted 1}]
lappend result [dict get $argd values]
set argd [punk::args::parse {light he} withdef @values {leftright -type {any any} -choices {light heavy} -choicerestricted 1}]
lappend result [dict get $argd values]
}\
-cleanup {
}\
-result [list\
{leftright {light heavy}}\
{leftright {light heavy}}\
{leftright {light heavy}}\
{leftright {light heavy}}\
]
test choice_multielement_clause_unrestricted {test -choice with a clause-length greater than 1 and values outside of choicelist}\
-setup $common -body {
#1 both values outside of -choices
set argd [punk::args::parse {11 x} withdef @values {leftright -type {int char} -choices {light heavy arc} -choicerestricted 0}]
lappend result [dict get $argd values]
#
set argd [punk::args::parse {11 arc} withdef @values {leftright -type {int char} -choices {light heavy arc} -choicerestricted 0}]
lappend result [dict get $argd values]
#
set argd [punk::args::parse {11 a} withdef @values {leftright -type {int char} -choices {light heavy arc} -choicerestricted 0}]
lappend result [dict get $argd values]
#
set argd [punk::args::parse {heavy x} withdef @values {leftright -type {int char} -choices {light heavy arc} -choicerestricted 0}]
lappend result [dict get $argd values]
#
set argd [punk::args::parse {h x} withdef @values {leftright -type {int char} -choices {light heavy arc} -choicerestricted 0}]
lappend result [dict get $argd values]
#
set argd [punk::args::parse {a h} withdef @values {leftright -type {int char} -choices {light heavy arc} -choicerestricted 0}]
lappend result [dict get $argd values]
}\
-cleanup {
}\
-result [list\
{leftright {11 x}}\
{leftright {11 arc}}\
{leftright {11 arc}}\
{leftright {heavy x}}\
{leftright {heavy x}}\
{leftright {arc heavy}}\
]
}

254
src/modules/test/punk/#modpod-args-999999.0a1.0/args-0.1.5_testsuites/args/define.test

@ -1,128 +1,128 @@
package require tcltest
namespace eval ::testspace {
namespace import ::tcltest::*
variable common {
set result ""
}
test define_tstr_template1 {Test basic tstr substitution finds vars in namespace in which define was called}\
-setup $common -body {
namespace eval whatever {
set plus +++
set minus ---
punk::args::define {
@id -id ::testspace::test1
@values
param -type string -default "${$plus}XXX${$minus}YYY"
}
}
set argd [punk::args::parse {} withid ::testspace::test1]
set vals [dict get $argd values]
lappend result [dict get $vals param]
}\
-cleanup {
namespace delete ::testspace::whatever
punk::args::undefine ::testspace::test1
}\
-result [list\
+++XXX---YYY
]
test define_tstr_template2 {Test basic tstr substitution when @dynamic}\
-setup $common -body {
namespace eval whatever {
set plus +++
set minus ---
punk::args::define {
@dynamic
@id -id ::testspace::test2
@values
param -type string -default "${$plus}XXX${$minus}YYY"
}
}
set argd [punk::args::parse {} withid ::testspace::test2]
puts ">>>>define_tstr_template2 argd:$argd"
set vals [dict get $argd values]
lappend result [dict get $vals param]
}\
-cleanup {
namespace delete ::testspace::whatever
punk::args::undefine ::testspace::test2
}\
-result [list\
+++XXX---YYY
]
test define_tstr_template3 {Test double tstr substitution when @dynamic}\
-setup $common -body {
variable test_list
set test_list {A B C}
proc ::testspace::get_list {} {
variable test_list
return $test_list
}
namespace eval whatever {
set plus +++
set minus ---
set DYN_LIST {${[::testspace::get_list]}}
set DYN_CLOCKSECONDS {${[clock seconds]}}
punk::args::define {
@dynamic
@id -id ::testspace::test2
@values
param1 -type string -default "${$plus}XXX${$minus}YYY"
param2 -type list -default "${$DYN_LIST}"
param3 -type string -default "${[clock seconds]}"
param4 -type string -default "${$DYN_CLOCKSECONDS}"
}
}
set argd [punk::args::parse {} withid ::testspace::test2]
set vals [dict get $argd values]
lappend result [dict get $vals param1]
lappend result [dict get $vals param2]
set c1_at_define [dict get $vals param3]
set c1_at_resolve [dict get $vals param4]
#update test_list to ensure parse is actually dynamic
set ::testspace::test_list {X Y Z}
#update plus - should not affect output as it is resolved at define time
set ::testspace::whatever::plus "new+"
#unset minus - should not cause error
unset ::testspace::whatever::minus
after 1100 ;#ensure more than 1 sec apart
set argd [punk::args::parse {} withid ::testspace::test2]
set vals [dict get $argd values]
lappend result [dict get $vals param1]
lappend result [dict get $vals param2]
set c2_at_define [dict get $vals param3]
set c2_at_resolve [dict get $vals param4]
if {$c1_at_define == $c2_at_define} {
lappend result "OK_define_time_var_match"
} else {
lappend result "UNEXPECTED_define_time_var_mismatch"
}
if {$c1_at_resolve < $c2_at_resolve} {
lappend result "OK_resolve_time_2_greater"
} else {
lappend result "UNEXPECTED_resolve_time_2_not_greater"
}
}\
-cleanup {
namespace delete ::testspace::whatever
punk::args::undefine ::testspace::test2
}\
-result [list\
+++XXX---YYY {A B C} +++XXX---YYY {X Y Z} OK_define_time_var_match OK_resolve_time_2_greater
]
package require tcltest
namespace eval ::testspace {
namespace import ::tcltest::*
variable common {
set result ""
}
test define_tstr_template1 {Test basic tstr substitution finds vars in namespace in which define was called}\
-setup $common -body {
namespace eval whatever {
set plus +++
set minus ---
punk::args::define {
@id -id ::testspace::test1
@values
param -type string -default "${$plus}XXX${$minus}YYY"
}
}
set argd [punk::args::parse {} withid ::testspace::test1]
set vals [dict get $argd values]
lappend result [dict get $vals param]
}\
-cleanup {
namespace delete ::testspace::whatever
punk::args::undefine ::testspace::test1
}\
-result [list\
+++XXX---YYY
]
test define_tstr_template2 {Test basic tstr substitution when @dynamic}\
-setup $common -body {
namespace eval whatever {
set plus +++
set minus ---
punk::args::define {
@dynamic
@id -id ::testspace::test2
@values
param -type string -default "${$plus}XXX${$minus}YYY"
}
}
set argd [punk::args::parse {} withid ::testspace::test2]
puts ">>>>define_tstr_template2 argd:$argd"
set vals [dict get $argd values]
lappend result [dict get $vals param]
}\
-cleanup {
namespace delete ::testspace::whatever
punk::args::undefine ::testspace::test2
}\
-result [list\
+++XXX---YYY
]
test define_tstr_template3 {Test double tstr substitution when @dynamic}\
-setup $common -body {
variable test_list
set test_list {A B C}
proc ::testspace::get_list {} {
variable test_list
return $test_list
}
namespace eval whatever {
set plus +++
set minus ---
set DYN_LIST {${[::testspace::get_list]}}
set DYN_CLOCKSECONDS {${[clock seconds]}}
punk::args::define {
@dynamic
@id -id ::testspace::test2
@values
param1 -type string -default "${$plus}XXX${$minus}YYY"
param2 -type list -default "${$DYN_LIST}"
param3 -type string -default "${[clock seconds]}"
param4 -type string -default "${$DYN_CLOCKSECONDS}"
}
}
set argd [punk::args::parse {} withid ::testspace::test2]
set vals [dict get $argd values]
lappend result [dict get $vals param1]
lappend result [dict get $vals param2]
set c1_at_define [dict get $vals param3]
set c1_at_resolve [dict get $vals param4]
#update test_list to ensure parse is actually dynamic
set ::testspace::test_list {X Y Z}
#update plus - should not affect output as it is resolved at define time
set ::testspace::whatever::plus "new+"
#unset minus - should not cause error
unset ::testspace::whatever::minus
after 1100 ;#ensure more than 1 sec apart
set argd [punk::args::parse {} withid ::testspace::test2]
set vals [dict get $argd values]
lappend result [dict get $vals param1]
lappend result [dict get $vals param2]
set c2_at_define [dict get $vals param3]
set c2_at_resolve [dict get $vals param4]
if {$c1_at_define == $c2_at_define} {
lappend result "OK_define_time_var_match"
} else {
lappend result "UNEXPECTED_define_time_var_mismatch"
}
if {$c1_at_resolve < $c2_at_resolve} {
lappend result "OK_resolve_time_2_greater"
} else {
lappend result "UNEXPECTED_resolve_time_2_not_greater"
}
}\
-cleanup {
namespace delete ::testspace::whatever
punk::args::undefine ::testspace::test2
}\
-result [list\
+++XXX---YYY {A B C} +++XXX---YYY {X Y Z} OK_define_time_var_match OK_resolve_time_2_greater
]
}

448
src/modules/test/punk/#modpod-args-999999.0a1.0/args-0.1.5_testsuites/args/mashopts.test

@ -1,225 +1,225 @@
package require tcltest
namespace eval ::testspace {
namespace import ::tcltest::*
variable common {
set result ""
}
#test mash opts aka "option clustering" aka "flag stacking" aka "option combining" aka "short flag bundling" etc.
test mashopts_default {Test basic combining of short options when -mash set as default for short flags on @opts directive}\
-setup $common -body {
#first test they work individually as normal
set argd [punk::args::parse {-a -b -c} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none}]
lappend result [dict get $argd opts]
#test all combined
set argd [punk::args::parse {-abc} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none}]
lappend result [dict get $argd opts]
#varying order of flags in mash should still work
set argd [punk::args::parse {-cab} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none}]
lappend result [dict get $argd opts]
#repeating flags in mash should still work and be treated as if they were repeated separately (ie -aa should be treated as if it were -a -a)
#in this case we have not configured any of the flags to be multiple, so the second occurrence of each flag should just override the first occurrence and have no effect
set argd [punk::args::parse {-caba} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none}]
lappend result [dict get $argd opts]
#order of flags in the result should be the same as the order of flags in the definition of the optionset,
#not the order in which they were supplied in the mash - this is because we want the result to be deterministic and not depend on the order in which the user happened to combine the flags in the mash
#the actual order should be reflected in the received list.
set argd [punk::args::parse {-caba} withdef {@opts -mash 1} {-c -type none} {-a -type none} {-b -type none}]
lappend result [dict get $argd opts]
#the received list should show the repeated -a even though it's not set for multiple.
lappend result [dict get $argd received]
}\
-cleanup {
}\
-result [list\
{-a 1 -b 1 -c 1}\
{-a 1 -b 1 -c 1}\
{-a 1 -b 1 -c 1}\
{-a 1 -b 1 -c 1}\
{-c 1 -a 1 -b 1}\
{-c 0 -a 1 -b 2 -a 3}\
]
test mashopts_default_with_multiple {Test combining of short options when -mash set as default for short flags on @opts directive and a flag is set to -multiple}\
-setup $common -body {
#first test they work individually as normal
set argd [punk::args::parse {-a -b -c} withdef {@opts -mash 1} {-a -type none} {-b -type none -multiple true} {-c -type none}]
lappend result [dict get $argd opts]
set argd [punk::args::parse {-cba} withdef {@opts -mash 1} {-a -type none} {-b -type none -multiple true} {-c -type none}]
lappend result [dict get $argd opts]
#test a repeated flag within the mash
set argd [punk::args::parse {-cbba} withdef {@opts -mash 1} {-a -type none} {-b -type none -multiple true} {-c -type none}]
lappend result [dict get $argd opts]
#test a repeated flag after the mash
set argd [punk::args::parse {-cba -b} withdef {@opts -mash 1} {-a -type none} {-b -type none -multiple true} {-c -type none}]
lappend result [dict get $argd opts]
#test a repeated flag before the mash
set argd [punk::args::parse {-b -cba} withdef {@opts -mash 1} {-a -type none} {-b -type none -multiple true} {-c -type none}]
lappend result [dict get $argd opts]
#test a repeated flag before and after the mash
set argd [punk::args::parse {-b -cba -b} withdef {@opts -mash 1} {-a -type none} {-b -type none -multiple true} {-c -type none}]
lappend result [dict get $argd opts]
#test a repeated flag before, within and after the mash
set argd [punk::args::parse {-b -cbab -b} withdef {@opts -mash 1} {-a -type none} {-b -type none -multiple true} {-c -type none}]
lappend result [dict get $argd opts]
}\
-cleanup {
}\
-result [list\
{-a 1 -b 1 -c 1}\
{-a 1 -b 1 -c 1}\
{-a 1 -b {1 1} -c 1}\
{-a 1 -b {1 1} -c 1}\
{-a 1 -b {1 1} -c 1}\
{-a 1 -b {1 1 1} -c 1}\
{-a 1 -b {1 1 1 1} -c 1}\
]
test mashopts_default_with_typed_shortflag {Test combining of short options when -mash set as default for short flags on @opts directive and a shortopt accepts a value}\
-setup $common -body {
#test individually
set argd [punk::args::parse {-a -b -f fff -c} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none} {-f -type string}]
lappend result [dict get $argd opts]
#test with mash - the flag that accepts a value must be at the end of the mash.
set argd [punk::args::parse {-bacf fff} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none} {-f -type string}]
lappend result [dict get $argd opts]
#should error if the flag that accepts a value is not at the end of the mash, because that would be ambiguous - we would not know which flag the value belongs to
if {[catch {punk::args::parse {-bafc fff} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none} {-f -type string}} err]} {
lappend result "expected-error"
} else {
lappend result "missing-expected-error"
}
#failing to provide a value for -f should raise an error.
if {[catch {punk::args::parse {-bacf} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none} {-f -type string}} err]} {
lappend result "expected-error"
} else {
lappend result "missing-expected-error"
}
}\
-cleanup {
}\
-result [list\
{-a 1 -b 1 -c 1 -f fff}\
{-a 1 -b 1 -c 1 -f fff}\
expected-error\
expected-error\
]
test mashopts_default_with_other_flags {Test combining of short options when -mash set as default for short flags on @opts directive plus a longer value-accepting flag and a value}\
-setup $common -body {
#test individually
set argd [punk::args::parse {-a -b -f fff -c -cabinet ccc ttt} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none} {-f -type string} {-cabinet -type string} @values tail]
lappend result [dict get $argd opts]
lappend result [dict get $argd values]
#should error if the non-mash flag that accepts a value is supplied with a prefix shorter than the number of mash flags.
#(we don't calculate prefixes based on a possibly huge combination of mash flags, so we simply require prefixes for non-mash flags to be at least as long as the number of mash flags)
if {[catch {punk::args::parse {-bacf fff -cabi ccc ttt} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none} {-f -type string} {-cabinet -type string} @values tail} err]} {
lappend result "expected-error"
} else {
lappend result "missing-expected-error"
}
#we have 4 mash flags here, so a unique prefix of cabinet that is 5 long should be accepted.
set argd [punk::args::parse {-cabf fff -c -cabin ccc ttt} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none} {-f -type string} {-cabinet -type string} @values {tail -multiple 1 -optional 1}]
lappend result [dict get $argd opts]
lappend result [dict get $argd values]
#test it's not confused by a short prefix of cabinet that matches only mash flags.
#-cab should be processed as match flags - not a prefix of cabinet.
set argd [punk::args::parse {-cabf fff -c -cab ccc ttt} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none} {-f -type string} {-cabinet -type string} @values {tail -multiple 1 -optional 1}]
lappend result [dict get $argd opts]
lappend result [dict get $argd values]
}\
-cleanup {
}\
-result [list\
{-a 1 -b 1 -c 1 -f fff -cabinet ccc}\
{tail ttt}\
expected-error\
{-a 1 -b 1 -c 1 -f fff -cabinet ccc}\
{tail ttt}\
{-a 1 -b 1 -c 1 -f fff}\
{tail {ccc ttt}}\
]
test mashopts_mix_default_and_explicit {Test combining of short options when -mash set both on @opts and directly}\
-setup $common -body {
#-c no longer allowed in mash
set argd [punk::args::parse {-a -b -f fff -c -cabinet ccc ttt} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none -mash 0} {-f -type string} {-cabinet -type string} @values tail]
lappend result [dict get $argd opts]
lappend result [dict get $argd values]
set argd [punk::args::parse {-abf fff -c -cabinet ccc ttt} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none -mash 0} {-f -type string} {-cabinet -type string} @values tail]
lappend result [dict get $argd opts]
lappend result [dict get $argd values]
#attempting to mash -c should raise an error.
if {[catch {punk::args::parse {-bacf fff -cabinet ccc ttt} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none -mash 0} {-f -type string} {-cabinet -type string} @values tail} err]} {
lappend result "expected-error"
} else {
lappend result "missing-expected-error"
}
#test with only explicit -mash 1 on individual flags.
set argd [punk::args::parse {-abf fff -c -cabinet ccc ttt} withdef @opts {-a -type none -mash 1} {-b -type none -mash 1} {-c -type none} {-f -type string -mash 1} {-cabinet -type string} @values tail]
lappend result [dict get $argd opts]
lappend result [dict get $argd values]
#attempting to explicitly apply -mash 1 to -cabinet should raise an error because -cabinet is not a short flag and we only allow -mash 1 to be applied to short flags.
#(default -mash 1 on @opts is different as it is automatically only propagated to short flags.)
if {[catch {punk::args::parse {-acbf fff -cabinet ccc ttt} withdef @opts {-a -type none -mash 1} {-b -type none -mash 1} {-c -type none} {-f -type string -mash 1} {-cabinet -type string -mash 1} @values tail} err]} {
lappend result "expected-error"
} else {
lappend result "missing-expected-error"
}
#-c should default to not being mashable, so attempting to mash it should raise an error.
if {[catch {punk::args::parse {-acbf fff -cabinet ccc ttt} withdef @opts {-a -type none -mash 1} {-b -type none -mash 1} {-c -type none} {-f -type string -mash 1} {-cabinet -type string} @values tail} err]} {
lappend result "expected-error"
} else {
lappend result "missing-expected-error"
}
}\
-cleanup {
}\
-result [list\
{-a 1 -b 1 -c 1 -f fff -cabinet ccc}\
{tail ttt}\
{-a 1 -b 1 -c 1 -f fff -cabinet ccc}\
{tail ttt}\
expected-error\
{-a 1 -b 1 -c 1 -f fff -cabinet ccc}\
{tail ttt}\
expected-error\
expected-error\
]
package require tcltest
namespace eval ::testspace {
namespace import ::tcltest::*
variable common {
set result ""
}
#test mash opts aka "option clustering" aka "flag stacking" aka "option combining" aka "short flag bundling" etc.
test mashopts_default {Test basic combining of short options when -mash set as default for short flags on @opts directive}\
-setup $common -body {
#first test they work individually as normal
set argd [punk::args::parse {-a -b -c} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none}]
lappend result [dict get $argd opts]
#test all combined
set argd [punk::args::parse {-abc} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none}]
lappend result [dict get $argd opts]
#varying order of flags in mash should still work
set argd [punk::args::parse {-cab} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none}]
lappend result [dict get $argd opts]
#repeating flags in mash should still work and be treated as if they were repeated separately (ie -aa should be treated as if it were -a -a)
#in this case we have not configured any of the flags to be multiple, so the second occurrence of each flag should just override the first occurrence and have no effect
set argd [punk::args::parse {-caba} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none}]
lappend result [dict get $argd opts]
#order of flags in the result should be the same as the order of flags in the definition of the optionset,
#not the order in which they were supplied in the mash - this is because we want the result to be deterministic and not depend on the order in which the user happened to combine the flags in the mash
#the actual order should be reflected in the received list.
set argd [punk::args::parse {-caba} withdef {@opts -mash 1} {-c -type none} {-a -type none} {-b -type none}]
lappend result [dict get $argd opts]
#the received list should show the repeated -a even though it's not set for multiple.
lappend result [dict get $argd received]
}\
-cleanup {
}\
-result [list\
{-a 1 -b 1 -c 1}\
{-a 1 -b 1 -c 1}\
{-a 1 -b 1 -c 1}\
{-a 1 -b 1 -c 1}\
{-c 1 -a 1 -b 1}\
{-c 0 -a 1 -b 2 -a 3}\
]
test mashopts_default_with_multiple {Test combining of short options when -mash set as default for short flags on @opts directive and a flag is set to -multiple}\
-setup $common -body {
#first test they work individually as normal
set argd [punk::args::parse {-a -b -c} withdef {@opts -mash 1} {-a -type none} {-b -type none -multiple true} {-c -type none}]
lappend result [dict get $argd opts]
set argd [punk::args::parse {-cba} withdef {@opts -mash 1} {-a -type none} {-b -type none -multiple true} {-c -type none}]
lappend result [dict get $argd opts]
#test a repeated flag within the mash
set argd [punk::args::parse {-cbba} withdef {@opts -mash 1} {-a -type none} {-b -type none -multiple true} {-c -type none}]
lappend result [dict get $argd opts]
#test a repeated flag after the mash
set argd [punk::args::parse {-cba -b} withdef {@opts -mash 1} {-a -type none} {-b -type none -multiple true} {-c -type none}]
lappend result [dict get $argd opts]
#test a repeated flag before the mash
set argd [punk::args::parse {-b -cba} withdef {@opts -mash 1} {-a -type none} {-b -type none -multiple true} {-c -type none}]
lappend result [dict get $argd opts]
#test a repeated flag before and after the mash
set argd [punk::args::parse {-b -cba -b} withdef {@opts -mash 1} {-a -type none} {-b -type none -multiple true} {-c -type none}]
lappend result [dict get $argd opts]
#test a repeated flag before, within and after the mash
set argd [punk::args::parse {-b -cbab -b} withdef {@opts -mash 1} {-a -type none} {-b -type none -multiple true} {-c -type none}]
lappend result [dict get $argd opts]
}\
-cleanup {
}\
-result [list\
{-a 1 -b 1 -c 1}\
{-a 1 -b 1 -c 1}\
{-a 1 -b {1 1} -c 1}\
{-a 1 -b {1 1} -c 1}\
{-a 1 -b {1 1} -c 1}\
{-a 1 -b {1 1 1} -c 1}\
{-a 1 -b {1 1 1 1} -c 1}\
]
test mashopts_default_with_typed_shortflag {Test combining of short options when -mash set as default for short flags on @opts directive and a shortopt accepts a value}\
-setup $common -body {
#test individually
set argd [punk::args::parse {-a -b -f fff -c} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none} {-f -type string}]
lappend result [dict get $argd opts]
#test with mash - the flag that accepts a value must be at the end of the mash.
set argd [punk::args::parse {-bacf fff} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none} {-f -type string}]
lappend result [dict get $argd opts]
#should error if the flag that accepts a value is not at the end of the mash, because that would be ambiguous - we would not know which flag the value belongs to
if {[catch {punk::args::parse {-bafc fff} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none} {-f -type string}} err]} {
lappend result "expected-error"
} else {
lappend result "missing-expected-error"
}
#failing to provide a value for -f should raise an error.
if {[catch {punk::args::parse {-bacf} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none} {-f -type string}} err]} {
lappend result "expected-error"
} else {
lappend result "missing-expected-error"
}
}\
-cleanup {
}\
-result [list\
{-a 1 -b 1 -c 1 -f fff}\
{-a 1 -b 1 -c 1 -f fff}\
expected-error\
expected-error\
]
test mashopts_default_with_other_flags {Test combining of short options when -mash set as default for short flags on @opts directive plus a longer value-accepting flag and a value}\
-setup $common -body {
#test individually
set argd [punk::args::parse {-a -b -f fff -c -cabinet ccc ttt} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none} {-f -type string} {-cabinet -type string} @values tail]
lappend result [dict get $argd opts]
lappend result [dict get $argd values]
#should error if the non-mash flag that accepts a value is supplied with a prefix shorter than the number of mash flags.
#(we don't calculate prefixes based on a possibly huge combination of mash flags, so we simply require prefixes for non-mash flags to be at least as long as the number of mash flags)
if {[catch {punk::args::parse {-bacf fff -cabi ccc ttt} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none} {-f -type string} {-cabinet -type string} @values tail} err]} {
lappend result "expected-error"
} else {
lappend result "missing-expected-error"
}
#we have 4 mash flags here, so a unique prefix of cabinet that is 5 long should be accepted.
set argd [punk::args::parse {-cabf fff -c -cabin ccc ttt} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none} {-f -type string} {-cabinet -type string} @values {tail -multiple 1 -optional 1}]
lappend result [dict get $argd opts]
lappend result [dict get $argd values]
#test it's not confused by a short prefix of cabinet that matches only mash flags.
#-cab should be processed as match flags - not a prefix of cabinet.
set argd [punk::args::parse {-cabf fff -c -cab ccc ttt} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none} {-f -type string} {-cabinet -type string} @values {tail -multiple 1 -optional 1}]
lappend result [dict get $argd opts]
lappend result [dict get $argd values]
}\
-cleanup {
}\
-result [list\
{-a 1 -b 1 -c 1 -f fff -cabinet ccc}\
{tail ttt}\
expected-error\
{-a 1 -b 1 -c 1 -f fff -cabinet ccc}\
{tail ttt}\
{-a 1 -b 1 -c 1 -f fff}\
{tail {ccc ttt}}\
]
test mashopts_mix_default_and_explicit {Test combining of short options when -mash set both on @opts and directly}\
-setup $common -body {
#-c no longer allowed in mash
set argd [punk::args::parse {-a -b -f fff -c -cabinet ccc ttt} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none -mash 0} {-f -type string} {-cabinet -type string} @values tail]
lappend result [dict get $argd opts]
lappend result [dict get $argd values]
set argd [punk::args::parse {-abf fff -c -cabinet ccc ttt} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none -mash 0} {-f -type string} {-cabinet -type string} @values tail]
lappend result [dict get $argd opts]
lappend result [dict get $argd values]
#attempting to mash -c should raise an error.
if {[catch {punk::args::parse {-bacf fff -cabinet ccc ttt} withdef {@opts -mash 1} {-a -type none} {-b -type none} {-c -type none -mash 0} {-f -type string} {-cabinet -type string} @values tail} err]} {
lappend result "expected-error"
} else {
lappend result "missing-expected-error"
}
#test with only explicit -mash 1 on individual flags.
set argd [punk::args::parse {-abf fff -c -cabinet ccc ttt} withdef @opts {-a -type none -mash 1} {-b -type none -mash 1} {-c -type none} {-f -type string -mash 1} {-cabinet -type string} @values tail]
lappend result [dict get $argd opts]
lappend result [dict get $argd values]
#attempting to explicitly apply -mash 1 to -cabinet should raise an error because -cabinet is not a short flag and we only allow -mash 1 to be applied to short flags.
#(default -mash 1 on @opts is different as it is automatically only propagated to short flags.)
if {[catch {punk::args::parse {-acbf fff -cabinet ccc ttt} withdef @opts {-a -type none -mash 1} {-b -type none -mash 1} {-c -type none} {-f -type string -mash 1} {-cabinet -type string -mash 1} @values tail} err]} {
lappend result "expected-error"
} else {
lappend result "missing-expected-error"
}
#-c should default to not being mashable, so attempting to mash it should raise an error.
if {[catch {punk::args::parse {-acbf fff -cabinet ccc ttt} withdef @opts {-a -type none -mash 1} {-b -type none -mash 1} {-c -type none} {-f -type string -mash 1} {-cabinet -type string} @values tail} err]} {
lappend result "expected-error"
} else {
lappend result "missing-expected-error"
}
}\
-cleanup {
}\
-result [list\
{-a 1 -b 1 -c 1 -f fff -cabinet ccc}\
{tail ttt}\
{-a 1 -b 1 -c 1 -f fff -cabinet ccc}\
{tail ttt}\
expected-error\
{-a 1 -b 1 -c 1 -f fff -cabinet ccc}\
{tail ttt}\
expected-error\
expected-error\
]
}

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

@ -1,76 +1,76 @@
package require tcltest
namespace eval ::testspace {
namespace import ::tcltest::*
variable common {
set result ""
}
test opts_longoptvalue {Test -alt|--longopt= can accept value as longopt}\
-setup $common -body {
set argd [punk::args::parse {--filename=abc} withdef @opts {-f|--filename= -default spud -type string}]
lappend result [dict get $argd opts];#name by default should be last flag alternative (stripped of =) ie "--filename"
}\
-cleanup {
}\
-result [list\
{--filename abc}\
]
test opts_longoptvalue_alternative {Test -alt|--longopt= can accept value as spaced argument to given alternative}\
-setup $common -body {
#test full name of alt flag
set argd [punk::args::parse {-fx xyz} withdef @opts {-fx|--filename= -default spud -type string}]
lappend result [dict get $argd opts] ;#name by default should be last flag alternative (stripped of =) ie "--filename"
#test prefixed version of flag
set argd [punk::args::parse {-f xyz} withdef @opts {-fx|--filename= -default spud -type string}]
lappend result [dict get $argd opts]
}\
-cleanup {
}\
-result [list\
{--filename xyz}\
{--filename xyz}\
]
test opts_longoptvalue_alternative_noninterference {Test -alt|--longopt= can accept longopt values as normal }\
-setup $common -body {
#test full name of longopt
set argd [punk::args::parse {--filename=xyz} withdef @opts {-fx|--filename= -default spud -type string}]
lappend result [dict get $argd opts] ;#name by default should be last flag alternative (stripped of =) ie "--filename"
#test prefixed version of longopt
set argd [punk::args::parse {--file=xyz} withdef @opts {-fx|--filename= -default spud -type string}]
lappend result [dict get $argd opts]
}\
-cleanup {
}\
-result [list\
{--filename xyz}\
{--filename xyz}\
]
test opts_longoptvalue_choice {Test --longopt= works wiith -choices}\
-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 result [dict get $argd opts]
set argd [punk::args::parse {--file=x} withdef @opts {--filename= -default spud -type string -choices {abc xyz}}]
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 result [dict get $argd opts]
set argd [punk::args::parse {--file=xyz} withdef @opts {--filename= -default spud -type string -choices {abc xyz}}]
lappend result [dict get $argd opts]
}\
-cleanup {
}\
-result [list\
{--filename xyz}\
{--filename xyz}\
{--filename xyz}\
{--filename xyz}\
]
package require tcltest
namespace eval ::testspace {
namespace import ::tcltest::*
variable common {
set result ""
}
test opts_longoptvalue {Test -alt|--longopt= can accept value as longopt}\
-setup $common -body {
set argd [punk::args::parse {--filename=abc} withdef @opts {-f|--filename= -default spud -type string}]
lappend result [dict get $argd opts];#name by default should be last flag alternative (stripped of =) ie "--filename"
}\
-cleanup {
}\
-result [list\
{--filename abc}\
]
test opts_longoptvalue_alternative {Test -alt|--longopt= can accept value as spaced argument to given alternative}\
-setup $common -body {
#test full name of alt flag
set argd [punk::args::parse {-fx xyz} withdef @opts {-fx|--filename= -default spud -type string}]
lappend result [dict get $argd opts] ;#name by default should be last flag alternative (stripped of =) ie "--filename"
#test prefixed version of flag
set argd [punk::args::parse {-f xyz} withdef @opts {-fx|--filename= -default spud -type string}]
lappend result [dict get $argd opts]
}\
-cleanup {
}\
-result [list\
{--filename xyz}\
{--filename xyz}\
]
test opts_longoptvalue_alternative_noninterference {Test -alt|--longopt= can accept longopt values as normal }\
-setup $common -body {
#test full name of longopt
set argd [punk::args::parse {--filename=xyz} withdef @opts {-fx|--filename= -default spud -type string}]
lappend result [dict get $argd opts] ;#name by default should be last flag alternative (stripped of =) ie "--filename"
#test prefixed version of longopt
set argd [punk::args::parse {--file=xyz} withdef @opts {-fx|--filename= -default spud -type string}]
lappend result [dict get $argd opts]
}\
-cleanup {
}\
-result [list\
{--filename xyz}\
{--filename xyz}\
]
test opts_longoptvalue_choice {Test --longopt= works wiith -choices}\
-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 result [dict get $argd opts]
set argd [punk::args::parse {--file=x} withdef @opts {--filename= -default spud -type string -choices {abc xyz}}]
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 result [dict get $argd opts]
set argd [punk::args::parse {--file=xyz} withdef @opts {--filename= -default spud -type string -choices {abc xyz}}]
lappend result [dict get $argd opts]
}\
-cleanup {
}\
-result [list\
{--filename xyz}\
{--filename xyz}\
{--filename xyz}\
{--filename xyz}\
]
}

326
src/modules/test/punk/#modpod-args-999999.0a1.0/args-0.1.5_testsuites/args/synopsis.test

@ -1,151 +1,175 @@
package require tcltest
namespace eval ::testspace {
namespace import ::tcltest::*
namespace import ::punk::ansi::a+ ::punk::ansi::a
variable common {
set result ""
}
test synopsis_basic {test basic synopsis of punkargs definition}\
-setup $common -body {
#no @cmd -summary
#we still expect and require a leading line "# " in the synopsis
namespace eval testns {
punk::args::define {
@id -id ::testspace::testns::t1
@leaders
a1 -optional 0
@opts
-o1 -type boolean
@values
v1 -optional 1
}
}
lappend result [punk::ns::synopsis ::testspace::testns::t1]
}\
-cleanup {
namespace delete ::testspace::testns
}\
-result [list\
"# \n::testspace::testns::t1 [a+ italic]a1[a+ noitalic] ?-o1 <[a+ italic]bool[a+ noitalic]>? ?[a+ italic]v1[a+ noitalic]?"
]
test synopsis_basic_ensemble-like {test basic synopsis of punkargs ensemble-like definition}\
-setup $common -body {
namespace eval testns {
punk::args::define {
@id -id ::testspace::testns::t1
@cmd -summary "summary"
@leaders
subcmd -default c1 -choices {c1 c2}
@values -min 0 -max 0
}
punk::args::define {
@id -id "::testspace::testns::t1 c1"
@cmd -summary "summary"
@values -min 0 -max 1
v1 -type string
}
}
lappend result [punk::ns::synopsis ::testspace::testns::t1]
lappend result [punk::ns::synopsis ::testspace::testns::t1 c1]
}\
-cleanup {
namespace delete ::testspace::testns
}\
-result [list\
"# summary\n::testspace::testns::t1 ?[a+ italic]subcmd[a+ noitalic]?"\
"# summary\n::testspace::testns::t1 c1 [a+ italic]v1[a+ noitalic]"
]
test synopsis_alias_longopt_requiredval {}\
-setup $common -body {
namespace eval testns {
punk::args::define {
@id -id ::testspace::testns::t1
@cmd -summary summary
--verbose= -type int -default unreceived
}
}
lappend result [punk::ns::synopsis ::testspace::testns::t1]
#test that missing flag uses -default value
set argd [punk::args::parse {} withid ::testspace::testns::t1]
lappend result [dict get $argd opts]
#test prefix version of longopt accepts supplied int
set argd [punk::args::parse {--v=33} withid ::testspace::testns::t1]
lappend result [dict get $argd opts]
if {[catch {
set argd [punk::args::parse {--v=} withid ::testspace::testns::t1]
} eMsg eOpts]} {
lappend result "expected-error1"
} else {
lappend result "missing-required-error1"
}
if {[catch {
set argd [punk::args::parse {--v} withid ::testspace::testns::t1]
} eMsg eOpts]} {
lappend result "expected-error2"
} else {
lappend result "missing-required-error2"
}
}\
-cleanup {
namespace delete ::testspace::testns
}\
-result [list\
"# summary\n::testspace::testns::t1 ?--verbose=<[a+ italic]int[a+ noitalic]>?"\
{--verbose unreceived}\
{--verbose 33}\
expected-error1\
expected-error2
]
test synopsis_alias_longopt_optionalval {}\
-setup $common -body {
namespace eval testns {
punk::args::define {
@id -id ::testspace::testns::t1
@cmd -summary summary
--verbose= -type ?int? -default unreceived -typedefaults received
}
}
lappend result [punk::ns::synopsis ::testspace::testns::t1]
#test that missing flag uses -default value
set argd [punk::args::parse {} withid ::testspace::testns::t1]
lappend result [dict get $argd opts]
#test prefix version of longopt accepts supplied int
set argd [punk::args::parse {--v=33} withid ::testspace::testns::t1]
lappend result [dict get $argd opts]
if {[catch {
set argd [punk::args::parse {--v=} withid ::testspace::testns::t1]
} eMsg eOpts]} {
#expect fail due to received empty string failing <int>
lappend result "expected-error1"
} else {
lappend result "missing-required-error1"
}
#because the type is optional (?int?) - we expect the longopt to support solo operation.
#It should pick up the -typedefaults value as a default (not -default, which is for missing flag only)
set argd [punk::args::parse {--v} withid ::testspace::testns::t1]
lappend result [dict get $argd opts]
}\
-cleanup {
namespace delete ::testspace::testns
}\
-result [list\
"# summary\n::testspace::testns::t1 ?--verbose[a+ italic strike]?[a+ noitalic nostrike]=<[a+ italic]int[a+ noitalic]>[a+ italic strike]?[a+ noitalic nostrike]?"\
{--verbose unreceived}\
{--verbose 33}\
expected-error1\
{--verbose received}
]
}
package require tcltest
namespace eval ::testspace {
namespace import ::tcltest::*
namespace import ::punk::ansi::a+ ::punk::ansi::a
variable common {
set result ""
}
test synopsis_basic {test basic synopsis of punkargs definition}\
-setup $common -body {
#no @cmd -summary
#we still expect and require a leading line "# " in the synopsis
namespace eval testns {
punk::args::define {
@id -id ::testspace::testns::t1
@leaders
a1 -optional 0
@opts
-o1 -type boolean
@values
v1 -optional 1
}
}
set syntext [punk::ns::synopsis ::testspace::testns::t1]
set remlines [list]
foreach ln [split $syntext \n] {
if {[string match "##*" $ln]} {continue}
lappend remlines $ln
}
lappend result [join $remlines \n]
}\
-cleanup {
namespace delete ::testspace::testns
}\
-result [list\
"# \n::testspace::testns::t1 [a+ italic]a1[a+ noitalic] \[-o1 [a+ italic]<bool>[a+ noitalic]\] \[[a+ italic]v1[a+ noitalic]\]"
]
test synopsis_basic_ensemble-like {test basic synopsis of punkargs ensemble-like definition}\
-setup $common -body {
namespace eval testns {
punk::args::define {
@id -id ::testspace::testns::t1
@cmd -summary "summary"
@leaders
subcmd -default c1 -choices {c1 c2}
@values -min 0 -max 0
}
punk::args::define {
@id -id "::testspace::testns::t1 c1"
@cmd -summary "summary"
@values -min 0 -max 1
v1 -type string
}
}
#strip out the secondary form lines starting with ##.
#lappend result [punk::ansi::grepstr -return matched {^(?!##)} [punk::ns::synopsis ::testspace::testns::t1]]
lappend result [grepstr -h + -return matched -v {^##} [punk::ns::synopsis ::testspace::testns::t1]]
#lappend result [grepstr -return matched {^(?!##)} [punk::ns::synopsis ::testspace::testns::t1 c1]]
lappend result [grepstr -h + -return matched -v {^##} [punk::ns::synopsis ::testspace::testns::t1 c1]]
}\
-cleanup {
namespace delete ::testspace::testns
}\
-result [list\
"# summary\n::testspace::testns::t1 \[[a+ italic]subcmd[a+ noitalic]\]"\
"# summary\n::testspace::testns::t1 c1 [a+ italic]v1[a+ noitalic]"
]
test synopsis_alias_longopt_requiredval {}\
-setup $common -body {
namespace eval testns {
punk::args::define {
@id -id ::testspace::testns::t1
@cmd -summary summary
--verbose= -type int -default unreceived
}
}
#lappend result [grepstr -return matched {^(?!##)} [punk::ns::synopsis ::testspace::testns::t1]]
lappend result [grepstr -h + -return matched -v {^##} [punk::ns::synopsis ::testspace::testns::t1]]
#test that missing flag uses -default value
set argd [punk::args::parse {} withid ::testspace::testns::t1]
lappend result [dict get $argd opts]
#test prefix version of longopt accepts supplied int
set argd [punk::args::parse {--v=33} withid ::testspace::testns::t1]
lappend result [dict get $argd opts]
if {[catch {
set argd [punk::args::parse {--v=} withid ::testspace::testns::t1]
} eMsg eOpts]} {
lappend result "expected-error1"
} else {
lappend result "missing-required-error1"
}
if {[catch {
set argd [punk::args::parse {--v} withid ::testspace::testns::t1]
} eMsg eOpts]} {
lappend result "expected-error2"
} else {
lappend result "missing-required-error2"
}
}\
-cleanup {
namespace delete ::testspace::testns
}\
-result [list\
"# summary\n::testspace::testns::t1 \[--verbose=[a+ italic]<int>[a+ noitalic]\]"\
{--verbose unreceived}\
{--verbose 33}\
expected-error1\
expected-error2
]
test synopsis_alias_longopt_optionalval {}\
-setup $common -body {
namespace eval testns {
punk::args::define {
@id -id ::testspace::testns::t1
@cmd -summary summary
--verbose= -type ?int? -default unreceived -typedefaults received
}
}
#test relies heavily on overtype::renderline behaviour within call to grepstr -h + to return the original lines with ANSI intact,
#We should ensure that overtype tests also reflect this required behaviour.
#this test is also very specific about the required ANSI such as italics for the synopsis.
#italics differentiate literal strings from variable names and types and are also applied to nested optional values to make the synopsis easier to read.
#We should ensure that the synopsis tests also reflect this required behaviour.
#todo - consider referencing some of these tests from the code where it's implemented.
#lappend result [grepstr -return matched {^(?!##)} [punk::ns::synopsis ::testspace::testns::t1]]
#use -highlight + with negated match to return the original lines with ANSI intact
lappend result [grepstr -h + -return matched -v {^##} [punk::ns::synopsis ::testspace::testns::t1]]
#test that missing flag uses -default value
set argd [punk::args::parse {} withid ::testspace::testns::t1]
lappend result [dict get $argd opts]
#test prefix version of longopt accepts supplied int
set argd [punk::args::parse {--v=33} withid ::testspace::testns::t1]
lappend result [dict get $argd opts]
if {[catch {
set argd [punk::args::parse {--v=} withid ::testspace::testns::t1]
} eMsg eOpts]} {
#expect fail due to received empty string failing <int>
lappend result "expected-error1"
} else {
lappend result "missing-required-error1"
}
#because the type is optional (?int?) - we expect the longopt to support solo operation.
#It should pick up the -typedefaults value as a default (not -default, which is for missing flag only)
set argd [punk::args::parse {--v} withid ::testspace::testns::t1]
lappend result [dict get $argd opts]
}\
-cleanup {
namespace delete ::testspace::testns
}\
-result [list\
"# summary\n::testspace::testns::t1 \[--verbose[a+ italic]\[[a+ noitalic]=[a+ italic]<int>\][a+ noitalic]\]"\
{--verbose unreceived}\
{--verbose 33}\
expected-error1\
{--verbose received}
]
}

67
src/modules/test/punk/#modpod-lib-999999.0a1.0/lib-0.1.3_testsuites/lib/index_functions.test

@ -31,15 +31,23 @@ namespace eval ::testspace {
-setup $common -body {
#e.g indices {0 1 2 3 4} n = 5
#lindex_resolve_basic should always return -Inf for out of bounds,
#but lindex_resolve should return Inf for out of bounds on upper side, and -Inf for out of bounds on lower side
lappend result [punk::lib::lindex_resolve 5 end+1] ;# -> Inf out of bounds on upper side
lappend result [punk::lib::lindex_resolve_basic 5 end+1] ;# -> -Inf
lappend result [punk::lib::lindex_resolve 5 end--1] ;# equiv to +1 -> Inf
lappend result [punk::lib::lindex_resolve_basic 5 end--1] ;# -> -Inf
lappend result [punk::lib::lindex_resolve 5 4--5] ;# -> Inf out of bounds on upper side
lappend result [punk::lib::lindex_resolve_basic 5 4--5] ;# -> -Inf
lappend result [punk::lib::lindex_resolve 5 end--5] ;# -> Inf out of bounds on upper side
lappend result [punk::lib::lindex_resolve_basic 5 end--5] ;# -> -Inf
lappend result [punk::lib::lindex_resolve 5 4-5] ;# -> -Inf out of bounds on lower side
lappend result [punk::lib::lindex_resolve_basic 5 4-5] ;# -> -Inf
lappend result [punk::lib::lindex_resolve 5 end-5] ;# -> -Inf out of bounds on lower side
lappend result [punk::lib::lindex_resolve_basic 5 end-5] ;# -> -Inf
lappend result [punk::lib::lindex_resolve 5 4+-5] ;# -> -Inf out of bounds on lower side
lappend result [punk::lib::lindex_resolve 5 end+-5] ;# -> -Inf out of bounds on lower side
@ -51,7 +59,10 @@ namespace eval ::testspace {
-cleanup {
}\
-result [list\
Inf Inf Inf Inf -Inf -Inf -Inf -Inf -Inf -Inf
Inf -Inf Inf -Inf\
Inf -Inf Inf -Inf\
-Inf -Inf -Inf -Inf\
-Inf -Inf -Inf -Inf\
]
test lindex_resolve_endoffset_errors {test some end-like offsets that should error}\
@ -76,4 +87,58 @@ namespace eval ::testspace {
1 1 1 1 1 1
]
test lindex_resolve_expressions {test some expressions that should work}\
-setup $common -body {
#e.g indices {0 1 2 3 4} n = 5
lappend result [punk::lib::lindex_resolve 5 -0]
lappend result [punk::lib::lindex_resolve 5 +0]
lappend result [punk::lib::lindex_resolve 5 -1] ;# -Inf
lappend result [punk::lib::lindex_resolve 5 +1]
lappend result [punk::lib::lindex_resolve 5 -1+0] ;# -Inf
lappend result [punk::lib::lindex_resolve 5 -1+2]
lappend result [punk::lib::lindex_resolve 5 -1-1] ;# -Inf
lappend result [punk::lib::lindex_resolve 5 -1+3]
lappend result [punk::lib::lindex_resolve 5 +1+3]
lappend result [punk::lib::lindex_resolve 5 1++1]
lappend result [punk::lib::lindex_resolve 5 -1++1]
}\
-cleanup {
}\
-result [list\
0 0 -Inf 1 -Inf 1 -Inf 2 4 2 0
]
test lindex_resolve_ {test some expressions that should work}\
-setup $common -body {
#e.g indices {0 1 2 3 4} n = 5
lappend result [punk::lib::lindex_resolve 5 -0]
lappend result [punk::lib::lindex_resolve 5 +0]
lappend result [punk::lib::lindex_resolve 5 -1] ;# -Inf
lappend result [punk::lib::lindex_resolve 5 +1]
lappend result [punk::lib::lindex_resolve 5 -1+0] ;# -Inf
lappend result [punk::lib::lindex_resolve 5 -1+2]
lappend result [punk::lib::lindex_resolve 5 -1-1] ;# -Inf
lappend result [punk::lib::lindex_resolve_basic 5 -1-1] ;# -Inf
lappend result [punk::lib::lindex_resolve 5 -1+3]
lappend result [punk::lib::lindex_resolve 5 +1+3]
lappend result [punk::lib::lindex_resolve 5 1++1]
lappend result [punk::lib::lindex_resolve 5 -1++1]
lappend result [punk::lib::lindex_resolve 5 +0++6] ;# Inf
lappend result [punk::lib::lindex_resolve_basic 5 +0++6] ;# -Inf (since basic returns -Inf for out of bounds on either side)
}\
-cleanup {
}\
-result [list\
0 0 -Inf 1 -Inf 1 -Inf -Inf 2 4 2 0 Inf -Inf
]
}

57
src/modules/test/punk/#modpod-lib-999999.0a1.0/lib-0.1.3_testsuites/lib/lineprocessing.test

@ -0,0 +1,57 @@
package require tcltest
namespace eval ::testspace {
namespace import ::tcltest::*
variable common {
set result ""
}
test linelist_default_trimming {}\
-setup $common -body {
#default -block is {trimhead1 trimtail1} which should trim 1 line from head and tail if they are empty.
lappend result [punk::lib::linelist "line1\nline2\nline3"] ;# -> {line1 line2 line3}
lappend result [punk::lib::linelist "\nline1\nline2\nline3\n"] ;# -> {line1 line2 line3}
lappend result [punk::lib::linelist "\n\nline1\nline2\nline3\n\n"] ;# -> {{} line1 line2 line3 {}}
lappend result [punk::lib::linelist "\n\nline1\nline2\nline3\n\n\n"] ;# -> {{} line1 line2 line3 {} {}}
lappend result [punk::lib::linelist "\n\nline1\nline2\nline3\n"] ;# -> {{} line1 line2 line3}
#make sure only head and tail are trimmed, not inner empty lines.
lappend result [punk::lib::linelist "\nline1\n\nline2\n\n\nline3\n"] ;# -> {line1 {} line2 {} {} line3}
}\
-cleanup {
}\
-result [list\
{line1 line2 line3} \
{line1 line2 line3} \
{{} line1 line2 line3 {}} \
{{} line1 line2 line3 {} {}} \
{{} line1 line2 line3} \
{line1 {} line2 {} {} line3} \
]
test linelist_block_collateempty {}\
-setup $common -body {
#with -block collateempty empty (and without trimhead1 and trimtail1) lines should be collated together into single empty lines.
lappend result [punk::lib::linelist -block collateempty "line1\nline2\nline3"] ;# -> {line1 line2 line3}
lappend result [punk::lib::linelist -block collateempty "\nline1\nline2\nline3\n"] ;# -> {{} line1 line2 line3 {}}
lappend result [punk::lib::linelist -block collateempty "\n\nline1\nline2\nline3\n\n"] ;# -> {{} line1 line2 line3 {}}
lappend result [punk::lib::linelist -block collateempty "\n\nline1\nline2\nline3\n\n\n"] ;# -> {{} line1 line2 line3 {}}
lappend result [punk::lib::linelist -block collateempty "\n\nline1\nline2\nline3\n"] ;# -> {{} line1 line2 line3 {}}
lappend result [punk::lib::linelist -block collateempty "\nline1\n\nline2\n\n\nline3\n"] ;# -> {{} line1 line2 line3 {}}
}\
-cleanup {
}\
-result [list\
{line1 line2 line3} \
{{} line1 line2 line3 {}} \
{{} line1 line2 line3 {}} \
{{} line1 line2 line3 {}} \
{{} line1 line2 line3 {}} \
{{} line1 line2 line3 {}}
]
}

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

@ -2116,7 +2116,7 @@ tcl::namespace::eval textblock {
set ansibase_header [tcl::dict::get $o_opts_table -ansibase_header] ;#merged to single during configure
set ansiborder_header [tcl::dict::get $o_opts_table -ansiborder_header]
if {[tcl::dict::get $o_opts_table -frametype_header] eq "block"} {
set extrabg [punk::ansi::codetype::sgr_merge_singles [list $ansibase_header] -filter_fg 1]
set extrabg [punk::ansi::codetype::sgr_merge_singles [list $ansibase_header] -filter_fg]
set ansiborder_final $ansibase_header$ansiborder_header$extrabg
} else {
set ansiborder_final $ansibase_header$ansiborder_header
@ -2504,7 +2504,7 @@ tcl::namespace::eval textblock {
if {[tcl::dict::get $o_opts_table -frametype] eq "block"} {
#block is the only style where bg colour can fill the frame content area exactly if the L-shaped border elements are styled
#we need to only accept background ansi codes from the columndef ansibase for this
set col_bg [punk::ansi::codetype::sgr_merge_singles [list $opt_col_ansibase] -filter_fg 1] ;#special merge for block borders - don't override fg colours
set col_bg [punk::ansi::codetype::sgr_merge_singles [list $opt_col_ansibase] -filter_fg] ;#special merge for block borders - don't override fg colours
set border_ansi $body_ansibase$body_ansiborder$col_bg
} else {
set border_ansi $body_ansibase$body_ansiborder
@ -2520,7 +2520,7 @@ tcl::namespace::eval textblock {
set row_bg ""
set row_ansibase [tcl::dict::get $o_rowdefs $r -ansibase]
if {$row_ansibase ne ""} {
set row_bg [punk::ansi::codetype::sgr_merge_singles [list $row_ansibase] -filter_fg 1]
set row_bg [punk::ansi::codetype::sgr_merge_singles [list $row_ansibase] -filter_fg]
}
#todo - joinleft,joinright,joindown based on opts in args
@ -2542,8 +2542,8 @@ tcl::namespace::eval textblock {
lappend ptlens [string length $pt]
}
#set takebg [lindex $parts end-1]
#set cell_bg [punk::ansi::codetype::sgr_merge_singles [list $takebg] -filter_fg 1]
set cell_bg [punk::ansi::codetype::sgr_merge_singles $codes -filter_fg 1 -filter_reset 1]
#set cell_bg [punk::ansi::codetype::sgr_merge_singles [list $takebg] -filter_fg]
set cell_bg [punk::ansi::codetype::sgr_merge_singles $codes -filter_fg -filter_reset]
#puts --->[ansistring VIEW $codes]
if {[punk::ansi::codetype::is_sgr_reset [lindex $codes end-1]]} {
@ -2554,7 +2554,7 @@ tcl::namespace::eval textblock {
set ansibase ""
set row_ansibase ""
if {$ftblock} {
set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_body_col_row] -filter_bg 1]
set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_body_col_row] -filter_bg]
set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_final $cell_bg]]
}
set cell_ansibase $cell_ansi_tail
@ -2577,7 +2577,7 @@ tcl::namespace::eval textblock {
# set ansibase ""
# set row_ansibase ""
# if {$ftblock} {
# set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_body_col_row] -filter_bg 1]
# set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_body_col_row] -filter_bg]
# }
# set cell_ansibase $cell_ansi_tail
# } else {
@ -2643,7 +2643,7 @@ tcl::namespace::eval textblock {
}
#return empty (zero content height) row if no rows
if {![llength $cells]} {
set basebg [punk::ansi::codetype::sgr_merge_singles [list $body_ansibase] -filter_fg 1]
set basebg [punk::ansi::codetype::sgr_merge_singles [list $body_ansibase] -filter_fg]
set ansiborder_final [punk::ansi::codetype::sgr_merge [list $basebg $body_ansiborder]]
set joins [lremove $joins [lsearch $joins down*]]
@ -4497,7 +4497,7 @@ tcl::namespace::eval textblock {
foreach {pt code} [lrange $parts 2 end] {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
#set parts [linsert $parts $code_idx+1 $base]
ledit parts $code_idx+1 $code_idx $base
ledit parts $code_idx+1 -1 $base
}
incr code_idx 2
}
@ -4527,8 +4527,9 @@ tcl::namespace::eval textblock {
}
}
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set parts [linsert $parts [expr {$code_idx+1+$offset}] $base]
#set parts [linsert $parts [expr {$code_idx+1+$offset}] $base]
#ledit parts [expr {$code_idx+1+$offset}] $code_idx+$offset $base
ledit parts [expr {$code_idx+1+$offset}] -1 $base
incr offset
}
incr code_idx 2
@ -4912,7 +4913,8 @@ tcl::namespace::eval textblock {
set colour2 [tcl::string::map [list rainbow [lindex $rainbow_list $i]] $colour]
set ansi [a+ {*}$colour2]
set ansicode [punk::ansi::codetype::sgr_merge_list "" $ansi]
#set ansicode [punk::ansi::codetype::sgr_merge_list "" $ansi]
set ansicode [punk::ansi::codetype::sgr_merge [list $ansi]]
lappend clist ${ansicode}$c$RST
}
if {$noreset} {
@ -4926,8 +4928,9 @@ tcl::namespace::eval textblock {
set block ""
for {set r 0} {$r < $size} {incr r} {
set colour2 [tcl::string::map [list rainbow [lindex $rainbow_list $r]] $colour]
set ansi [a+ {*}$colour2]
set ansicode [punk::ansi::codetype::sgr_merge_list "" $ansi]
set ansi [a+ {*}$colour2] ;#not always a single SGR sequence (ESC...m) e.g when contains 'underdotted'
#set ansicode [punk::ansi::codetype::sgr_merge_list "" $ansi]
set ansicode [punk::ansi::codetype::sgr_merge [list $ansi]]
set row "$ansicode"
foreach c $charsubset {
append row $c
@ -5393,10 +5396,11 @@ tcl::namespace::eval textblock {
}
r-1 {
if {[lindex $line_chunks end] eq ""} {
#Insert so that pad *ends* up at position end-2
set line_chunks [linsert $line_chunks end-2 $pad]
#breaks layout e.g subtables in: i i
#why?
#ledit line_chunks end-2 end-3 $pad
#Note that 'ledit line_chunks end-2 -1 $pad' is not equivalent,
#because linsert behaves differently depending on whether the index is start-relative or end-relative.
#(breaks layout e.g subtables in: i i)
} else {
lappend line_chunks $pad
}
@ -5487,6 +5491,9 @@ tcl::namespace::eval textblock {
r-2 {
if {[lindex $line_chunks end] eq ""} {
set line_chunks [linsert $line_chunks end-2 $pad]
#(ledit line_chunks end-2 -1 $pad) is not equivalent to linsert
#because of the different behaviour of end-relative vs start-relative indices with linsert
#- it can break layout e.g subtables in: i i
} else {
lappend line_chunks $pad
}

405
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/overtype-1.7.4.tm

@ -90,7 +90,9 @@ package require punk::assertion
# - need to extract and replace ansi codes?
tcl::namespace::eval overtype {
namespace import ::punk::assertion::assert
if {[info commands ::overtype::assert] eq ""} {
namespace import ::punk::assertion::assert
}
punk::assertion::active true
namespace path ::punk::lib
@ -625,7 +627,7 @@ tcl::namespace::eval overtype {
#set overtext [lpop inputchunks 0] ;#could be a list 'ansisplit' or text 'plain|mixed'
lassign [lpop inputchunks 0] overtext_type overtext
#use eq test with emptystring instead of 'string length' - test for emptiness shouldn't cause shimmering if popped inputchunks member if an 'ansisplit' list
#use eq test with emptystring instead of 'string length' - test for emptiness shouldn't cause shimmering if popped inputchunks member is an 'ansisplit' list
if {$overtext eq ""} {
incr loop
continue
@ -728,7 +730,7 @@ tcl::namespace::eval overtype {
set existing_reverse_state 0
#split_codes_single is single esc sequence - but could have multiple sgr codes within one esc sequence
#e.g \x1b\[0;31;7m has a reset,colour red and reverse
set codeinfo [punk::ansi::codetype::sgr_merge [list $replay_codes_overlay] -info 1]
set codeinfo [punk::ansi::codetype::sgr_merge [list $replay_codes_overlay] -info]
set codestate_reverse [dict get $codeinfo codestate reverse]
switch -- $codestate_reverse {
7 {
@ -863,7 +865,7 @@ tcl::namespace::eval overtype {
# ----
# review
set col $post_render_col
#just because it's out of range of the renderwidth - doesn't mean a move down should jump to witin the range - 2025
#just because it's out of range of the renderwidth - doesn't mean a move down should jump to within the range - 2025
#----
#set existingdata [lindex $outputlines [expr {$post_render_row -1}]]
@ -908,7 +910,7 @@ tcl::namespace::eval overtype {
#It would perhaps be more properly handled as a queue of instructions from our initial renderline call
#we don't need to worry about overflow next call (?)- but we should carry forward our gx and ansi stacks
puts stdout ">>>[a+ red bold]overflow_right during restore_cursor[a]"
puts stdout ">>>renderspace<<<[a+ red bold]overflow_right during restore_cursor[a]"
set sub_info [overtype::renderline\
-info 1\
@ -924,7 +926,7 @@ tcl::namespace::eval overtype {
tcl::dict::set vtstate autowrap_mode [tcl::dict::get $sub_info autowrap_mode] ;#nor this..
#todo!!!
# 2025 fix - this does nothing - so what uses it?? create a test!
# 2025 fix - this does nothing - so what is the intention?? create a test!
linsert outputlines $renderedrow $foldline
#review - row & col set by restore - but not if there was no save..
}
@ -1053,7 +1055,9 @@ tcl::namespace::eval overtype {
set overflow_right ""
} else {
if {[tcl::dict::get $vtstate autowrap_mode]} {
set outputlines [linsert $outputlines $renderedrow $overflow_right]
#set outputlines [linsert $outputlines $renderedrow $overflow_right]
#ledit outputlines $renderedrow $renderedrow-1 $overflow_right
ledit outputlines $renderedrow -1 $overflow_right
set overflow_right ""
set row [expr {$renderedrow + 2}]
} else {
@ -1150,7 +1154,8 @@ tcl::namespace::eval overtype {
if {$insert_lines_above > 0} {
set row $renderedrow
#set outputlines [linsert $outputlines $renderedrow-1 {*}[lrepeat $insert_lines_above ""]]
ledit outputlines $renderedrow-1 $renderedrow-2 {*}[lrepeat $insert_lines_above ""]
#ledit outputlines $renderedrow-1 $renderedrow-2 {*}[lrepeat $insert_lines_above ""]
ledit outputlines $renderedrow-1 -1 {*}[lrepeat $insert_lines_above ""]
incr row [expr {$insert_lines_above -1}] ;#we should end up on the same line of text (at a different index), with new empties inserted above
#? set row $post_render_row #can renderline tell us?
}
@ -1461,6 +1466,7 @@ tcl::namespace::eval overtype {
set nextprefix_list $overflow_right_pt_code_pt
} else {
#merge tail and head
#ledit <list> end end <val> will work with empty list (ledit <list> end <val> does not)
ledit nextprefix_list end end "[lindex $nextprefix_list end][lindex $overflow_right_pt_code_pt 0]"
lappend nextprefix_list {*}[lrange $overflow_right_pt_code_pt 1 end]
}
@ -1476,16 +1482,17 @@ tcl::namespace::eval overtype {
}
if 0 {
if {$nextprefix ne ""} {
set nextoveridx [expr {$overidx+1}]
if {$nextoveridx >= [llength $inputchunks]} {
lappend inputchunks $nextprefix
} else {
#lset overlines $nextoveridx $nextprefix[lindex $overlines $nextoveridx]
set inputchunks [linsert $inputchunks $nextoveridx $nextprefix]
if {$nextprefix ne ""} {
set nextoveridx [expr {$overidx+1}]
if {$nextoveridx >= [llength $inputchunks]} {
lappend inputchunks $nextprefix
} else {
#lset overlines $nextoveridx $nextprefix[lindex $overlines $nextoveridx]
#set inputchunks [linsert $inputchunks $nextoveridx $nextprefix]
ledit inputchunks $nextoveridx -1 $nextprefix
}
}
}
}
if {[llength $nextprefix_list]} {
#set inputchunks [linsert $inputchunks 0 $nextprefix]
@ -1669,13 +1676,17 @@ tcl::namespace::eval overtype {
}
}
}
lappend outputlines $rendered
#JULZ
#lappend outputlines $rendered
lappend outputlines $rendered\x1b\[m
#lappend outputlines [renderline -insert_mode 0 -transparent $opt_transparent $undertext $overtext]
} else {
#background block is wider than or equal to data for this line
#lappend outputlines [renderline -insert_mode 0 -startcolumn [expr {$left_exposed + 1}] -transparent $opt_transparent -exposed1 $opt_exposed1 -exposed2 $opt_exposed2 $undertext $overtext]
set rinfo [renderline -info 1 -insert_mode 0 -startcolumn [expr {$left_exposed + 1}] -transparent $opt_transparent -exposed1 $opt_exposed1 -exposed2 $opt_exposed2 $undertext $overtext]
lappend outputlines [tcl::dict::get $rinfo result]
#JULZ
#lappend outputlines [tcl::dict::get $rinfo result]
lappend outputlines [tcl::dict::get $rinfo result]\x1b\[m
}
set replay_codes_underlay [tcl::dict::get $rinfo replay_codes_underlay]
set replay_codes_overlay [tcl::dict::get $rinfo replay_codes_overlay]
@ -1787,6 +1798,9 @@ tcl::namespace::eval overtype {
set overflowlength [expr {$overtext_datalen - $renderwidth}]
if {$overflowlength > 0} {
#raw overtext wider than undertext column
#broken:
#todo - renderline -overflow is invalid.
# we need renderline to support -expand_left ??
set rinfo [renderline\
-info 1\
-insert_mode 0\
@ -1814,13 +1828,18 @@ tcl::namespace::eval overtype {
}
}
}
lappend outputlines $rendered
#JULZ
#lappend outputlines $rendered
lappend outputlines $rendered\x1b\[m
} else {
#padded overtext
#lappend outputlines [renderline -insert_mode 0 -transparent $opt_transparent -startcolumn [expr {$left_exposed + 1}] $undertext $overtext]
#Note - we still need overflow(exapnd_right) here - as although the overtext is short - it may oveflow due to the startoffset
set rinfo [renderline -info 1 -insert_mode 0 -transparent $opt_transparent -expand_right $opt_overflow -startcolumn [expr {$left_exposed + 1 + $startoffset}] $undertext $overtext]
lappend outputlines [tcl::dict::get $rinfo result]
#JULZ
#lappend outputlines [tcl::dict::get $rinfo result]
lappend outputlines [tcl::dict::get $rinfo result]\x1b\[m
}
set replay_codes [tcl::dict::get $rinfo replay_codes]
set replay_codes_underlay [tcl::dict::get $rinfo replay_codes_underlay]
@ -2014,7 +2033,8 @@ tcl::namespace::eval overtype {
# }
#}
}
lappend outputlines $rendered
#JULZ
lappend outputlines $rendered\x1b\[m
} else {
#padded overtext
#lappend outputlines [renderline -insert_mode 0 -transparent $opt_transparent -startcolumn [expr {$left_exposed + 1}] $undertext $overtext]
@ -2023,7 +2043,9 @@ tcl::namespace::eval overtype {
#puts stderr "--> [ansistring VIEW -lf 1 -nul 1 $rinfo] <--"
set overflow_right [tcl::dict::get $rinfo overflow_right]
set unapplied [tcl::dict::get $rinfo unapplied]
lappend outputlines [tcl::dict::get $rinfo result]
#JULZ
#lappend outputlines [tcl::dict::get $rinfo result]
lappend outputlines [tcl::dict::get $rinfo result]\x1b\[m
}
set replay_codes [tcl::dict::get $rinfo replay_codes]
set replay_codes_underlay [tcl::dict::get $rinfo replay_codes_underlay]
@ -2136,6 +2158,24 @@ tcl::namespace::eval overtype {
}]
}
proc stack_eq {a b} {
#single level list equality test to avoid generating internal string representations of the lists unnecessarily.
if {[llength $a] != [llength $b]} {
return 0
}
foreach code1 $a code2 $b {
if {$code1 ne $code2} {
return 0
}
}
return 1
}
#todo: tests
#set j [overtype::renderline -transparent " " -insert_mode 0 -expand_right 1 "[a+ red underline]xxx[a+ blue][a+ nounderline]" "[a green]J" ]yyy
# yyy should be blue with no underline - and the J should be green - and the x's should be red with underline and the J should overwrite the first x
#At the moment we return a reset at the end of the renderline result instead of the replay codes.
proc renderline {args} {
#todo - fix 'unapplied' mechanism.This is particularly inefficient for long lines, or data such as binarytext which is not line-based.
#All unapplied data is re-split/reprocessed repeatedly for each line! This is very wasteful and slow.
@ -2476,7 +2516,9 @@ tcl::namespace::eval overtype {
if {$maybemouse ne "<" && [tcl::string::index $code end] eq "m"} {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set u_codestack [list "\x1b\[m"]
#will normalize all resets to the same code - including 8bit reset.
#set u_codestack [list "\x1b\[m"]
set u_codestack [list $code]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set u_codestack [list $code]
} else {
@ -2557,6 +2599,17 @@ tcl::namespace::eval overtype {
}
}
#----------------------------------------
#set test_c [showlist $undercols]
##set test_s [showlist $understacks %ansiview]
#set sview [list]
#foreach us $understacks {
# lappend sview [ansistring VIEW $us]
#}
#set test_s [showlist $sview]
#puts stderr "undercols/stacks:\n[textblock::join -- $test_c " " $test_s]"
#----------------------------------------
if {$opt_width ne "\uFFEF"} {
set renderwidth $opt_width
} else {
@ -2567,7 +2620,10 @@ tcl::namespace::eval overtype {
#trailing codes in effect for underlay
if {[llength $u_codestack]} {
#set replay_codes_underlay [join $u_codestack ""]
set replay_codes_underlay [punk::ansi::codetype::sgr_merge_list {*}$u_codestack]
#set replay_codes_underlay [punk::ansi::codetype::sgr_merge_list {*}$u_codestack]
#u_codestack was built from codes split using split_codes_single
#- so should already be simplified to single codes with no multiple SGR params in one code
set replay_codes_underlay [punk::ansi::codetype::sgr_merge_singles $u_codestack]
} else {
set replay_codes_underlay ""
}
@ -2767,13 +2823,17 @@ tcl::namespace::eval overtype {
} else {
lappend overlay_grapheme_control_stacks $o_codestack
#there will always be an empty code at end due to foreach on 2 vars with odd-sized list ending with pt (overmap coming from perlish split)
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set o_codestack [list "\x1b\[m"] ;#reset better than empty list - fixes some ansi art issues
set code_endswith_m [expr {[tcl::string::index $code end] eq "m"}] ;#skip SGR regexp testing for cases that don't end with m - as they can't be SGR
if {$code_endswith_m && [punk::ansi::codetype::is_sgr_reset $code]} {
#reset better than empty list - fixes some ansi art issues
#set o_codestack [list "\x1b\[m"]
set o_codestack [list $code]
lappend overlay_grapheme_control_list [list sgr $code]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
} elseif {$code_endswith_m && [punk::ansi::codetype::has_sgr_leadingreset $code]} {
set o_codestack [list $code]
lappend overlay_grapheme_control_list [list sgr $code]
} elseif {[priv::is_sgr $code]} {
} elseif {$code_endswith_m && [priv::is_sgr $code]} {
#basic simplification first - remove straight dupes
set dup_posns [lsearch -all -exact $o_codestack $code] ;#must be -exact because of square-bracket glob chars
set o_codestack [lremove $o_codestack {*}$dup_posns]
@ -2827,7 +2887,12 @@ tcl::namespace::eval overtype {
lappend overstacks_gx $o_gxstack
#set replay_codes_overlay [join $o_codestack ""]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}$o_codestack]
if {[llength $o_codestack]} {
#set replay_codes_overlay [join $o_codestack ""]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_singles $o_codestack]
} else {
set replay_codes_overlay [list]
}
#if {[tcl::dict::exists $overstacks $max_overlay_grapheme_index]} {
# set replay_codes_overlay [join [tcl::dict::get $overstacks $max_overlay_grapheme_index] ""]
@ -2952,7 +3017,7 @@ tcl::namespace::eval overtype {
#specials - each shoud have it's own test of what to do if it happens after overflow_idx reached
switch -- $chtest {
"<lf>" {
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
if {$idx == 0} {
#puts "---a <lf> at col 1"
#linefeed at column 1
@ -3069,8 +3134,7 @@ tcl::namespace::eval overtype {
set next_gc [lindex $overlay_grapheme_control_list $gci+1] ;#next grapheme or control
lassign $next_gc next_type next_item
if {$autowrap_mode} {
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci-1]]
#set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci-1]]
#don't incr idx beyond the overflow_idx
#idx_over already incremented - decrement so current overlay grapheme stacks go to unapplied
incr idx_over -1
@ -3087,7 +3151,7 @@ tcl::namespace::eval overtype {
#no point throwing back to caller for each grapheme that is overflowing
#without this branch - renderline would be called with overtext reducing only by one grapheme per call
#processing a potentially long overtext each time (ie - very slow)
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#JMN4
}
@ -3427,7 +3491,7 @@ tcl::namespace::eval overtype {
switch -exact -- $code_end {
A {
#Row move - up
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#todo
lassign [split $param {;}] num modifierkey
if {$modifierkey ne ""} {
@ -3452,7 +3516,7 @@ tcl::namespace::eval overtype {
#CUD - Cursor Down
#Row move - down
lassign [split $param {;}] num modifierkey
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#move down
if {$modifierkey ne ""} {
puts stderr "modifierkey:$modifierkey"
@ -3503,7 +3567,7 @@ tcl::namespace::eval overtype {
incr cursor_column $num
} else {
if {$autowrap_mode} {
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#jmn
if {$idx == $overflow_idx} {
incr num
@ -3598,7 +3662,7 @@ tcl::namespace::eval overtype {
set cursor_column 1
set idx 0
} else {
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
incr cursor_column -$num
priv::render_to_unapplied $overlay_grapheme_control_list $gci
set instruction wrapmovebackward
@ -3626,7 +3690,9 @@ tcl::namespace::eval overtype {
set cursor_column 1
set cursor_row [expr {$cursor_row + $downmove}]
set idx [expr {$cursor_column -1}]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
#sgr_merge_list
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#sgr_merge_singles ??
incr idx_over
priv::render_to_unapplied $overlay_grapheme_control_list $gci
set instruction move
@ -3647,7 +3713,7 @@ tcl::namespace::eval overtype {
set cursor_row 1
}
set idx [expr {$cursor_column - 1}]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
incr idx_over
priv::render_to_unapplied $overlay_grapheme_control_list $gci
set instruction move
@ -3656,6 +3722,7 @@ tcl::namespace::eval overtype {
}
G {
#CHA - Cursor Horizontal Absolute (move to absolute column no)
#see also HPA - Horizontal Position Absolute (same functionality)
if {$param eq ""} {
set targetcol 1
} else {
@ -3680,6 +3747,29 @@ tcl::namespace::eval overtype {
set cursor_column $targetcol
#puts stderr "renderline absolute col move ESC G (TEST)"
}
` {
#https://vt100.net/docs/vt510-rm/HPA.html
#docs don't mention that it defaults to one if $parm omitted - but it seems to do in practice
if {$param eq ""} {
set targetcol 1
} else {
set targetcol $param
if {![string is integer -strict $targetcol]} {
puts stderr "renderline HPA (Horizontal Position Absolute) error. Unrecognised parameter '$param'"
}
set targetcol [expr {$param}]
set max [llength $outcols]
if {$overflow_idx == -1} {
incr max
}
if {$targetcol > $max} {
puts stderr "renderline HPA (Horizontal Position Absolute) error. Param '$param' > max: $max"
set targetcol $max
}
}
set idx [expr {($targetcol -1) + $opt_colstart -1}]
set cursor_column $targetcol
}
H - f {
#CSI n;m H - CUP - Cursor Position
@ -3727,7 +3817,7 @@ tcl::namespace::eval overtype {
set cursor_row $target_row
set cursor_column $target_column
set idx [expr {$cursor_column -1}]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
incr idx_over
priv::render_to_unapplied $overlay_grapheme_control_list $gci
set instruction move
@ -3758,7 +3848,7 @@ tcl::namespace::eval overtype {
set cursor_row 1
set cursor_column 1
set idx [expr {$cursor_column -1}]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
incr idx_over
if {[llength $outcols]} {
priv::render_erasechar 0 [llength $outcols]
@ -4000,7 +4090,8 @@ tcl::namespace::eval overtype {
}
}
#append cursor_saved_attributes [join $sgr_stack ""]
append cursor_saved_attributes [punk::ansi::codetype::sgr_merge_list {*}$sgr_stack]
#append cursor_saved_attributes [punk::ansi::codetype::sgr_merge_list {*}$sgr_stack]
append cursor_saved_attributes [punk::ansi::codetype::sgr_merge $sgr_stack]
#as there is apparently only one cursor storage element we don't need to throw back to the calling loop for a save.
@ -4024,7 +4115,7 @@ tcl::namespace::eval overtype {
# set replay_codes_overlay $cursor_saved_attributes ;#empty - or last save if it happend in this input chunk
#} else {
#jj
#set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
#set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay ""
#}
@ -4398,7 +4489,7 @@ tcl::namespace::eval overtype {
#vt102-docs: "Moves cursor up one line in same column. If cursor is at top margin, screen performs a scroll-down"
puts stderr "overtype::renderline ESC M not fully implemented"
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#move up
incr cursor_row -1
if {$cursor_row < 1} {
@ -4743,6 +4834,9 @@ tcl::namespace::eval overtype {
#puts stderr "first_tail_null_posn: $first_tail_null_posn"
#puts stderr "colview: [ansistring VIEW $outcols]"
#NOTE understacks has been updated with data from the overlay - so it should reflect the final state of the stacks for each grapheme in outcols
foreach ch $outcols {
#puts "---- [ansistring VIEW $ch]"
@ -4766,15 +4860,58 @@ tcl::namespace::eval overtype {
if {$i < [llength $understacks]} {
#set cstack [tcl::dict::get $understacks $i]
set cstack [lindex $understacks $i]
if {$cstack ne $prevstack} {
if {[llength $prevstack] && ![llength $cstack]} {
#This reset is important e.g testfile fruit.ans - we get overhang on rhs without it. But why is cstack empty?
append sgrleader \033\[m
#use stack_eq for depth 1 comparison without generating string rep.
if {![stack_eq $cstack $prevstack]} {
#possible SGR attribute change.
if {[llength $prevstack]} {
if {![llength $cstack]} {
#why is cstack empty?
#a) no ansi in underlay and we are at a position 2 after an overlay insertion.
# (position 1 after overlay insertion should already have had a reset inserted)
#b) no ansi in overlay and we are at an overlay insertion point.
#--------------
#review
#todo? consider testing next-char's understack when applying each overlay char in the main loop.
#if empty or has no leading reset - we need to add a leading reset at that point.
#--------------
#--------
#following statement is FALSE - (historical info). Doesn't seem to apply.
#This reset is important e.g testfile fruit.ans - we get overhang on rhs without it.
#append sgrleader \033\[m
#--------
##test
#set view_prev ""
#foreach ps $prevstack {
# append view_prev [ansistring VIEW -lf 1 -vt 1 -nul 1 $ps]
#}
#puts stderr "col $i, ch: $ch - cstack empty vs prevstack $view_prev"
} else {
#without this we get extra redundant codes in some places.
#e.g a continuous string of underlay that originally had \x1b\[31m red text,
#but then when an overlay char is inserted near the start, the following underlay char (insertion index +1) codestack had a reset added.
#All subsequent underlay chars in the same run of plaintext don't have the reset and so appear 'different' but are actually part of the same run.
#check if actually different. ie if current stack actually changes anything from previous stack when merged together.
set prevmerge [punk::ansi::codetype::sgr_merge $prevstack]
set currmerge [punk::ansi::codetype::sgr_merge $cstack]
set together [punk::ansi::codetype::sgr_merge [list $prevmerge $currmerge]]
if {$together ne $prevmerge} {
#stacks are different enough that we need to output something
#if {{[punk::ansi::codetype::has_sgr_leading_reset $currmerge]}} {
#}
append sgrleader $currmerge
}
}
} else {
append sgrleader [punk::ansi::codetype::sgr_merge_list {*}$cstack]
if {[llength $cstack]} {
append sgrleader [punk::ansi::codetype::sgr_merge $cstack]
}
}
set prevstack $cstack
}
set prevstack $cstack
} else {
set prevstack [list]
}
@ -4797,7 +4934,8 @@ tcl::namespace::eval overtype {
#if {[llength $prevstack] && ![llength $cstack]} {
# append sgrleader \033\[m
#}
append sgrleader [punk::ansi::codetype::sgr_merge_list {*}$cstack]
#append sgrleader [punk::ansi::codetype::sgr_merge_list {*}$cstack]
append sgrleader [punk::ansi::codetype::sgr_merge $cstack]
append overflow_right $sgrleader
append overflow_right $ch
} else {
@ -4853,14 +4991,50 @@ tcl::namespace::eval overtype {
set replay_codes ""
if {[llength $understacks] > 0} {
if {$overflow_idx == -1} {
#set tail_idx [tcl::dict::size $understacks]
set tail_idx [llength $understacks]
} else {
set tail_idx [llength $undercols]
}
if {$tail_idx-1 < [llength $understacks]} {
if {$tail_idx == [llength $undercols]} {
#we got to the end of the original underlay
#- so we want the full stack at the end of the original underlay ie including trailing codes which are not associated with any grapheme in the underlay
#but would be in effect for any text after the underlay.
#---------------------
#REVIEW - determine if last col was overwritten by overlay?
#how best to determine if last underlay column was overwritten by overlay?
#we could track in the main loop whether each underlay column was overwritten by overlay
#This seems like the best mechanism, because the overlay ANSI can include movement codes, so the underlay can be overwritten in any order.
#We should consider that just because the last grapheme was overwritten, that doesn't necessarily mean we should disregard the trailing codes
#perhaps trailing underlay codes are never overwritten unless the overlay extends beyond the end of the underlay - in which case we can just check if overlay extends beyond end of underlay to determine whether to include trailing underlay codes in replay or not.
#if overlay extends beyond end of underlay - we use the overlay stack at the end of the underlay as the replay codes, which won't include any trailing underlay codes.
#---------------------
if {[lindex $undermap end] eq ""} {
#there were trailing codes in the underlay with no grapheme - we want to include those in the replay as they would affect any text after the underlay
#we need to backtrack from the end of the underlay to find the last grapheme with codes, and merge those codes with any trailing codes in the underlay with no grapheme
set tailcodes [list] ;#build in reverse order.
foreach {pt code} [lreverse $undermap] {
if {$pt ne ""} {
break
}
lappend tailcodes $code
}
set tailcodes [lreverse $tailcodes]
#set tailcodes [lindex $undermap end-1]
set laststack [lindex $understacks $tail_idx-1]
lappend laststack {*}$tailcodes
set replay_codes [punk::ansi::codetype::sgr_merge $laststack] ;#stack at end of underlay including trailing codes
} else {
#last part of underlay was plain text with no trailing codes - we can just use the stack at the last grapheme of the underlay
set replay_codes [punk::ansi::codetype::sgr_merge [lindex $understacks $tail_idx-1]] ;#stack at end of underlay
}
} elseif {$tail_idx-1 < [llength $understacks]} {
#set replay_codes [join [lindex $understacks $tail_idx-1] ""] ;#tail replay codes
set replay_codes [punk::ansi::codetype::sgr_merge_list {*}[lindex $understacks $tail_idx-1]] ;#tail replay codes
#set replay_codes [punk::ansi::codetype::sgr_merge_list {*}[lindex $understacks $tail_idx-1]] ;#tail replay codes
set replay_codes [punk::ansi::codetype::sgr_merge [lindex $understacks $tail_idx-1]] ;#tail replay codes
}
if {$tail_idx-1 < [llength $understacks_gx]} {
set gx0 [lindex $understacks_gx $tail_idx-1]
@ -4876,10 +5050,33 @@ tcl::namespace::eval overtype {
#pdict $understacks
if {[punk::ansi::ta::detect_sgr $outstring]} {
append outstring [punk::ansi::a] ;#without this - we would get for example, trailing backgrounds after rightmost column
#JULZ
#The caller is responsible for adding a reset at the end of returned lines depending on how they want to use it - so we don't add one here.
#<deprecated>
#append outstring [punk::ansi::a] ;#without this - we would get for example, trailing backgrounds after rightmost column
#</deprecated>
#we only want to append the replay codes if they are different to those already in effect at the end of the rendered line.
if {$overflow_idx == -1} {
set tail_idx [llength $understacks]
} else {
set tail_idx [llength $undercols]
}
set laststack [lindex $understacks $tail_idx-1]
set laststackmerge [punk::ansi::codetype::sgr_merge $laststack]
if {$replay_codes ne $laststackmerge} {
append outstring $replay_codes
}
#review
#close off any open gx?
#probably should - and overflow_right reopen?
#probably not, this is akin to adding a reset to close off open SGR codes, which we specifically don't do.
#caller will need to close off any open gx at the end of the line if they want to, and provide appropriate replay codes for the next line if they want to maintain gx state across lines.
#we just need to make sure we provide all necessary info in the result dictionary.
#todo - tests and examples.
#and overflow_right reopen?
}
if {$opt_returnextra} {
@ -4902,29 +5099,29 @@ tcl::namespace::eval overtype {
set result [tcl::dict::create\
result $outstring\
visualwidth [punk::ansi::printing_length $outstring]\
instruction $instruction\
stringlen [string length $outstring]\
overflow_right_column $overflow_right_column\
overflow_right $overflow_right\
unapplied $unapplied\
unapplied_list $unapplied_list\
unapplied_ansisplit $unapplied_ansisplit\
insert_mode $insert_mode\
autowrap_mode $autowrap_mode\
crm_mode $crm_mode\
reverse_mode $reverse_mode\
insert_lines_above $insert_lines_above\
insert_lines_below $insert_lines_below\
cursor_saved_position $cursor_saved_position\
visualwidth [punk::ansi::printing_length $outstring]\
instruction $instruction\
stringlen [string length $outstring]\
overflow_right_column $overflow_right_column\
overflow_right $overflow_right\
unapplied $unapplied\
unapplied_list $unapplied_list\
unapplied_ansisplit $unapplied_ansisplit\
insert_mode $insert_mode\
autowrap_mode $autowrap_mode\
crm_mode $crm_mode\
reverse_mode $reverse_mode\
insert_lines_above $insert_lines_above\
insert_lines_below $insert_lines_below\
cursor_saved_position $cursor_saved_position\
cursor_saved_attributes $cursor_saved_attributes\
cursor_column $cursor_column\
cursor_row $cursor_row\
expand_right $opt_expand_right\
replay_codes $replay_codes\
replay_codes_underlay $replay_codes_underlay\
replay_codes_overlay $replay_codes_overlay\
pm_list $pm_list\
cursor_column $cursor_column\
cursor_row $cursor_row\
expand_right $opt_expand_right\
replay_codes $replay_codes\
replay_codes_underlay $replay_codes_underlay\
replay_codes_overlay $replay_codes_overlay\
pm_list $pm_list\
]
if {$opt_returnextra == 1} {
#puts stderr "renderline: $result"
@ -5073,6 +5270,11 @@ tcl::namespace::eval overtype::priv {
#caching the answer saves some regex expense - possibly a few uS to lookup vs under 1uS
#todo - test if still worthwhile after a large cache is built up. (limit cache size?)
proc is_sgr {code} {
set code_endswith_m [expr {[tcl::string::index $code end] eq "m"}] ;#skip SGR regexp testing for cases that don't end with m - as they can't be SGR
if {!$code_endswith_m} {
#don't even cache.
return 0
}
variable cache_is_sgr
if {[tcl::dict::exists $cache_is_sgr $code]} {
return [tcl::dict::get $cache_is_sgr $code]
@ -5081,6 +5283,7 @@ tcl::namespace::eval overtype::priv {
tcl::dict::set cache_is_sgr $code $answer
return $answer
}
proc render_to_unapplied {overlay_grapheme_control_list gci} {
upvar idx_over idx_over
@ -5104,7 +5307,8 @@ tcl::namespace::eval overtype::priv {
set unapplied_ansisplit [list ""]
#append unapplied [join [lindex $overstacks $idx_over] ""]
#append unapplied [punk::ansi::codetype::sgr_merge_list {*}[lindex $overstacks $idx_over]]
set sgr_merged [punk::ansi::codetype::sgr_merge_list {*}[lindex $og_stacks $gci]]
#set sgr_merged [punk::ansi::codetype::sgr_merge_list {*}[lindex $og_stacks $gci]]
set sgr_merged [punk::ansi::codetype::sgr_merge [lindex $og_stacks $gci]]
if {$sgr_merged ne ""} {
lappend unapplied_list $sgr_merged
lappend unapplied_ansisplit $sgr_merged ""
@ -5167,7 +5371,8 @@ tcl::namespace::eval overtype::priv {
set unapplied_list [list]
set unapplied_ansisplit [list ""] ;#remove empty entry at end if nothing added
set sgr_merged [punk::ansi::codetype::sgr_merge_list {*}[lindex $og_stacks $gci]]
#set sgr_merged [punk::ansi::codetype::sgr_merge_list {*}[lindex $og_stacks $gci]]
set sgr_merged [punk::ansi::codetype::sgr_merge [lindex $og_stacks $gci]]
if {$sgr_merged ne ""} {
lappend unapplied_list $sgr_merged
lappend unapplied_ansisplit $sgr_merged ""
@ -5217,9 +5422,13 @@ tcl::namespace::eval overtype::priv {
upvar understacks_gx gxstacks
set nxt [llength $o]
if {$i < $nxt} {
set o [lreplace $o $i $i]
set ustacks [lreplace $ustacks $i $i]
set gxstacks [lreplace $gxstacks $i $i]
#set o [lreplace $o $i $i]
ledit o $i $i
#set ustacks [lreplace $ustacks $i $i]
ledit ustacks $i $i
#review - do we need to ensure that stack at new $i has a reset code at the start?
#set gxstacks [lreplace $gxstacks $i $i]
ledit gxstacks $i $i
} elseif {$i == 0 || $i == $nxt} {
#nothing to do
} else {
@ -5329,6 +5538,27 @@ tcl::namespace::eval overtype::priv {
}
if {$i < [llength $ustacks]} {
lset ustacks $i $sgrstack
#check if next ustacks entry has a reset.
#It will need one if it doesn't already have one because our inserted char should not affect the pre-existing ansi state of the underlay.
#we have just replaced an entry into the ustacks at position i but we are still at the same position - so the next entry is still at position i+1
if {[llength $sgrstack] && $i+1 < [llength $ustacks]} {
set next_ustack [lindex $ustacks $i+1]
#could be a reset or just empty - either way we need to add a reset if it's not already there
#(empty if underlay had no ansi)
#temporarily emit something to stderr
if {![llength $next_ustack]} {
#puts -nonewline stderr " next_ustack (empty) at position [expr {$i+1}] after replacing position $i with '$c' and sgrstack '[join $sgrstack ""]'\n"
lset ustacks $i+1 [list "\x1b\[m"]
} else {
#review - next_ustack is a list - has_sgr_leadingreset will not work as expected if called on whole next_ustack as a list.
#As the stack will need merging anyway - we can just prepend a reset without checking.
#REVIEW.
#puts -nonewline stderr "check next_ustack '$next_ustack' for reset at position [expr {$i+1}] after replacing position $i with '$c' and sgrstack '[join $sgrstack ""]'\n"
#set next_ustack [linsert $next_ustack 0 [a+ reset]]
ledit next_ustack -1 -1 "\x1b\[m"
lset ustacks $i+1 $next_ustack
}
}
lset gxstacks $i $gx0stack
} else {
lappend ustacks $sgrstack
@ -5339,7 +5569,8 @@ tcl::namespace::eval overtype::priv {
if {$i < $nxt} {
#set o [linsert $o $i $c]
#JMN insert via ledit
ledit o $i $i-1 $c
#ledit o $i $i-1 $c
ledit o $i -1 $c
} else {
lappend o $c
}
@ -5347,8 +5578,10 @@ tcl::namespace::eval overtype::priv {
#set ustacks [linsert $ustacks $i $sgrstack]
#set gxstacks [linsert $gxstacks $i $gx0stack]
#insert via ledit
ledit ustacks $i $i-1 $sgrstack
ledit gxstacks $i $i-1 $gx0stack
#ledit ustacks $i $i-1 $sgrstack
ledit ustacks $i -1 $sgrstack
#ledit gxstacks $i $i-1 $gx0stack
ledit gxstacks $i -1 $gx0stack
} else {
lappend ustacks $sgrstack
lappend gxstacks $gx0stack

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

@ -1743,7 +1743,7 @@ namespace eval punk {
append script \n {set assigned [ansistring length $leveldata]}
set level_script_complete 1
}
%str {
%str - %string {
set active_key_type "string"
if {$get_not} {
error "!%str - not string-get is not supported"
@ -1752,6 +1752,9 @@ namespace eval punk {
append script \n {# set active_key_type "" index_operation: string-get}
append script \n {set assigned $leveldata}
set level_script_complete 1
#todo - %lpad- %lpadstr- %join- etc as in punk::lib::showdict
#review - merge code shared with showdict for these operations
}
%sp {
#experimental
@ -1822,6 +1825,8 @@ namespace eval punk {
set level_script_complete 1
}
%ansiview {
#review - implemented differently in showdict.
#(showdict uses ansistring VIEW -lf 1 <str>)
set active_key_type "string"
if {$get_not} {
error "!%# not string-ansiview is not supported"
@ -2446,7 +2451,41 @@ namespace eval punk {
set index <idx>
}]
}
%split-* {
#split on one or more chars - review
#set hidekey 1
#lassign [split $key -] _ splitchars
#set thisval [split $dval $splitchars]
set active_key_type "string"
set splitchars [string range $index 7 end]
append script \n [string map [list <splitchars> $splitchars] {
# set active_key_type "string" index_operation: split-string
#e.g supports %split-"\\n"= "l1\n\nl3" -> {l1 "" l3}
set splitchars "<splitchars>"
set assigned [split $leveldata $splitchars]
}]
set level_script_complete 1
#todo %splitat- %splitn- ??
}
%lpad-* {
#moved from punk::lib::showdict patterns.
#set hidekey 1
#lassign [split $key -] _ extra
#set width [expr {[textblock::width $dval] + $extra}]
#set thisval [textblock::pad $dval -which left -width $width]
set active_key_type "string"
set extra [string range $index 6 end]
append script \n [string map [list <extra> $extra] {
# set active_key_type "string" index_operation: lpad-string
set extra "<extra>"
set width [expr {[textblock::width $leveldata] + $extra}]
set assigned [textblock::pad $leveldata -which left -width $width]
}]
set level_script_complete 1
}
%* {
#see above re %lpad- etc and synchronizing with showdict
set active_key_type "string"
set do_bounds_check 0
set index [string range $index 1 end]
@ -2827,11 +2866,21 @@ namespace eval punk {
} else {
if {$is_range} {
lappend INDEX_OPERATIONS list-range
#todo - if we know it's a contiguous range, we could use lrange here instead of lindex
#we would also need to detect if it's a reverse range such as @5..1 and handle that correctly
#- lrange doesn't support reverse ranges, but we could resolve the indexset to a list of indices
#and then use lindex with that list of indices to get the correct result.
#we don't always know at this point if the range is in reverse or not because we don't know the size of the list until
#runtime - so we will handle both cases in the same way for now.
#e.g for index 5..end-6 - this could be forward or reverse depending on the length of the list.
set assign_script {
set assigned [lmap i [punk::lib::indexset_resolve [llength $leveldata] <idx>] {lindex $leveldata $i}]
}
} else {
lappend INDEX_OPERATIONS listindex
}
set assign_script {
set assigned [lmap i [punk::lib::indexset_resolve [llength $leveldata] <idx>] {lindex $leveldata $i}]
set assign_script {
set assigned [lindex $leveldata [punk::lib::indexset_resolve [llength $leveldata] <idx>]]
}
}
}
@ -2881,6 +2930,8 @@ namespace eval punk {
}
set script [string map [list <idx> $index] $script]
} elseif {[string first "end" $index] >=0} {
#review - obsoleted by indexset syntax. prune branch?
puts stderr "index with end detected - review if this branch still reachable - prune? $index"
if {[regexp {^end([-+]{1,2}[0-9]+)$} $index _match endspec]} {
if {$get_not} {
@ -2923,6 +2974,8 @@ namespace eval punk {
}
} elseif {[regexp {^([0-9]+|end|end[-+]{1,2}[0-9]+)-([0-9]+|end|end[-+]{1,2}([0-9]+))$} $index _ start end]} {
#review - obsoleted by indexset syntax. prune branch?
puts stderr "index with range and end detected - review if this branch still reachable - prune? $index"
if {$get_not} {
lappend INDEX_OPERATIONS list-range-not
set assign_script [string map [list <s> $start <e> $end ] {
@ -3012,6 +3065,10 @@ namespace eval punk {
error $listmsg "destructure $selector" [list pipesyntax destructure selector $selector]
}
} elseif {[string first - $index] > 0} {
puts stderr "index with - detected - review if this branch still reachable - prune? $index"
#review - we changed to detect indexset above.
#syntax @m-n should be deprecated in favour of @m..n
#todo - check if this branch still reachable - prune?
#e.g @1-3 gets here
#JMN
if {$get_not} {
@ -3089,19 +3146,61 @@ namespace eval punk {
}
}
} elseif {$active_key_type eq "string"} {
if {[string match *-* $index]} {
lappend INDEX_OPERATIONS string-range
set re_idxdashidx {^([-+]{0,1}\d+|end[-+]{1}\d+|end)-([-+]{0,1}\d+|end[-+]{1}\d+|end)$}
#todo - support more complex indices: 0-end-1 etc
#changed to indexset notation m..n allowing eg 2..end-1 etc.
#if {[string match *-* $index]} {}
if {[punk::lib::is_indexset $index]} {
#review - we are assuming a single element indexset here - ie no comma separated sets.
#todo - support $get_not
#todo - consider bounds_check for string indices.
# - Tcl doesn't do bounds checking for string index, but we need to consider in the context of pattern-matching
# whether we want to support syntaxes for with and without bounds checking on string indices.
set is_range [expr {[string first ".." $index] >= 0}]
if {$is_range} {
lappend INDEX_OPERATIONS string-range
#review - not efficient for contiguous monotonically increasing ranges
#because we are retrievinng each character individually and concatenating
#- but it is more flexible because it also supports reverse ranges and could support non-contiguous ranges such as @0,2,4..6
set assign_script {
set assigned [join [lmap i [punk::lib::indexset_resolve [string length $leveldata] <idx>] {string index $leveldata $i}] ""]
}
} else {
lappend INDEX_OPERATIONS string-index
set assign_script {
set assigned [string index $leveldata [punk::lib::indexset_resolve [string length $leveldata] <idx>]]
}
}
#set assign_script {
# set assigned [lmap i [punk::lib::indexset_resolve [llength $leveldata] <idx>] {lindex $leveldata $i}]
#}
lassign [split $index -] a b
#todo - consider where/if we can support 'ansistring INDEX' for ANSI strings.
#if so - it shouldn't overload the % operator we currently use for string access.
append script \n [tstr -return string -allowcommands {
# set active_key_type "string"
set assigned [string range $leveldata ${$a} ${$b}]
if {$leveldata eq ""} {
set assigned ""
} else {
${$assign_script}
}
}]
set script [string map [list <idx> $index] $script]
#set re_idxdashidx {^([-+]{0,1}\d+|end[-+]{1}\d+|end)-([-+]{0,1}\d+|end[-+]{1}\d+|end)$}
##todo - support more complex indices: 0-end-1 etc
#lassign [split $index -] a b
#append script \n [tstr -return string -allowcommands {
# # set active_key_type "string"
# set assigned [string range $leveldata ${$a} ${$b}]
#}]
} else {
if {$index eq "*"} {
#equivalent to indexset ".."
lappend INDEX_OPERATIONS string-all
append script \n [tstr -return string -allowcommands {
# set active_key_type "string"
@ -4294,6 +4393,7 @@ namespace eval punk {
}
#todo check end-x bounds?
}
#todo - change to ledit
if {$isint} {
append script [string map [list <listvar> $listvar <idx> $index <exp> $exp <val> $data] {
set <listvar> [linsert [lindex [list $<listvar> [unset <listvar>]] 0] <idx> <exp><val>]
@ -4350,7 +4450,8 @@ namespace eval punk {
#last element has no -, so we are inserting at the final position - not replacing
append script [string map [list <listvar> $listvar <containerkeys> [lrange $parts 0 end-1] <lastkey> $last <exp> $exp <val> $data] {
set target [lindex $<listvar> <containerkeys>]
set target [linsert $target <lastkey> <exp><val>]
#set target [linsert $target <lastkey> <exp><val>]
ledit target <lastkey> -1 <exp><val>
lset <listvar> <containerkeys> $target
}]
}
@ -8564,7 +8665,7 @@ namespace eval punk {
lappend chunks [list stdout $text]
}
console - term - terminal {
set term_env_vars {TERM TERM_PROGRAM TERM_PROGRAM_VERSION}
set term_env_vars {TERM TERM_PROGRAM TERM_PROGRAM_VERSION COLORTERM}
set term_dict [dict create]
foreach e $term_env_vars {
if {[info exists ::env($e)]} {
@ -8577,6 +8678,7 @@ namespace eval punk {
append text [punk::lib::showdict $term_dict] \n
lappend chunks [list stdout $text]
set text ""
set indent [string repeat " " [string length "WARNING: "]]
if {[catch {package require punk::console} result]} {
set text "Unable to load punk::console package - cannot test\n$result"
@ -8591,7 +8693,6 @@ namespace eval punk {
}
lappend chunks [list stdout $text]
set indent [string repeat " " [string length "WARNING: "]]
lappend cstring_tests [dict create\
type "PM "\
msg "UN"\
@ -8686,10 +8787,45 @@ namespace eval punk {
}
}
}
set posn [punk::console::get_cursor_pos] ;#warmup call - and test if works
if {$posn eq ""} {
append warningblock \n "WARNING: terminal doesn't respond to cursor position query - may cause display bugs in some cases."
} else {
set timeresult [timerate {set cpos [punk::console::get_cursor_pos]}]
lassign [split $cpos {;}] row col
if {![string is integer -strict $row] || ![string is integer -strict $col]} {
append warningblock \n "WARNING: terminal returns non-integer values for cursor position query - may cause display bugs in some cases. got row:'$row' col:'$col'"
} else {
set micros [lindex $timeresult 0]
if {$micros > 2000} {
append warningblock \n "WARNING: terminal cursor position query is very slow ($micros microseconds - expect < 2000us )"
append warningblock \n $indent "- may cause display lag/bugs in some cases."
} else {
if {$micros > 1000} {
set text "\n[a+ yellow]Terminal cursor position query test passed."
append text \n $indent "Response time: ${micros} microseconds (OK, good would be <= 1000us).[a]"
} else {
set text "[a+ green]Terminal cursor position query test passed."
append text \n $indent "Response time: ${micros} microseconds (GOOD).[a]"
}
lappend chunks [list stdout $text]
}
}
}
if {![string length $warningblock]} {
set text "[a+ green]No terminal warnings[a]\n"
lappend chunks [list stdout $text]
} else {
set mode [punk::console::mode]
if {$mode eq "line"} {
append warningblock \n "Terminal appears to be in line mode. Consider switching to raw mode and re-testing (command: punk::console::mode raw)."
}
}
puts stdout [punk::ansi::move_back 200] ;#hack for some horizontal position bugs where the above tests can leave the cursor in the wrong place for the next output.
#200 is arbitrary large number to move back enough to get to start of line.
}
}
topics - help {
@ -8815,10 +8951,11 @@ namespace eval punk {
#interp alias {} c {} clear ;#external executable 'clear' may not always be available
#todo - review
interp alias {} clear {} ::punk::reset
interp alias {} c {} ::punk::reset
#interp alias {} clear {} ::punk::reset
#interp alias {} c {} ::punk::reset
interp alias {} reset {} ::punk::reset
proc reset {} {
if {[llength [info commands ::punk::repl::reset_terminal]]} {
#punk::repl::reset_terminal notifies prompt system of reset
@ -8828,6 +8965,91 @@ namespace eval punk {
}
}
namespace eval argdoc {
punk::args::define {
@id -id ::punk::ansi8
@cmd -name punk::ansi8\
-summary\
"Tell terminal to enable 8-bit ANSI codes."\
-help\
"Enable 8-bit ANSI codes in the terminal.
May not be supported by all terminals.
Some terminals may already have 8-bit ANSI enabled, but some may require an explicit command to enable it.
7-bit ANSI codes are generally preferred - and will still work on terminals with 8-bit ANSI support.
(This is nothing to do with 8-bit colors - it is about the underlying bytes used for ANSI control sequences).
The ANSI sequence sent to the terminal to enable 8-bit codes is: ESC <sp> 7
To disable 8-bit ANSI support - a reset of the terminal may be required.
"
@opts
@values -min 0 -max 0
}
}
proc ansi8 {} {
punk::console::S8C1R
}
namespace eval argdoc {
punk::args::define {
@id -id ::punk::clear
@cmd -name punk::clear\
-summary\
"Clear the terminal screen (and scrollback buffer by default)."\
-help\
"Clear the terminal screen.
By default this will also clear scrollback if supported by the terminal.
With -x option it will preserve scrollback but clear the screen.
"
@opts
-x -optional 1 -type none -mash 1 -help\
"Preserve scrollback (if supported by terminal) but clear screen."
-s -optional 1 -type none -mash 1 -help\
"Stay at the current cursor position instead of moving to top-left after clearing."
@values -min 0 -max 0
}
}
proc clear {args} {
set argd [punk::args::parse $args withid ::punk::clear]
lassign [dict values $argd] leaders opts values received
set opt_x [dict exists $received -x]
set opt_s [dict exists $received -s]
# -x preserves scrollback but clears screen
if {$opt_s} {
#set pre_move_cmd [punk::ansi::move_up 1]
#review - terminal support for save/restore.
#we can just move up one line before clearing to preserve the line we're on,
#but this won't work if we're already at the last line.
#save/restore would be better if widely supported.
#review - get_size already calls get_cursor pos - maybe we can optimize by not calling get_cursor_pos separately?
#review - consider turning off cursor updating while doing this to avoid flicker?
set cpos [punk::console::get_cursor_pos]
set row [lindex $cpos 0]
set size [punk::console::get_size]
set lastrow [dict get $size rows]
if {$row >= $lastrow} {
set pre_move_cmd [punk::ansi::cursor_save_dec]
} else {
set pre_move_cmd [punk::ansi::move_up 1][punk::ansi::cursor_save_dec]
}
set move_cmd [punk::ansi::cursor_restore_dec]
#set pre_move_cmd [punk::ansi::move_up 1]
#set move_cmd ""
} else {
set pre_move_cmd ""
set move_cmd [punk::ansi::move 1 1]
}
if {$opt_x} {
puts -nonewline stdout $pre_move_cmd[punk::ansi::clear]$move_cmd
} else {
puts -nonewline stdout $pre_move_cmd[punk::ansi::clear_all]$move_cmd
}
}
#c aliased to clear -xs
#cc aliases to clear -x
#fileutil::cat except with checking for windows illegal path names (when on windows platform)

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

@ -125,6 +125,10 @@ tcl::namespace::eval punk::aliascore {
grepstr ::punk::ansi::grepstr\
colour ::punk::console::colour\
color ::punk::console::colour\
ansi8 ::punk::ansi8\
clear ::punk::clear\
c {::punk::clear -xs}\
cc {::punk::clear -x}\
ansi ::punk::console::ansi\
a? ::punk::console::code_a?\
A? {::punk::console::code_a? forcecolor}\

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

File diff suppressed because it is too large Load Diff

2
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/ansi/colourmap-0.1.0.tm

@ -103,7 +103,7 @@ tcl::namespace::eval ::punk::ansi::colourmap {
name -type string|stringstartswith(#)
}]
proc get_rgb_using_tk {name} {
package require tk
package require Tk ;#package require tk (lowercase) doesn't always work
#assuming 'winfo depth .' is always 32 ?
set RGB [winfo rgb . $name]
set rgb [lmap n $RGB {expr {$n / 256}}]

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

@ -1250,6 +1250,11 @@ tcl::namespace::eval punk::args {
}
set optionspecs [list]
#REVIEW - whilst this is only done once for each command definition, the -help section processing is sometimes expensive,
#and isn't required for parsing of arguments, so it unnecessarily slows first use of a command that uses punk::args and is heavily documented,
#especially if it has tcl syntax highlighted examples.
#- ideally we would delay expansion of -help sections until needed for display,
#and use a different cache key for the parsing vs display versions of the resolved definition.
foreach block $normargs {
if {[string first \$\{ $block] >= 0} {
if {$defspace ne ""} {
@ -2550,7 +2555,7 @@ tcl::namespace::eval punk::args {
tcl::dict::set spec_merged -typesynopsis $specval
}
-parsekey - -group {
tcl::dict::set spec_merged -typesynopsis $specval
tcl::dict::set spec_merged $spec $specval
}
-mash {
#allow when any alt in argname is a single letter flag such s -a or -Z
@ -8535,7 +8540,7 @@ tcl::namespace::eval punk::args {
}
#todo - move block below up here.
if {!$all_mashable} {
puts stderr "Debug: flagsupplied '$flagsupplied' not a valid flagname and not a valid mash of flags - treating as value"
#puts stderr "Debug: flagsupplied '$flagsupplied' not a valid flagname and not a valid mash of flags - treating as value"
#- probably isn't a flag at all - could be a value
#treat as value
set optionset ""
@ -8668,9 +8673,8 @@ tcl::namespace::eval punk::args {
#tcl::dict::set opts $flag_ident $tdflt
if {$flag_ident_is_parsekey} {
#(shimmer - but required for ordering correctness during override)
puts stderr "Debug: flag '$mashflagname' in mash '$flagsupplied' flag_ident '$flag_ident' is the same as parsekey '$api_opt' tdflt: $tdflt - using lappend to ensure it ends up after any previous flag in the mash that had the same parsekey"
#puts stderr "Debug: flag '$mashflagname' in mash '$flagsupplied' flag_ident '$flag_ident' is the same as parsekey '$api_opt' tdflt: $tdflt - using lappend to ensure it ends up after any previous flag in the mash that had the same parsekey"
lappend opts $flag_ident $tdflt
puts stderr "opts after lappend: $opts"
} else {
tcl::dict::set opts $flag_ident $tdflt
}
@ -10241,6 +10245,128 @@ tcl::namespace::eval punk::args {
}
}
proc _synopsis_form_arg_display {formdict argname} {
#non-colour SGR such as bold/italic/strike - so we don't need to worry about NOCOLOR settings
set I "\x1b\[3m" ;#[punk::ansi::a+ italic]
set NI "\x1b\[23m" ;# [punk::ansi::a+ noitalic]
#for inner question marks marking optional type
set IS "\x1b\[3\;9m" ;#[punk::ansi::a+ italic strike]
set NIS "\x1b\[23\;29m" ;#[punk::ansi::a+ noitalic nostrike]
set RST "\x1b\[m" ;#[punk::ansi::a]
set arginfo [dict get $formdict ARG_INFO $argname]
set typelist [dict get $arginfo -type]
set ts [Dict_getdef $arginfo -typesynopsis ""]
set n [expr {[llength $typelist]-1}]
set name_tail [lrange $argname end-$n end];#if there are enough tail words in the argname to match -types
set clause ""
if {$ts ne ""} {
set tp_displaylist $ts
} else {
set tp_displaylist [lrepeat [llength $typelist] ""]
}
foreach typespec $typelist td $tp_displaylist elementname $name_tail {
#elementname will commonly be empty
if {[string match {\?*\?} $typespec]} {
set tp [string range $typespec 1 end-1]
set member_optional 1
} else {
set tp $typespec
set member_optional 0
}
if {$td ne ""} {
set c $td
} else {
#handle alternate-types e.g literal(text)|literal(binary)
set alternates [list]
set type_alternatives [_split_type_expression $tp]
foreach tp_alternative $type_alternatives {
set tp_alternative_word1 [lindex $tp_alternative 0]
set match [lindex $tp_alternative 1]
switch -exact -- $tp_alternative_word1 {
literal {
lappend alternates [list $match]
}
literalprefix {
#todo - trie styling on prefix calc
lappend alternates [list $match]
}
stringstartswith {
lappend alternates [list $match*]
}
stringendswith {
lappend alternates [list *$match]
}
default {
#we'll only take display hints from the name itself if there was no defined typesynopsis element for this position in the type,
#and if the type-alternatives don't specify a literal or string match that we can use for display
#and if there are enough tail words in the argname to match the position in the type list
#empty strings can be put in -typesynopsis positions to only override the type information for certain elements of the clause
#- e.g for a type list of {string int} we could specify a typesynopsis of {"" "count"} to get display of "FILENAME count" for an argname of "file FILENAME FILECOUNT"
if {[llength $name_tail] >= [llength $typelist]} {
#important to list protect $elementname e.g look at ::apply
#The name may contain spaces e.g "{args body ?namespace?}"
#This must not be split into multiple words - it is a single element name that happens to contain spaces.
lappend alternates $I[list $elementname]$NI
} else {
lappend alternates $I<$tp_alternative>$NI
}
}
}
}
set alternates [punk::args::lib::lunique $alternates]
set c [join $alternates |]
}
if {$member_optional} {
#append clause " " "(?$c?)"
append clause " " "\[$c\]"
} else {
append clause " " $c
}
}
set clause [string trimleft $clause]
#set ARGD [dict create argname $argname class leader]
if {[dict get $arginfo -optional] || [dict exists $arginfo -default]} {
if {[dict get $arginfo -multiple]} {
#set display "?$I$argname$NI?..."
set display "\[$clause\]..."
} else {
set display "\[$clause\]"
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "?[lindex [dict get $arginfo -choices] 0]?"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display "?$argname?"
#} else {
# set display "?$I$argname$NI?"
#}
}
} else {
if {[dict get $arginfo -multiple]} {
#set display "$I$argname$NI ?$I$argname$NI?..."
set display "$clause \[$clause\]..."
} else {
set display $clause
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "[lindex [dict get $arginfo -choices] 0]"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display $argname
#} else {
# set display "$I$argname$NI"
#}
}
}
return $display
}
lappend PUNKARGS [list {
@id -id ::punk::args::synopsis
@cmd -name punk::args::synopsis\
@ -10295,7 +10421,19 @@ tcl::namespace::eval punk::args {
if {$spec eq ""} {
return
}
set form_names [dict get $spec form_names]
set dict_idx_to_name [dict create]
set dict_name_to_idx [dict create]
set all_form_names [dict get $spec form_names]
set idx 0
#assert: form_names is ordered as defined in the command definition - so idx into it is stable.
foreach fn $all_form_names {
dict set dict_idx_to_name $idx $fn
dict set dict_name_to_idx $fn $idx
incr idx
}
set form_names $all_form_names
if {$form ne "*"} {
if {[string is integer -strict $form]} {
set f [lindex $form_names $form]
@ -10314,171 +10452,51 @@ tcl::namespace::eval punk::args {
}
set SYND [dict create]
dict set SYND cmd_info [dict get $spec cmd_info]
set c_info [dict get $spec cmd_info]
set cmd_info [dict create]
dict for {k v} $c_info {
if {[string match -* $k]} {
dict set cmd_info [string range $k 1 end] $v
}
}
dict set SYND COMMAND $cmd_info
#leading "# " required (punk::ns::synopsis will pass through)
if {![dict exists $received -noheader]} {
set syn "# [Dict_getdef $spec cmd_info -summary ""]\n"
set GRY "\x1b\[38\;5\;8m"
set RST "\x1b\[m"
}
#todo - -multiple etc
foreach f $form_names {
set SYNLIST [list]
dict set SYND FORMS $f [list]
append syn "$id"
set forminfo [dict get $spec FORMS $f]
#foreach argname [dict get $forminfo LEADER_NAMES] {
# set arginfo [dict get $forminfo ARG_INFO $argname]
# set ARGD [dict create argname $argname class leader]
# if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display [lindex [dict get $arginfo -choices] 0]
# } elseif {[dict get $arginfo -type] eq "literal"} {
# set display $argname
# } else {
# set display $I$argname$RST
# }
# if {[dict get $arginfo -optional]} {
# append syn " ?$display?"
# } else {
# append syn " $display"
# }
# dict set ARGD type [dict get $arginfo -type]
# dict set ARGD optional [dict get $arginfo -optional]
# dict set ARGD display $display
# dict lappend SYND $f $ARGD
#}
set idx [dict get $dict_name_to_idx $f]
dict set SYND FORMS $f [dict create]
if {![dict exists $received -noheader]} {
set formsummary "FORM $idx $f"
if {[dict exists $forminfo -summary]} {
append formsummary " - [dict get $forminfo -summary]"
}
append syn "## $GRY$formsummary$RST\n"
}
append syn "$id"
set FORMARGS [list]
foreach argname [dict get $forminfo LEADER_NAMES] {
set arginfo [dict get $forminfo ARG_INFO $argname]
set typelist [dict get $arginfo -type]
if {[llength $typelist] == 1} {
set tp [lindex $typelist 0]
set ts [Dict_getdef $arginfo -typesynopsis ""]
if {$ts ne ""} {
#set arg_display [dict get $arginfo -typesynopsis]
set clause $ts
} else {
#set arg_display $argname
set alternates [list];#alternate acceptable types e.g literal(yes)|literal(ok) or indexpression|literal(first)
set type_alternatives [_split_type_expression $tp]
foreach tp_alternative $type_alternatives {
set tp_alternative_word1 [lindex $tp_alternative 0]
switch -exact -- $tp_alternative_word1 {
literal {
set match [lindex $tp_alternative 1]
lappend alternates $match
}
literalprefix {
#todo - trie styling on prefix calc
set match [lindex $tp_alternative 1]
lappend alternates $match
}
stringstartswith {
set match [lindex $tp_alternative 1]
lappend alternates $match*
}
stringendswith {
set match [lindex $tp_alternative 1]
lappend alternates *$match
}
default {
lappend alternates $I$argname$NI
}
}
#if {$tp_alternative eq "literal"} {
# lappend alternates [lindex $argname end]
#} elseif {[string match literal(*) $tp_alternative]} {
# set match [string range $tp_alternative 8 end-1]
# lappend alternates $match
#} elseif {[string match literalprefix(*) $tp_alternative]} {
# set match [string range $tp_alternative 14 end-1]
# lappend alternates $match
#} else {
# lappend alternates $I$argname$NI
#}
}
#remove dupes - but keep order (e.g of dupes -type string|int when no -typesynopsis was specified)
#todo - trie prefixes display
set alternates [punk::args::lib::lunique $alternates]
set clause [join $alternates |]
}
} else {
set n [expr {[llength $typelist]-1}]
set name_tail [lrange $argname end-$n end];#if there are enough tail words in the argname to match -types
set clause ""
set ts [Dict_getdef $arginfo -typesynopsis ""]
if {$ts ne ""} {
set tp_displaylist $ts
} else {
set tp_displaylist [lrepeat [llength $typelist] ""]
}
foreach typespec $typelist td $tp_displaylist elementname $name_tail {
#elementname will commonly be empty
if {[string match {\?*\?} $typespec]} {
set tp [string range $typespec 1 end-1]
set member_optional 1
} else {
set tp $typespec
set member_optional 0
}
if {$tp eq "literal"} {
set c $elementname
} elseif {[string match literal(*) $tp]} {
set match [string range $tp 8 end-1]
set c $match
} else {
if {$td eq ""} {
set c $I$tp$NI
} else {
set c $td
}
}
if {$member_optional} {
append clause " " "(?$c?)"
} else {
append clause " " $c
}
}
set clause [string trimleft $clause]
}
foreach argname [dict get $forminfo LEADER_NAMES] {
set display [_synopsis_form_arg_display $forminfo $argname]
append syn " $display"
set arginfo [dict get $forminfo ARG_INFO $argname]
set ARGD [dict create argname $argname class leader]
if {[dict get $arginfo -optional] || [dict exists $arginfo -default]} {
if {[dict get $arginfo -multiple]} {
#set display "?$I$argname$NI?..."
set display "?$clause?..."
} else {
set display "?$clause?"
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "?[lindex [dict get $arginfo -choices] 0]?"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display "?$argname?"
#} else {
# set display "?$I$argname$NI?"
#}
}
} else {
if {[dict get $arginfo -multiple]} {
#set display "$I$argname$NI ?$I$argname$NI?..."
set display "$clause ?$clause?..."
} else {
set display $clause
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "[lindex [dict get $arginfo -choices] 0]"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display $argname
#} else {
# set display "$I$argname$NI"
#}
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
foreach k {choices choiceprefix choicerestricted choicemultiple} {
if {[dict exists $arginfo -$k]} {
dict set ARGD $k [dict get $arginfo -$k]
}
}
append syn " $display"
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
dict set ARGD display $display
#dict lappend SYND $f $ARGD
lappend FORMARGS $ARGD
}
foreach argname [dict get $forminfo OPT_NAMES] {
@ -10490,7 +10508,7 @@ tcl::namespace::eval punk::args {
#(disallowed in punk::args::define)
set argdisplay $argname
} else {
#assert [llength $tp] == 1 (multiple values for flag unspported in punk::args::define)
#assert [llength $tp] == 1 (multiple values for flag unsupported in punk::args::define)
if {[string match {\?*\?} $tp]} {
set tp [string range $tp 1 end-1]
set value_is_optional true
@ -10509,19 +10527,30 @@ tcl::namespace::eval punk::args {
} else {
set alternates [list];#alternate acceptable types e.g literal(yes)|literal(ok) or indexpression|literal(first)
foreach tp_alternative [split $tp |] {
#-type literal not valid for opt - review
if {[string match literal(*) $tp_alternative]} {
set match [string range $tp_alternative 8 end-1]
lappend alternates $match
} elseif {[string match literalprefix(*) $tp_alternative]} {
set match [string range $tp_alternative 14 end-1]
lappend alternates $match
} else {
lappend alternates <$I$tp_alternative$NI>
set type_alternatives [_split_type_expression $tp]
foreach tp_alternative $type_alternatives {
set match [lindex $tp_alternative 1]
switch -- [lindex $tp_alternative 0] {
literal {
lappend alternates [list $match]
}
literalprefix {
lappend alternates [list $match]
}
stringstartswith {
lappend alternates [list $match*]
}
stringendswith {
lappend alternates [list *$match]
}
default {
lappend alternates $I<$tp_alternative>$NI
}
}
}
#todo - trie prefixes display?
#trie prefixes display?
#we probably don't want to show prefixes in synopsis.
#AI agents should be encouraged to use full values for clarity, and human users can refer to help for the prefix info if they care.
set alternates [punk::args::lib::lunique $alternates]
set tp_display [join $alternates |]
}
@ -10529,44 +10558,102 @@ tcl::namespace::eval punk::args {
#need to bracket alternate-types to distinguish pipes delimiting flag aliases
set tp_display "($tp_display)"
}
#consider optional: -f|--file|--file= -type string|num
#we can't show this as [-f|--file|--file= string|num]
#because the pipes make visually parsing it ambiguous.
#we *could* show this as [-f|--file|--file= (string|num)]
# but it lacks clarity in descripting we can supply --file string or --file=string
#showing it as [-f (string|num)|--file (string|num)|--file=(string|num)] is not as compact as it could be, but is reasonably precise.
#we could merge the first two to avoid repeating the type info - but then we would also need brackets to clarify the pipe applicability:
#e.g
# [(-f|--file (string|num))|--file=(string|num)]
#
#we choose to only merge in the case where there are no trailing= aliases or they are all trailing= aliases.
set aliasflags [split $argname |]
#set has_longopt_inlinevalue_alias [expr {[lsearch -glob $aliasflags *=] >= 0}]
set num_longopt_inlinevalue_aliases [llength [lsearch -all -glob $aliasflags *=]] ;#count list of indices of aliasflags that end with =
set homogenous_aliases [expr {$num_longopt_inlinevalue_aliases == 0 || $num_longopt_inlinevalue_aliases == [llength $aliasflags]}]
set argdisplay ""
foreach aliasflag [split $argname |] {
if {[string match --* $aliasflag]} {
if {[string index $aliasflag end] eq "="} {
set alias [string range $aliasflag 0 end-1]
if {$value_is_optional} {
append argdisplay "$alias$IS?$NIS=$tp_display$IS?$NIS|"
if {!$homogenous_aliases} {
foreach aliasflag $aliasflags {
if {[string match --* $aliasflag]} {
if {[string index $aliasflag end] eq "="} {
set alias [string range $aliasflag 0 end-1]
if {$value_is_optional} {
#append argdisplay "$alias$IS\[$NIS=$tp_display$IS\]$NIS|"
append argdisplay "$alias$I\[$NI=$tp_display$I\]$NI|"
} else {
append argdisplay "$alias=$tp_display|"
}
} else {
append argdisplay "$alias=$tp_display|"
if {$value_is_optional} {
#double-dashed flag without trailing = can't accept optional value
#append argdisplay "$aliasflag $IS\[$NIS$tp_display$IS\]$NIS|"
append argdisplay "$aliasflag|"
} else {
append argdisplay "$aliasflag $tp_display|"
}
}
} else {
if {$value_is_optional} {
append argdisplay "$aliasflag $IS?$NIS$tp_display$IS?$NIS|"
#flag can't accept optional value
append argdisplay "$aliasflag|"
} else {
append argdisplay "$aliasflag $tp_display|"
}
}
}
set argdisplay [string trimright $argdisplay |]
} else {
if {$num_longopt_inlinevalue_aliases > 0} {
#all aliases are longopt inlinevalue aliases
#review
# --file=|--fname= -type string
# -> (--file|--fname)=type
# or
# -> (--file|--fname)[=type]
#first transform the argname to remove the trailing = and bracket the aliases if there are multiple
#review - we don't expect any arguments to be defined with inner = in the name.
#todo - enforce no inner = in argname in punk::args::define for options?
#
set argname "[string map {= ""} $argname]"
if {$num_longopt_inlinevalue_aliases > 1} {
set argname "($argname)"
}
if {$value_is_optional} {
set argdisplay "$argname$I\[$NI=$tp_display$I\]$NI"
} else {
set argdisplay "$argname=$tp_display"
}
} else {
#no longopts with trailing = aliases, so we can show the type info without ambiguity as applying to all aliases
if {$value_is_optional} {
#single dash flag can't accept optional value
append argdisplay "$aliasflag|"
set argdisplay "$argname $I\[$NI$tp_display$I\]$NI"
} else {
append argdisplay "$aliasflag $tp_display|"
set argdisplay "$argname $tp_display"
}
}
}
set argdisplay [string trimright $argdisplay |]
}
if {[dict get $arginfo -optional]} {
if {[dict get $arginfo -multiple]} {
set display "?$argdisplay?..."
#set display "?$argdisplay?..."
set display "\[$argdisplay\]..."
} else {
set display "?$argdisplay?"
#set display "?$argdisplay?"
set display "\[$argdisplay\]"
}
} else {
if {[dict get $arginfo -multiple]} {
set display "$argdisplay ?$argdisplay?..."
#set display "$argdisplay ?$argdisplay?..."
set display "$argdisplay \[$argdisplay\]..."
} else {
set display $argdisplay
}
@ -10606,136 +10693,43 @@ tcl::namespace::eval punk::args {
# }
# }
#}
#todo -mash
append syn " $display"
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
foreach k {choices choiceprefix choicerestricted choicemultiple} {
if {[dict exists $arginfo -$k]} {
dict set ARGD $k [dict get $arginfo -$k]
}
}
dict set ARGD display $display
#dict lappend SYND $f $ARGD
lappend FORMARGS $ARGD
}
foreach argname [dict get $forminfo VAL_NAMES] {
set arginfo [dict get $forminfo ARG_INFO $argname]
set typelist [dict get $arginfo -type]
if {[llength $typelist] == 1} {
set tp [lindex $typelist 0]
set ts [Dict_getdef $arginfo -typesynopsis ""]
if {$ts ne ""} {
#set arg_display [dict get $arginfo -typesynopsis]
set clause $ts
} else {
#set arg_display $argname
set alternates [list];#alternate acceptable types e.g literal(yes)|literal(ok) or indexpression|literal(first)
foreach tp_alternative [split $tp |] {
if {$tp_alternative eq "literal"} {
lappend alternates [lindex $argname end]
} elseif {[string match literal(*) $tp_alternative]} {
set match [string range $tp_alternative 8 end-1]
lappend alternates $match
} elseif {[string match literalprefix(*) $tp_alternative]} {
set match [string range $tp_alternative 14 end-1]
lappend alternates $match
} else {
lappend alternates $I$argname$NI
}
}
#remove dupes - but keep order (e.g of dupes -type string|int when no -typesynopsis was specified)
#todo - trie prefixes display
set alternates [punk::args::lib::lunique $alternates]
set clause [join $alternates |]
}
} else {
set n [expr {[llength $typelist]-1}]
set name_tail [lrange $argname end-$n end];#if there are enough tail words in the argname to match -types
set clause ""
set ts [Dict_getdef $arginfo -typesynopsis ""]
if {$ts ne ""} {
set tp_displaylist $ts
} else {
set tp_displaylist [lrepeat [llength $typelist] ""]
}
foreach typespec $typelist td $tp_displaylist elementname $name_tail {
#elementname will commonly be empty
if {[string match {\?*\?} $typespec]} {
set tp [string range $typespec 1 end-1]
set member_optional 1
} else {
set tp $typespec
set member_optional 0
}
#handle alternate-types e.g literal(text)|literal(binary)
set alternates [list]
foreach tp_alternative [split $tp |] {
if {$tp_alternative eq "literal"} {
lappend alternates $elementname
} elseif {[string match literal(*) $tp_alternative]} {
set match [string range $tp_alternative 8 end-1]
lappend alternates $match
} elseif {[string match literalprefix(*) $tp_alternative]} {
set match [string range $tp_alternative 14 end-1]
lappend alternates $match
} else {
if {$td eq ""} {
lappend alternates $I$tp$NI
} else {
lappend alternates $td
}
}
}
set alternates [punk::args::lib::lunique $alternates]
set c [join $alternates |]
if {$member_optional} {
append clause " " "(?$c?)"
} else {
append clause " " $c
}
}
set clause [string trimleft $clause]
}
set display [_synopsis_form_arg_display $forminfo $argname]
append syn " $display"
set ARGD [dict create argname $argname class value]
if {[dict get $arginfo -optional] || [dict exists $arginfo -default]} {
if {[dict get $arginfo -multiple]} {
#set display "?$I$argname$NI?..."
set display "?$clause?..."
} else {
set display "?$clause?"
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "?[lindex [dict get $arginfo -choices] 0]?"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display "?$argname?"
#} else {
# set display "?$I$argname$NI?"
#}
}
} else {
if {[dict get $arginfo -multiple]} {
#set display "$I$argname$NI ?$I$argname$NI?..."
set display "$clause ?$clause?..."
} else {
set display $clause
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "[lindex [dict get $arginfo -choices] 0]"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display $argname
#} else {
# set display "$I$argname$NI"
#}
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
foreach k {choices choiceprefix choicerestricted choicemultiple} {
if {[dict exists $arginfo -$k]} {
dict set ARGD $k [dict get $arginfo -$k]
}
}
append syn " $display"
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
dict set ARGD display $display
#dict lappend SYND $f $ARGD
lappend FORMARGS $ARGD
}
#accepts unnamed extra arguments e.g toplevel docid for ensembles and ensemble-like commands
if {[dict get $forminfo VAL_UNNAMED]} {
set display "?<unnamed>...?"
set display {[<unnamed>...]}
append syn " $display"
set ARGD [dict create argname "" class value]
dict set ARGD type any
@ -10745,7 +10739,7 @@ tcl::namespace::eval punk::args {
lappend FORMARGS $ARGD
}
append syn \n
dict set SYND FORMS $f $FORMARGS
dict set SYND FORMS $f args $FORMARGS
}
switch -- $opt_return {
full {
@ -10757,7 +10751,8 @@ tcl::namespace::eval punk::args {
set summary "# [Dict_getdef $spec cmd_info -summary ""]\n"
}
set FORMS [dict get $SYND FORMS]
dict for {form arglist} $FORMS {
dict for {form arginfo} $FORMS {
set arglist [dict get $arginfo args]
append summary $id
set class_state leader
set option_count 0
@ -10774,7 +10769,7 @@ tcl::namespace::eval punk::args {
incr value_count
if {$class_state ne "value"} {
if {$option_count > 0} {
append summary " ?options ($option_count defined)?"
append summary " \[OPTIONS ($option_count defined)\]"
}
set class_state value
}
@ -10783,7 +10778,7 @@ tcl::namespace::eval punk::args {
}
}
if {$value_count == 0 && $option_count > 0} {
append summary " ?options ($option_count defined)?"
append summary " \[OPTIONS ($option_count defined)\]"
}
append summary \n
}
@ -10803,6 +10798,7 @@ tcl::namespace::eval punk::args {
}
#REVIEW
lappend PUNKARGS [list {
@id -id ::punk::args::synopsis_summary
@cmd -name punk::args::synopsis_summary -help\
@ -10852,9 +10848,10 @@ tcl::namespace::eval punk::args {
}
}
}
if {$code ne ""} {
if {$code ne "" && [tcl::string::index $code end] eq "m"} {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set codestack [list "\x1b\[m"]
#set codestack [list "\x1b\[m"]
set codestack [list $code]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set codestack [list $code]
} elseif {[punk::ansi::codetype::is_sgr $code]} {
@ -10862,10 +10859,9 @@ tcl::namespace::eval punk::args {
set dup_posns [lsearch -all -exact $codestack $code] ;#must be -exact because of square-bracket glob chars
set codestack [lremove $codestack {*}$dup_posns]
lappend codestack $code
} else {
#? ignore other ANSI codes?
}
}
#? ignore other ANSI codes?
}
if {[string match -* $plain_s] || [string match ?- $plain_s]} {
}

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

@ -2986,6 +2986,71 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
time -type integer -optional 1
} "@doc -name Manpage: -url [manpage_tcl file]" ]
lappend PUNKARGS [list {
@id -id ::tcl::file::attributes
@cmd -name "Built-in: tcl::file::attributes"\
-summary\
"Get/Set platform-specific values associated with a file/directory."\
-help\
"This subcommand returns or sets platform-specific values associated with a file.
The first form without specificing option, returns a list of the platform-specific options and their values.
The first form with an option returns the value for the given option.
The last form sets one or more of the values. The values are as follows:
On Unix, ${$B}-group${$N} gets or sets the group name for the file. A group id can be given to the command, but it
returns a group name. ${$B}-owner${$N} gets or sets the user name of the owner of the file. The command returns the
owner name, but the numerical id can be passed when setting the owner. ${$B}-permissions${$N} retrieves or sets a
file's access permissions, using octal notation by default. This option also provides limited support for
setting permissions using the symbolic notation accepted by the chmod command, following the form
${$B}[ugo]?[[+-=][rwxst],[...]]${$N}. Multiple permission specifications may be given, separated by commas.
E.g., ${$B}u+s,go-rw${$N} would set the setuid bit for a file's owner as well as remove read and write permission for
the file's group and other users. An ls-style string of the form rwxrwxrwx is also accepted but must always
be 9 characters long. E.g., ${$B}rwxr-xr-t${$N} is equivalent to ${$B}01755${$N}. On versions of Unix supporting file flags,
${$B}-readonly${$N} returns the value of, or sets, or clears the readonly attribute of a file, i.e., the user
immutable flag (${$B}uchg${$N}) to the ${$B}chflags${$N} command.
On Windows, ${$B}-archive${$N} gives the value or sets or clears the archive attribute of the file. ${$B}-hidden${$N} gives the
value or sets or clears the hidden attribute of the file. ${$B}-longname${$N} will expand each path element to its long
version. This attribute cannot be set. ${$B}-readonly${$N} gives the value or sets or clears the readonly attribute of
the file. ${$B}-shortname${$N} gives a string where every path element is replaced with its short (8.3) version of the
name if possible. For path elements that cannot be mapped to short names, the long name is retained. This
attribute cannot be set. ${$B}-system${$N} gives or sets or clears the value of the system attribute of the file.
On macOS and Darwin, ${$B}-creator${$N} gives or sets the Finder creator type of the file. ${$B}-hidden${$N} gives or sets or
clears the hidden attribute of the file. ${$B}-readonly${$N} gives or sets or clears the readonly attribute of the file.
${$B}-rsrclength${$N} gives the length of the resource fork of the file, this attribute can only be set to the value 0,
which results in the resource fork being stripped off the file.
On all platforms, files in ${$B}zipfs${$N} mounted archives return the following attributes.
These are all read-only and cannot be directly set.
${$B}-archive${$N}
The path of the mounted ZIP archive containing the file.
${$B}-compsize${$N}
The compressed size of the file within the archive. This is 0 for directories.
${$B}-crc${$N}
The CRC of the file if present, else 0.
${$B}-mount${$N}
The path where the containing archive is mounted.
${$B}-offset${$N}
The offset of the file within the archive.
${$B}-uncompsize${$N}
The uncompressed size of the file. This is ${$B}0${$N} for directories.
Other attributes may be present in the returned list. These should be ignored."
@form -form "get"
@values -min 1 -max 2
name -type string -optional 0
option -type stringstartswith(-) -typesynopsis {-${$I}option${$NI}} -optional 1
@form -form "set"
@values -min 3 -max -1
name -type string -optional 0
option_value -type {stringstartswith(-) string} -typesynopsis {-${$I}option${$NI} ${$I}value${$NI}} -optional 0 -multiple 1
} "@doc -name Manpage: -url [manpage_tcl file]" ]
lappend PUNKARGS [list {
@id -id ::tcl::file::channels
@cmd -name "Built-in: tcl::file::channels"\
@ -3026,6 +3091,26 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
pathname -optional 1 -type string -multiple 1
} "@doc -name Manpage: -url [manpage_tcl file]" ]
lappend PUNKARGS [list {
@id -id ::tcl::file::dirname
@cmd -name "Built-in: tcl::file::dirname"\
-summary\
"Return a path excluding last element."\
-help\
"Returns a name comprised of all of the path components in name excluding the last element.
If name is a relative file name and only contains one path element, then returns “.”. If name
refers to a root directory, then the root directory is returned. For example,
${[punk::args::helpers::example {
${$B} file dirname c:/
}]}
returns ${$B}c:/${$N}.
"
@values -min 1 -max 1
name -type string
} "@doc -name Manpage: -url [manpage_tcl file]" ]
lappend PUNKARGS [list {
@id -id ::tcl::file::copy
@cmd -name "Built-in: tcl::file::copy"\
@ -3104,7 +3189,10 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
#tcl 9+
lappend PUNKARGS [list {
@id -id ::tcl::file::home
@cmd -name "Built-in: tcl::file::home" -help\
@cmd -name "Built-in: tcl::file::home"\
-summary\
"Return the home directory for a user."\
-help\
"If no argument is specified, the command returns the home directory of the current user.
This is generally the value of the ${$B}$HOME${$N} environment variable except that on Windows
platforms backslashes in the path are replaced by forward slashes. An error is raised if
@ -3134,7 +3222,29 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
} "@doc -name Manpage: -url [manpage_tcl file]" ]
#join
#link
lappend PUNKARGS [list {
@id -id ::tcl::file::join
@cmd -name "Built-in: tcl::file::join"\
-summary\
"Join directory/file components into a single path."\
-help\
"Takes one or more file names and combines them, using the correct path separator for the current platform.
If a particular name is relative, then it will be joined to the previous file name argument. Otherwise, any
earlier arguments will be discarded, and joining will proceed from the current argument. For example,
${[punk::args::helpers::example {
${$B}file join ${$N} a b /foo bar
}]}
returns ${$B}/foo/bar${$N}.
Note that any of the names can contain separators, and that the result is always canonical for the current
platform: ${$B}/${$N} for Unix and Windows.
"
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]" ]
lappend PUNKARGS [list {
@id -id ::tcl::file::link
@cmd -name "Built-in: tcl::file::link"\
@ -3242,8 +3352,33 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
#owned
#pathtype
lappend PUNKARGS [list {
@id -id ::tcl::file::owned
@cmd -name "Built-in: tcl::file::owned"\
-summary\
"Test file owned by current user."\
-help\
"Returns ${$B}1${$N} if the file ${$I}name${$NI} is owned by the current user, ${$B}0${$N} otherwise."
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
lappend PUNKARGS [list {
@id -id ::tcl::file::pathtype
@cmd -name "Built-in: tcl::file::pathtype"\
-summary\
{Return path type. Either absolute, relative or volumerelative.}\
-help\
"Returns one of ${$B}absolute${$N}, ${$B}relative${$N}, ${$B}volumerelative${$N}. If name refers to a specific file on a specific
volume, the path type will be ${$B}absolute${$N}. If name refers to a file relative to the current working
directory, then the path type will be ${$B}relative${$N}. If name refers to a file relative to the current
working directory on a specified volume, or to a specific file on the current working volume, then
the path type is ${$B}volumerelative${$N}."
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
lappend PUNKARGS [list {
@id -id ::tcl::file::readable
@cmd -name "Built-in: tcl::file::readable"\
@ -3299,9 +3434,46 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
#separator
#size
#split
lappend PUNKARGS [list {
@id -id ::tcl::file::separator
@cmd -name "Built-in: tcl::file::separator"\
-summary\
{File separator character}\
-help\
"If no argument is given, returns the character which is used to separate path segments for native
files on this platform. If a path is given, the filesystem responsible for that path is asked to
return its separator character. If no file system accepts name, an error is generated."
@values -min 0 -max 1
name -optional 1 -type string -help\
"Path to query for separator character."
} "@doc -name Manpage: -url [manpage_tcl file]"]
lappend PUNKARGS [list {
@id -id ::tcl::file::size
@cmd -name "Built-in: tcl::file::size"\
-summary\
{Size of named file in bytes.}\
-help\
"Returns a decimal string giving the size of file ${$I}name${$NI} in bytes.
If the file does not exist or its size cannot be queried then an error is generated."
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
lappend PUNKARGS [list {
@id -id ::tcl::file::split
@cmd -name "Built-in: tcl::file::split"\
-summary\
{Split a path into list of components.}\
-help\
"Returns a list whose elements are the path components in ${$I}name${$NI}. The first element of the list will have
the same path type as ${$I}name${$NI}. All other elements will be relative. Path separators will be discarded unless
they are needed to ensure that an element is unambiguously relative."
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
lappend PUNKARGS [list {
@id -id ::tcl::file::stat
@cmd -name "Built-in: tcl::file::stat"\
@ -3399,8 +3571,20 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
As such, they can be relied upon to be used with operating-system native APIs
and external programs that require a filename."
@values -min 0 -max 2
nameVar -type string -optional 1
template -type string -optional 1
nameVar -type string -optional 1 -help\
"Variable to *receive* the name of the created temporary file.
Any existing value in the variable will not be read, and is just overwritten."
template -type string -optional 1 -help\
"On some platforms, such as windows:
- file extension is ignored.
- any directory components are ignored and
the last segment is used as a prefix for the temporary file name.
- If the TMP or TEMP environment variables are set, they are used
as the directory for the temporary file, otherwise the user's home
directory is used if it can be determined. (may depend on existence
of HOME or USERPROFILE environment variables.)
On other platforms, such as unix, the template may be handled
differently."
} "@doc -name Manpage: -url [manpage_tcl file]"]
#tildeexpand
@ -4528,11 +4712,16 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
}]}
}
@values -min 1
#{args body ?namespace?} is a single argument that is a list of two or three elements,
#as opposed to a clause of separate arguments.
#we don't have a way to validate the type of each element in a list - we can only check the length of the whole list.
@values -min 1 -max -1
"{args body ?namespace?}" -optional 0 -type list -minsize 2 -maxsize 3
arg -type any -optional 1 -multiple 1
} "@doc -name Manpage: -url [manpage_tcl apply]"\
{
@examples -help {
@ -7094,7 +7283,7 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
start -type number|expr
count -type literalprefix(count)
countelements -type number|expr
"by step" -type {literalprefix(by) number|expr} -optional 1
"by step" -type {?literalprefix(by)? number|expr} -optional 1
@form -form count
@leaders -min 0 -max 0
@ -10621,15 +10810,34 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
#force all on_handlers to be together and all try_handlers to be together, and it would force
#one type of handler to be listed always before or always after the other.
handler -optional 1 -multiple 1 -type {literal(on)|literal(trap) string list string}\
-typesynopsis {"" code|pattern variableList script}
-typesynopsis {"" oncode_or_trappattern variableList script}
#in our typesynopsis we deliberately don't put a pipe symbol in oncode_or_trappattern.
# e.g code|pattern would imply either on or trap could be combined with either code or pattern, which is not the case.
#todo?
#a way to define a compound type?
#handler -optional 1 -multiple 1 -type {<on_handler>|<try_handler>}
##<on_handler> -type {literal(on) <code> <variableList> <script>}
##<code> -type int -choices {0|ok 1|error 2|return 3|break 4|continue} -choicelabels {...}
#consider bracketed forms for -type - but we would have to do more complex parsing to determine size of clauses
##handler -type {(literal(on) code variableList script)|(literal(trap) pattern variableList script)}
## in this case either possible handler has length 4 - but we could easily imagine cases where different handlers have different lengths
#this gets unwieldy in synopsis listings.
#a way to define a compound type? perhaps with arity indicators for the component types? e.g
#handler -optional 1 -multiple 1 -type {<on_handler:4>|<try_handler:4>}
##on_handler:4 -type {literal(on) code variableList script}
##code -type int -choices {0|ok 1|error 2|return 3|break 4|continue} -choicelabels {...}
#..
##<try_handler> -type {literal(trap) <pattern> <variableList> <script>}
##<pattern> -type list
##try_handler -type {literal(trap) pattern variableList script}
##pattern -type list
##etc
#how would we declare arity for a compound type that has alternate subtypes of different arity?
#e.g <generalhandler>:3..4 -type {<on_handler:4>|<other_handler:3>}
#would these types be global or per definition?
#if both allowed - what about documentation packages clashing names?
#require some kind of namespacing for types? e.g package::types::code ?)
#e.g punk::args::moduledoc::tkcore::anchor (n|ne|e|se|s|sw|w|nw|center)
#could we provide a way to import for a definition eg @typeimport -package punk::args::moduledoc::tkcore
# so that the types defined there could be used in our definitions without needing to namespace them?
#consider also RPN for compound type definitions
##<mytype1> -type {{int double OR}}
@ -12052,7 +12260,7 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
@form -form "basic"
pattern -type string -optional 1 -help "glob pattern"
@form -form "controlledglob"
@form -form "controlled"
@values -min 2 -max 2
patterntype -type string -choices {-glob -regexp} -typesynopsis -glob|-regex -optional 0
pattern -type string -optional 0

21
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/auto_exec-0.1.0.tm

@ -96,7 +96,24 @@ tcl::namespace::eval punk::auto_exec {
-summary\
"Manage the hash table of autoexec commands cached in ::auto_execs."\
-help\
{see also ::punk::auto_exec::rehash}
{Manage the cache of autoexec commands in the ::auto_execs array.
This is analogous to the 'hash' command in shells such as csh, tcsh and zsh, or 'hash' in bash.
It can be used to display the current cached ${$B}auto_execok${$N} commands, to add new commands to the cache,
to delete commands from the cache, and to clear the cache.
When adding new commands to the cache, it will attempt to find the command string associated with
the given name by calling auto_execok for that name, and if found it will add it to the cache.
If not found, it will display an error message on stderr for that name and add an empty string to
the cache for that name if the name is an absolute path or a bare word.
When displaying commands with ${$B}hash -t ${$I}name${$NI}${$N}, if only a single name is provided, then the output will
be the raw command string associated with that autoexec command in the hash table. If multiple names
are provided, then the output will be a string containing each name and its associated command string
on a separate line.
see also ::punk::auto_exec::rehash}
#---------------------
@form -form {show_or_set}
@ -125,7 +142,7 @@ tcl::namespace::eval punk::auto_exec {
If multiple names are provided, then the output will be a string containing each
name and its associated command string on a separate line."
#---------------------
@form -form {delete}
@form -form {delete} -summary "Delete autoexec commands from the hash table."
@opts
-d -type none -optional 0 -help\
"Delete specified autoexec commands from the hash table."

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

@ -1860,8 +1860,10 @@ tcl::namespace::eval punk::char {
lappend settype_list [tcl::dict::get $charsets $setname settype]
}
set charset_names [linsert $charset_names 0 "Set Name"]
set settype_list [linsert $settype_list 0 "Set Type"]
#set charset_names [linsert $charset_names 0 "Set Name"]
ledit charset_names 0 -1 "Set Name"
#set settype_list [linsert $settype_list 0 "Set Type"]
ledit settype_list 0 -1 "Set Type"
return [textblock::join -- [list_as_lines -- $charset_names] " " [list_as_lines $settype_list]]
}

20
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/console-0.1.1.tm

@ -2608,7 +2608,7 @@ namespace eval punk::console {
error "dec_request_setting unrecognised name $name. Known values: [dict keys $DECRQSS_DICT]"
}
set str [dict get $DECRQSS_DICT $name]
set re_str [string map [list * \\* \$ \\\$ + \\+ ( \\(] $str] ;#regex escaped
set re_str [string map [list | \\| * \\* \$ \\\$ + \\+ ( \\( ) \\)] $str] ;#regex escaped
#review {[0-9;:]} - too restrictive? - what values can be returned? alnum? - we perhaps at least need to exclude ESC so we don't overmatch
set capturingregex [string map [list %s% $re_str] {(.*)(\x1bP([0-1]\$r[0-9;:]*)(?:%s%){0,1}\x1b\\)$}] ;#must capture prefix,entire-response,response-payload
#todo - handle xterm : [0-1] $ r D...D ST
@ -2938,6 +2938,13 @@ namespace eval punk::console {
proc clear_all {} {
puts -nonewline stdout [punk::ansi::clear_all]
}
proc clear_scrollback {} {
puts -nonewline stdout [punk::ansi::clear_scrollback]
}
proc S8C1R {} {
puts -nonewline stdout [punk::ansi::S8C1R]
}
proc reset {} {
puts -nonewline stdout [punk::ansi::reset]
}
@ -3073,11 +3080,12 @@ namespace eval punk::console {
proc move_emitblock_return {row col textblock} {
lassign [punk::console::get_cursor_pos_list] orig_row orig_col
set commands ""
foreach ln [split $textblock \n] {
append commands [punk::ansi::move_emit $row $col $ln]
incr row
}
set commands [punk::ansi::move_emit $row $col $textblock] ;#move_emit can handle multiple line blocks.
#set commands ""
#foreach ln [split $textblock \n] {
# append commands [punk::ansi::move_emit $row $col $ln]
# incr row
#}
append commands [punk::ansi::move $orig_row $orig_col]
puts -nonewline $commands
return

438
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/lib-0.1.6.tm

@ -2190,6 +2190,7 @@ namespace eval punk::lib {
} else {
set qry $key
}
#pipeline - use punk patterns.
% thisval.= $qry= $dval
}
@ -2219,7 +2220,7 @@ namespace eval punk::lib {
string {
set hidekey 1
switch -- $key {
"%string" {
"%string" - "%str" {
set hidekey 1
set thisval $dval
}
@ -2231,7 +2232,9 @@ namespace eval punk::lib {
}
default {
switch -glob -- $key {
*lpad-* {
%XXXlpad-* {
#todo - remove
#moved to punk patterns
set hidekey 1
lassign [split $key -] _ extra
set width [expr {[textblock::width $dval] + $extra}]
@ -2255,7 +2258,10 @@ namespace eval punk::lib {
set width [expr {[textblock::width $dval] + [tcl::string::length $extra]}]
set thisval [textblock::pad $dval -which right -width $width -padchar $extra]
}
%split-* {
%XXXsplit-* {
#todo - remove
# moved to punk patterns.
#supported here by default branch.
#split on one or more chars - review
set hidekey 1
lassign [split $key -] _ splitchars
@ -2271,7 +2277,7 @@ namespace eval punk::lib {
if {[string index $key 0] ne "%"} {
set key %$key
}
#pipeline
#pipeline - use punk patterns.
% thisval.= $key= $thisval
}
}
@ -3250,7 +3256,7 @@ namespace eval punk::lib {
We will get something like 10+1 - which can be resolved safely with expr
"
@values -min 2 -max 2
datalength -type integer
datalength -type integer -range {0 ""}
index -type indexexpression
}
proc lindex_resolve {len index {base 0}} {
@ -3280,6 +3286,7 @@ namespace eval punk::lib {
#basic forward compatibility with integers such as 1_000 for 8.6.x
set index [tcl::string::map {_ {}} $index]
set len [tcl::string::map {_ {}} $len]
set base [tcl::string::map {_ {}} $base]
}
if {![string is integer -strict $len] || $len < 0} {
@ -3339,10 +3346,10 @@ namespace eval punk::lib {
return $based_max
}
} else {
#plain +-<int> already handled above.
#plain +-<int> already handled above. (but not +-<int>+-<int> etc)
#we are trying to avoid evaluating unbraced expr of potentially insecure origin
#regexp must split a++b to a + +b (not a+ + b) ie first +/- is the op
if {[regexp {([^+-]*)([+-])(.*)} $index _match a op b]} {
if {[regexp {([+-]{0,1}[^+-]*)([+-])(.*)} $index _match a op b]} {
if {[string is integer -strict $a] && [string is integer -strict $b]} {
if {$op eq "-"} {
set index [expr {$a - $b}]
@ -3374,6 +3381,16 @@ namespace eval punk::lib {
#[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
#REVIEW - we need compat for 1_000 etc to handle things like toml even in 8.6?
#A basic string map means we aren't properly validating
#todo - be stricter about malformations such as 1000_
if {![string is integer -strict 1_0]} {
#basic forward compatibility with integers such as 1_000 for 8.6.x
set index [tcl::string::map {_ {}} $index]
set len [tcl::string::map {_ {}} $len]
set base [tcl::string::map {_ {}} $base]
}
if {![string is integer -strict $len] || $len < 0} {
error "lindex_resolve_basic len must be an integer greater than or equal to zero"
}
@ -4196,6 +4213,7 @@ namespace eval punk::lib {
# important for pipeline & match_assign
# -line trimline|trimleft|trimright -block trimhead|trimtail|triminner|trimall|trimhead1|trimtail1|collateempty -commandprefix {string length} ?
# -block trimming only trims completely empty lines. use -line trimming to remove whitespace e.g -line trimright will clear empty lines without affecting leading whitespace on other lines that aren't pure whitespace
set linelist_body {
set usage "linelist ?-ansiresets auto|<bool>? ?-ansireplays 0|1? ?-line trimline|trimleft|trimright? ?-block trimhead|trimtail|triminner|trimall|trimhead1|trimtail1|collateempty? -commandprefix <cmdlist> text"
if {[llength $args] == 0} {
@ -4487,7 +4505,8 @@ namespace eval punk::lib {
}
#set newreplay [join $codestack ""]
set newreplay [punk::ansi::codetype::sgr_merge_list {*}$codestack]
#set newreplay [punk::ansi::codetype::sgr_merge_list {*}$codestack]
set newreplay [punk::ansi::codetype::sgr_merge $codestack]
if {$line_has_sgr && $newreplay ne $replaycodes} {
#adjust if it doesn't already does a reset at start
@ -4823,7 +4842,8 @@ namespace eval punk::lib {
}
#set newreplay [join $codestack ""]
set newreplay [punk::ansi::codetype::sgr_merge_list {*}$codestack]
#set newreplay [punk::ansi::codetype::sgr_merge_list {*}$codestack]
set newreplay [punk::ansi::codetype::sgr_merge $codestack]
if {$RST ne "" && $line_has_sgr && $newreplay ne $replaycodes} {
#adjust if it doesn't already does a reset at start
@ -4868,6 +4888,406 @@ namespace eval punk::lib {
set linelist_body [string map {<require_punk_ansi> "package require punk::ansi"} $linelist_body]
}
proc linelist {args} $linelist_body
set linelist_body2 {
set usage "linelist ?-ansiresets auto|<bool>? ?-ansireplays 0|1? ?-line trimline|trimleft|trimright? ?-block trimhead|trimtail|triminner|trimall|trimhead1|trimtail1|collateempty? -commandprefix <cmdlist> text"
if {[llength $args] == 0} {
error "linelist missing textchunk argument usage:$usage"
}
set text [lindex $args end]
set text [string map {\r\n \n} $text] ;#review - option?
set arglist [lrange $args 0 end-1]
set opts [tcl::dict::create\
-block {trimhead1 trimtail1}\
-line {}\
-commandprefix ""\
-ansiresets auto\
-ansireplays 0\
]
foreach {o v} $arglist {
switch -- $o {
-block - -line - -commandprefix - -ansiresets - -ansireplays {
tcl::dict::set opts $o $v
}
default {
error "linelist: Unrecognized option '$o' usage:$usage"
}
}
}
# -- --- --- --- --- ---
set opt_block [tcl::dict::get $opts -block]
if {[llength $opt_block]} {
foreach bo $opt_block {
switch -- $bo {
trimhead - trimtail - triminner - trimall - trimhead1 - trimtail1 - collateempty {}
default {
set known_blockopts [list trimhead trimtail triminner trimall trimhead1 trimtail1 collateempty]
error "linelist: unknown -block option value: $bo known values: $known_blockopts"
}
}
}
#normalize certain combos
if {"trimhead" in $opt_block && [set posn [lsearch $opt_block trimhead1]] >=0} {
set opt_block [lreplace $opt_block $posn $posn]
}
if {"trimtail" in $opt_block && [set posn [lsearch $opt_block trimtail1]] >=0} {
set opt_block [lreplace $opt_block $posn $posn]
}
if {"trimall" in $opt_block} {
#no other block options make sense in combination with this
set opt_block [list "trimall"]
}
#TODO
if {"triminner" in $opt_block } {
error "linelist -block triminner not implemented - sorry"
}
}
# -- --- --- --- --- ---
set opt_line [tcl::dict::get $opts -line]
set tl_left 0
set tl_right 0
set tl_both 0
foreach lo $opt_line {
switch -- $lo {
trimline {
set tl_both 1
}
trimleft {
set tl_left 1
}
trimright {
set tl_right 1
}
default {
set known_lineopts [list trimline trimleft trimright]
error "linelist: unknown -line option value: $lo known values: $known_lineopts"
}
}
}
#normalize trimleft trimright combo
if {$tl_left && $tl_right} {
set opt_line [list "trimline"]
set tl_both 1
}
# -- --- --- --- --- ---
set opt_commandprefix [tcl::dict::get $opts -commandprefix]
# -- --- --- --- --- ---
set opt_ansiresets [tcl::dict::get $opts -ansiresets]
# -- --- --- --- --- ---
set opt_ansireplays [tcl::dict::get $opts -ansireplays]
if {$opt_ansireplays} {
if {$opt_ansiresets eq "auto"} {
set opt_ansiresets 1
}
} else {
if {$opt_ansiresets eq "auto"} {
set opt_ansiresets 0
}
}
# -- --- --- --- --- ---
#set linelist [list]
#set nlsplit [split $text \n]
set linelist [split $text \n]
set original_length [llength $linelist]
#---------------------------
#todo - consider applying these inline later
if {![llength $opt_line]} {
#set linelist $nlsplit
#lappend linelist {*}$nlsplit
} else {
#already normalized trimleft+trimright to trimline
set nlsplit $linelist
#set linelist [list]
if {$tl_both} {
set i 0
foreach ln $linelist {
#lappend linelist [string trim $ln]
lset linelist $i [string trim $ln]
incr i
}
} elseif {$tl_left} {
set i 0
foreach ln $linelist {
#lappend linelist [string trimleft $ln]
lset linelist $i [string trimleft $ln]
incr i
}
} elseif {$tl_right} {
set i 0
foreach ln $nlsplit {
#lappend linelist [string trimright $ln]
lset linelist $i [string trimright $ln]
incr i
}
}
}
#---------------------------
set remove_indices [list]
if {"collateempty" in $opt_block} {
set last "-"
for {set i 0} {$i < $original_length} {incr i} {
if {[lindex $linelist $i] ne ""} {
set last "-"
} else {
if {$last ne ""} {
lappend remove_indices $i
set last ""
}
}
}
}
if {"trimall" in $opt_block} {
#we have already made sure there are no other block options that would conflict with this
#set linelist [lsearch -all -inline -not -exact $linelist[set linelist {}] ""]
#set remove_indices [list]
for {set i 0} {$i < $original_length} {incr i} {
if {[lindex $linelist $i] eq ""} {
lappend remove_indices $i
}
}
} else {
if {"trimhead" in $opt_block} {
#set remove_indices [list]
for {set i 0} {$i < $original_length} {incr i} {
if {[lindex $linelist $i] ne ""} {
break
} else {
lappend remove_indices $i
}
}
}
if {"trimtail" in $opt_block} {
set remove_indices [list]
for {set i [expr {$original_length-1}]} {$i >=0} {incr i -1} {
if {[lindex $linelist $i] ne ""} {
break
} else {
lappend remove_indices $i
}
}
#set revlinelist [lreverse $linelist][set linelist {}]
#set i 0
#foreach ln $revlinelist {
# if {$ln ne ""} {
# set linelist [lreverse [lrange $revlinelist $i end]]
# break
# }
# incr i
#}
}
# --- ---
set start 0
set end "end"
if {"trimhead1" in $opt_block} {
if {[lindex $linelist 0] eq ""} {
lappend remove_indices 0
}
}
if {"trimtail1" in $opt_block} {
if {[lindex $linelist end] eq ""} {
lappend remove_indices [expr {$original_length-1}]
}
}
#set linelist [lrange $linelist $start $end]
}
#review - we need to make sure ansiresets don't accumulate/grow on any line
#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} {
<require_punk_ansi> ;#package require punk::ansi
if {$opt_ansiresets} {
set RST "\x1b\[0m"
} else {
set RST ""
}
set replaycodes $RST ;#todo - default?
#set transformed [list]
#shortcircuit common case of no ansi
#NOTE: running ta::detect on a list (or dict) as a whole can be problematic if items in the list have backslash escapes due to Tcl list quoting and escaping behaviour.
#This commonly happens if there is an unbalanced brace (which is a normal occurrence and needs to be handled)
#ta::detect on a list of ansi-containing string may appear to work for some simple inputs but is not reliable
#detect_in_list/detectcode_in_list will check at first level. (not intended for detecting ansi in deeper structures)
#we use detectcode_in_list instead of detect_in_list
#detectcode_in_list will detect unclosed (or unopened) paired sequences such as PM (privacy message)
# - but the main reason is it is slightly faster.
if {![punk::ansi::ta::detectcode_in_list $linelist]} {
if {$opt_ansiresets} {
for {set i 0} {$i < $original_length} {incr i} {
if {$i in $remove_indices} {
continue
}
lset linelist $i $RST[lindex $linelist $i]$RST
}
}
} else {
#INLINE punk::ansi::codetype::is_sgr_reset
#regexp {\x1b\[0*m$} $code
set re_is_sgr_reset {\x1b\[0*m$}
#INLINE punk::ansi::codetype::is_sgr
#regexp {\033\[[0-9;:]*m$} $code
set re_is_sgr {\x1b\[[0-9;:]*m$}
#foreach ln $linelist {}
for {set i 0} {$i < $original_length} {incr i} {
if {$i in $remove_indices} {
continue
}
#set ln [lindex $linelist $i]
#set is_replay_pure_reset [regexp {\x1b\[0*m$} $replaycodes] ;#only looks at tail code - but if tail is pure reset - any prefix is ignorable
#set ansisplits [punk::ansi::ta::split_codes_single $ln] ;#REVIEW - this split accounts for a large portion of the time taken to run this function.
#get_codes_single lists only the codes. no plaintext or empty elements
set ansisplits [punk::ansi::ta::get_codes_single [lindex $linelist $i]] ;#REVIEW - this split accounts for a large portion of the time taken to run this function.
if {[llength $ansisplits] == 0} {
#plaintext only - no ansi codes in line
#lappend transformed [string cat $replaycodes $ln $RST]
lset linelist $i $replaycodes[lindex $linelist $i]$RST
#leave replaycodes as is for next line
set nextreplay $replaycodes
} else {
set tail $RST
set lastcode [lindex $ansisplits end] ;#may or may not be SGR
set lastcodeoffset [expr {[string length $lastcode]-1}]
if {[punk::ansi::codetype::is_sgr_reset $lastcode]} {
if {[string range [lindex $linelist $i] end-$lastcodeoffset end] eq $lastcode} {
#last plaintext is empty. So the line is already suffixed with a reset
set tail ""
} else {
#trailing text has been reset within line - but no tail reset present
#we normalize by putting a tail reset on anyway
set tail $RST
}
set nextreplay $RST
} elseif {[string range [lindex $linelist $i] end-$lastcodeoffset end] eq $lastcode && [punk::ansi::codetype::has_sgr_leadingreset $lastcode]} {
#code is at tail (no trailing plaintext)
#No tail reset - and no need to examine whole line to determine stack that is in effect
set tail $RST
set nextreplay $lastcode
} else {
#last codeset doesn't reset from earlier codes or isn't SGR - so we have to look at whole line to determine codes in effect
#last codeset doesn't end in a pure-reset
#whether code was at very end or not - add a reset tail
set tail $RST
#determine effective replay for line
set codestack [list start]
foreach code $ansisplits {
if {[tcl::string::index $code end] eq "m"} {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set codestack [list] ;#different from 'start' marked - this means we've had a reset
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set codestack [list $code]
} else {
if {[punk::ansi::codetype::is_sgr $code]} {
#todo - proper test of each code - so we only take latest background/foreground etc.
#requires handling codes with varying numbers of parameters.
#basic simplification - remove straight dupes.
set dup_posns [lsearch -all -exact $codestack $code] ;#!must use -exact as codes have square brackets which are interpreted as glob chars.
set codestack [lremove $codestack {*}$dup_posns]
lappend codestack $code
}
}
}
;#else gx0 or other code - we don't want to stack it with SGR codes
}
if {[llength $codestack] == 1 && [lindex $codestack 0] eq "start"} {
#No SGRs - may have been other codes
set line_has_sgr 0
} else {
#list is either empty or begins with start - empty means it had SGR reset - so it still invalidates current state of replaycodes
set line_has_sgr 1
if {[lindex $codestack 0] eq "start"} {
#set codestack [lrange $codestack 1 end]
ledit codestack 0 0
}
}
if {$line_has_sgr} {
#set newreplay [punk::ansi::codetype::sgr_merge_list {*}$codestack]
set newreplay [punk::ansi::codetype::sgr_merge $codestack]
if {$newreplay ne $replaycodes} {
#adjust if it doesn't already does a reset at start
if {$RST ne ""} {
if {[punk::ansi::codetype::has_sgr_leadingreset $newreplay]} {
set nextreplay $newreplay
} else {
set nextreplay $RST$newreplay
}
} else {
set nextreplay $newreplay
}
} else {
set nextreplay $replaycodes
}
} else {
set nextreplay $replaycodes
}
}
if {"$replaycodes$tail" ne ""} {
if {[punk::ansi::codetype::has_sgr_leadingreset [lindex $linelist $i]]} {
#no point attaching any replay
#lappend transformed [string cat $ln $tail]
if {$tail ne ""} {
lset linelist $i [lindex $linelist $i]$tail
}
} else {
#lappend transformed [string cat $replaycodes $ln $tail]
lset linelist $i $replaycodes[lindex $linelist $i]$tail
}
}
}
set replaycodes $nextreplay
}
#jjj
#set linelist $transformed
}
}
#todo - run this before ansireplay processing and adjust indices accordingly? or just run it after as is and accept that commandprefix will be added to each line after replay processing?
if {[llength $opt_commandprefix]} {
for {set i 0} {$i < $original_length} {incr i} {
if {$i in $remove_indices} {
continue
}
lset linelist $i [{*}$opt_commandprefix [lindex $linelist $i]]
}
#set transformed [list]
#foreach ln $linelist {
# lappend transformed [{*}$opt_commandprefix $ln]
#}
#set linelist $transformed
}
if {[llength $remove_indices]} {
set linelist [lremove $linelist {*}$remove_indices]
}
return $linelist
}
if {$has_punk_ansi} {
#optimise linelist as much as possible
set linelist_body2 [string map {<require_punk_ansi> ""} $linelist_body2]
} else {
#punk ansi not avail at time of package load.
#by putting in calls to punk::ansi the user will get appropriate error messages
set linelist_body2 [string map {<require_punk_ansi> "package require punk::ansi"} $linelist_body2]
}
proc linelist {args} $linelist_body2
interp alias {} errortime {} punk::lib::errortime

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

@ -950,6 +950,7 @@ tcl::namespace::eval ::punk::libunknown {
}
if {$has_prefix} {
set update [linsert $update end-$offset $new]
#end based index used with linsert - so can't replace with ledit.
} else {
lappend update $new
}

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

@ -43,7 +43,7 @@ namespace eval punk::mix::commandset::repo {
lappend PUNKARGS [list {
@id -id ::punk::mix::commandset::repo::fossilize
@cmd -name punk::mix::commandset::repo::fossilize
@cmd -name punk::mix::commandset::repo::fossilize\
-summary\
"Initialise and check in a project to fossil (unimplemented)."\
-help\
@ -56,7 +56,7 @@ namespace eval punk::mix::commandset::repo {
lappend PUNKARGS [list {
@id -id ::punk::mix::commandset::repo::unfossilize
@cmd -name punk::mix::commandset::repo::unfossilize
@cmd -name punk::mix::commandset::repo::unfossilize\
-summary\
"Remove/archive .fossil (unimplemented)."\
-help\
@ -92,9 +92,9 @@ namespace eval punk::mix::commandset::repo {
#punk::args
lappend PUNKARGS [list {
@id -id ::punk::mix::commandset::repo::fossil-move-repository
@cmd -name punk::mix::commandset::repo::fossil-move-repository
@cmd -name punk::mix::commandset::repo::fossil-move-repository\
-summary\
"Move a fossil repository database file."\
"Interactively move a fossil repository database file."\
-help\
"Move the fossil repository file (usually named with .fossil extension).
This is an interactive function which will prompt for answers on stdin

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

@ -1170,22 +1170,24 @@ tcl::namespace::eval punk::ns {
#NOTE aliases may not be commands in current namespace - but we want to show them (marked red and with R)
#
set children [list]
set commands [list]
set exported [list]
set imported [list]
set aliases [list]
set procs [list]
set ensembles [list]
set ooclasses [list]
set ooobjects [list]
set children [list]
set packagetails [list]
set packageprefixes [list]
set commands [list]
set exported [list]
set imported [list]
set aliases [list]
set procs [list]
set ensembles [list]
set ooclasses [list]
set ooobjects [list]
set ooprivateobjects [list]
set ooprivateclasses [list]
set native [list]
set interps [list]
set coroutines [list]
set zlibstreams [list]
set usageinfo [list]
set native [list]
set interps [list]
set coroutines [list]
set zlibstreams [list]
set usageinfo [list]
if {![dict size $opt_nsdict]} {
set nsmatches [get_ns_dicts $fq_glob -allbelow 0]
@ -1216,6 +1218,8 @@ tcl::namespace::eval punk::ns {
package require overtype
if {"children" in $types} {
set children [dict get $contents children]
set packagetails [dict get $contents packagetails]
set packageprefixes [dict get $contents packageprefixes]
}
if {"commands" in $types} {
set commands [dict get $contents commands]
@ -1368,12 +1372,26 @@ tcl::namespace::eval punk::ns {
set c_ooC [a+ term-cornflowerblue] ;#privateClass
set c_zst [a+ term-yellow] ;#zlibstreams
set a1 [a][a+ cyan]
set a1 [a][a+ cyan] ;#child namespace SGR code.
foreach ch1 $children1 ch2 $children2 cmd1 $elements1 cmd2 $elements2 cmd3 $elements3 cmd4 $elements4 {
set c1 [a+ white]
set c2 [a+ white]
set c3 [a+ white]
set c4 [a+ white]
foreach nsvar {ch1 ch2} {
set v [set $nsvar]
if {$v in $packagetails} {
#may also be a packageprefix.
if {$v in $packageprefixes} {
set $nsvar [a+ underdouble]$v
} else {
#just a package - no prefix - we want to underline but not doubled
set $nsvar [a+ underline]$v
}
} elseif {$v in $packageprefixes} {
set $nsvar [a+ underdotted]$v
}
}
for {set i 1} {$i <= 4} {incr i} {
if {[llength [set cmd$i]]} {
@ -1441,7 +1459,7 @@ tcl::namespace::eval punk::ns {
}
#lappend displaylist $a1[overtype::left $col1 $ch1][a+]$a1[overtype::left $col2 $ch2][a+]$c1[overtype::left $col3 $cmd1][a+]$c2[overtype::left $col4 $cmd2][a+]$c3[overtype::left $col5 $cmd3][a+]$c4$cmd4[a+]
lappend displaylist $a1[overtype::left $col1 $ch1][a]$a1[overtype::left $col2 $ch2][a]$c1[overtype::left $col3 $cmd1][a]$c2[overtype::left $col4 $cmd2][a]$c3[overtype::left $col5 $cmd3][a]$c4$cmd4[a]
lappend displaylist $a1[overtype::left $col1 $ch1[a]][a]$a1[overtype::left $col2 $ch2][a]$c1[overtype::left $col3 $cmd1][a]$c2[overtype::left $col4 $cmd2][a]$c3[overtype::left $col5 $cmd3][a]$c4$cmd4[a]
}
return [list_as_lines $displaylist]
@ -3043,8 +3061,11 @@ y" {return quirkykeyscript}
set nspathcommands [dict get $opts -nspathcommands]
# -- --- --- --- --- --- --- --- --- --- --- ---
set packagetails [list] ;#child namespaces which are an exact match for a package name
set packageprefixes [list] ;#child namespaces which are a prefix match for a package name - but not an exact match
#set location [nsprefix $fq_glob]
set commands [list]
set commands [list]
set nsglob [nsprefix $fq_glob]
set glob [nstail $fq_glob]
@ -3471,10 +3492,27 @@ y" {return quirkykeyscript}
# set childtailmatches [lsort $childtailmatches]
#}
set childtailmatches [lsort -dictionary $childtailmatches]
foreach ct $childtailmatches {
set fqchild [nsjoin $location $ct]
set searchname [string trimleft $fqchild :]
foreach pkgname [lsearch -all -inline [package names] $searchname*] {
if {$pkgname eq $searchname} {
#exact match.
lappend packagetails $ct
} else {
if {[string match ${searchname}::* $pkgname]} {
#prefix match - but not exact match
lappend packageprefixes $ct
}
}
}
}
set nsdict [dict create\
location $location\
children $childtailmatches\
packagetails $packagetails\
packageprefixes $packageprefixes\
commands $commands\
procs $procs\
exported $exported\
@ -4807,7 +4845,8 @@ y" {return quirkykeyscript}
set scriptcmd [dict get $scriptinfo which]
set scriptargs [lrange $origin 1 end]
#ledit args -1 -1 {*}$scriptargs ;#prepend
set args [linsert $args 1 {*}$scriptargs]
#set args [linsert $args 1 {*}$scriptargs]
ledit args 1 -1 {*}$scriptargs ;#insert scriptargs before arg at index 1
#JJJ review
#set resolvedargs $scriptargs
punk::args::update_definitions [list [namespace qualifiers $scriptcmd]]
@ -5240,7 +5279,7 @@ y" {return quirkykeyscript}
the synopsis for that form.
"
@opts
-form -type string -default * -help\
-form -type number|name -default * -help\
"Ordinal index or name of command form."
-return -type string -default full -choices {full summary dict}
@values -min 1 -max -1
@ -5291,7 +5330,7 @@ y" {return quirkykeyscript}
full - summary {
set resultstr ""
foreach synline [split $syn \n] {
if {[string range $synline 0 1] eq "# "} {
if {[string range $synline 0 1] in {"# " "##"}} {
append resultstr $synline \n
} else {
#puts stderr [textblock::frame $syn]
@ -5447,9 +5486,9 @@ y" {return quirkykeyscript}
}
if {$opt_grepstr ne ""} {
if {[llength $opt_grepstr] == 1} {
set result [punk::ansi::grepstr --ignore-case -returnlines all [lindex $opt_grepstr 0] $result]
set result [punk::ansi::grepstr --ignore-case -return all [lindex $opt_grepstr 0] $result]
} else {
set result [punk::ansi::grepstr --ignore-case -returnlines all -highlight [lrange $opt_grepstr 1 end] [lindex $opt_grepstr 0] $result]
set result [punk::ansi::grepstr --ignore-case -return all -highlight [lrange $opt_grepstr 1 end] [lindex $opt_grepstr 0] $result]
}
}
return $result
@ -5529,9 +5568,9 @@ y" {return quirkykeyscript}
}
if {$opt_grepstr ne ""} {
if {[llength $opt_grepstr] == 1} {
set result [punk::ansi::grepstr --ignore-case -returnlines all [lindex $opt_grepstr 0] $result]
set result [punk::ansi::grepstr --ignore-case -return all [lindex $opt_grepstr 0] $result]
} else {
set result [punk::ansi::grepstr --ignore-case -returnlines all -highlight [lrange $opt_grepstr 1 end] [lindex $opt_grepstr 0] $result]
set result [punk::ansi::grepstr --ignore-case -return all -highlight [lrange $opt_grepstr 1 end] [lindex $opt_grepstr 0] $result]
}
}
return $result
@ -6674,7 +6713,7 @@ y" {return quirkykeyscript}
set body [punk::ansi::grepstr -return all -highlight term-teal {^(\{)|[^\\](\{+)} $body]
set body [punk::ansi::grepstr -return all -highlight term-teal {[^\\](\}+)} $body]
set body [punk::ansi::grepstr -return all -highlight term-orange {\[|\]} $body]
set body [punk::ansi::grepstr -return all -highlight term-orange1 {\[|\]} $body]
}
default {
set is_highlighted 0

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

@ -1078,7 +1078,8 @@ namespace eval punk::repl::class {
# incr nextrow -1
#}
#set o_rendered_lines [linsert $o_rendered_lines $cursor_row_idx ""]
ledit o_rendered_lines $cursor_row_idx $cursor_row_idx-1 ""
#ledit o_rendered_lines $cursor_row_idx $cursor_row_idx-1 ""
ledit o_rendered_lines $cursor_row_idx -1 ""
set o_cursor_col 1
}
@ -1151,7 +1152,9 @@ namespace eval punk::repl::class {
lappend o_rendered_lines ""
set activeline ""
}
lset o_rendered_lines $cursor_row_idx $result
#JULZ
#lset o_rendered_lines $cursor_row_idx $result
lset o_rendered_lines $cursor_row_idx $result\x1b[m
incr i
}
@ -1289,7 +1292,9 @@ namespace eval punk::repl::class {
set charhighlight [punk::ansi::a+ reverse]$char_at_cursor[a]
}
set cursorline [overtype::renderline -transparent 1 -insert_mode 0 -expand_right 0 $cursorline $prefix$charhighlight$suffix]
lset lines $o_cursor_row-1 $cursorline
#JULZ
#lset lines $o_cursor_row-1 $cursorline
lset lines $o_cursor_row-1 $cursorline\x1b[m
}
set numcol "$ANSI_linenum[join $nums \n][a]"
@ -1765,7 +1770,7 @@ proc punk::repl::console_debugview {editbuf consolewidth args} {
set patch_height [expr {2 + $debug_height + 2}]
set spacepatch [textblock::block $debug_width $patch_height " "]
#puts -nonewline [punk::ansi::cursor_off]
punk::console::cursor_off
#punk::console::cursor_off
#use non cursorsave versions - cursor save/restore will interfere with any concurrent ansi rendering that uses save/restore - because save/restore is a single item, not a stack.
set debug_offset [expr {$consolewidth - $debug_width - $opt_rightmargin}]
set row_clear [expr {$opt_row -2}]
@ -1773,7 +1778,7 @@ proc punk::repl::console_debugview {editbuf consolewidth args} {
punk::console::move_emitblock_return $opt_row $debug_offset $info
set topleft [list $debug_offset $opt_row] ;#col,row REVIEW
#puts -nonewline [punk::ansi::cursor_on]
punk::console::cursor_on
#punk::console::cursor_on
flush stdout
return [dict create width $debug_width height $debug_height topleft $topleft]
@ -2000,8 +2005,12 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
#if {$chunk eq "\x1b\[C"} {
#}
punk::console::cursor_off
flush stdout
$editbuf add_chunk $chunk
#--------------------------
# editbuf and debugview rhs frames
#for now disable entirely on vt52 - we can only do cursor save restore - nothing that requires responses on stdin (?)
@ -2058,7 +2067,9 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
flush stdout
#move_column is more efficient than move since it doesn't require a response on stdin to determine current column,
#but doesn't seem to be universally supported (kermit95 vt modes for example)
#the Horizontal Position Absolute sequence ESC \[ n ` seems to be a possible alternative.
set leftmargin 3
if {!$is_vt52} {
puts -nonewline stdout [a+ cyan][punk::ansi::move_column [expr {$leftmargin +1}]][punk::ansi::erase_eol][$editbuf line $cursor_row][a][punk::ansi::move_column [expr {$leftmargin + [$editbuf cursor_column]}]]
@ -2089,6 +2100,9 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
lappend input_chunks_waiting($inputchan) $waiting
}
}
punk::console::cursor_on
flush stdout
if {$editbuf_linenum_submitted == 0} {
#(there is no line 0 - lines start at 1)
if {[$editbuf last_char] eq "\n"} {
@ -2685,8 +2699,10 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
#editbuf
#----------------------------------------------------------------------------
#after any external command - raw mode as the console sees it can be disabled
#set it to match current state of the tsv
#----------------------------------------------------------------------------
if {[tsv::get console is_raw]} {
if {$::tcl_platform(platform) eq "windows"} {
#review
@ -2696,22 +2712,24 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
set sinfo [chan configure stdin]
if {[dict exists $sinfo -inputmode]} {
if {[dict get $sinfo -inputmode] ne "raw"} {
set re_enable_required 1
set re_enable_raw_required 1
} else {
set re_enable_required 0
set re_enable_raw_required 0
}
} else {
# -inputmode unavailable
#tcl 8.6 doesn't have -inputmode - meaning it has to call punk:console::enableRaw each time
#enableRaw on windows without twapi involves launching a pwsh process - which gives a noticeable lag in keyboard input.
#enableRaw on Unix involves a call to stty - which is generally fast - but still to be avoided if not required.
set re_enable_required 1
set re_enable_raw_required 1
}
#puts stderr "-here- re-enabling raw"
if {$re_enable_required} {
if {$re_enable_raw_required} {
punk::console::enableRaw
}
}
#----------------------------------------------------------------------------
} else {
#append commandstr \n
if {$::punk::repl::signal_control_c} {
@ -3801,7 +3819,8 @@ namespace eval repl {
#puts stderr [thread::id]
if {[llength $::codethread_initstatus] == 1} {
set ::codethread_initstatus [linsert $::codethread_initstatus 0 ok]
#set ::codethread_initstatus [linsert $::codethread_initstatus 0 ok]
ledit ::codethread_initstatus 0 -1 ok
}
thread::id
}

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

@ -249,7 +249,7 @@ namespace eval punk::repo {
@form -form "parsed"
${[punk::repo::get_fossil_subcommand_usage add]}
@form -form "raw" -synopsis "exec fossil add ?OPTIONS? FILE1 ?FILE2 ...?"
@form -form "raw" -synopsis "exec fossil add \[OPTIONS\] FILE1 \[FILE2\]..."
@formdisplay -header "fossil help add" -body {${[runout -n fossil help add]}}
} ""]
@ -263,7 +263,7 @@ namespace eval punk::repo {
@form -form "parsed"
${[punk::repo::get_fossil_subcommand_usage diff]}
@form -form "raw" -synopsis "exec fossil diff ?OPTIONS? FILE1 ?FILE2 ...?"
@form -form "raw" -synopsis "exec fossil diff \[OPTIONS\] FILE1 \[FILE2\]..."
@formdisplay -header "fossil help diff" -body {${[runout -n fossil help diff]}}
} ""]

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

@ -324,7 +324,7 @@ namespace eval punkcheck {
lappend record_list $o_fileset_record
} else {
#set record_list [linsert $record_list[unset record_list] $oldposition $o_fileset_record]
ledit record_list $oldposition $oldposition-1 $o_fileset_record
ledit record_list $oldposition -1 $o_fileset_record
}
if {$o_operation ne "QUERY"} {
punkcheck::save_records_to_file $record_list $punkcheck_file
@ -796,7 +796,7 @@ namespace eval punkcheck {
lappend record_list $file_record
} else {
#set record_list [linsert $record_list[unset record_list] $oldposition $file_record]
ledit record_list $oldposition $oldposition-1 $file_record
ledit record_list $oldposition -1 $file_record
}
save_records_to_file $record_list $punkcheck_file

42
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/shellfilter-0.2.1.tm

@ -755,6 +755,8 @@ namespace eval shellfilter::chan {
#puts stdout "===[ansistring VIEW -lf 1 $o_buffered]"
set buf $o_buffered$chunk
set emit ""
#Note 8-bit csi \x9b has already been mapped in the chunk to 7-bit form \x1b\[ by the caller - so we only need to check for \x1b here
#(under review - ideally we might not want to normalize 8-bit to 7-bit in a channel transform))
if {[string last \x1b $buf] >= 0} {
#detect will detect ansi SGR and gron groff and other codes
#REVIEW - ta::detect won't detect SOS without paired ST for things like PM
@ -798,18 +800,21 @@ namespace eval shellfilter::chan {
] $c1c2] 0 3]
switch -- $leadernorm {
7CSI - 8CSI {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set o_codestack [list "\x1b\[m"]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set o_codestack [list $code]
} elseif {[punk::ansi::codetype::is_sgr $code]} {
#todo - make caching is_sgr method
set dup_posns [lsearch -all -exact $o_codestack $code]
set o_codestack [lremove $o_codestack {*}$dup_posns]
lappend o_codestack $code
} else {
set code_endswith_m [expr {[tcl::string::index $code end] eq "m"}]
if {$code_endswith_m} {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
#review this normalizing of reset to a single form.
set o_codestack [list "\x1b\[m"]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set o_codestack [list $code]
} elseif {[punk::ansi::codetype::is_sgr $code]} {
#todo - make caching is_sgr method
set dup_posns [lsearch -all -exact $o_codestack $code]
set o_codestack [lremove $o_codestack {*}$dup_posns]
lappend o_codestack $code
}
}
}
7GFX {
switch -- [tcl::string::index $code 2] {
@ -1029,6 +1034,21 @@ namespace eval shellfilter::chan {
return ""
}
}
#------------------------------------------------------
# REVIEW
#Trackcodes logic is primarily designed for 7-bit codes
#It would be complex for it to support 8-bit as well
#- we can do a simple pre-map to convert 8-bit CSI to 7-bit CSI before processing
#we already normalize things like resets to a single 7-bit form anyway.
#review - is there a need for an ansiwrap channel that preserves 8-bit codes?
#8-bit are rarely used these days - and many terminals don't support them.
#We could take the view here that we should understand them but not emit them in general.
#Nonetheless - converting them on a channel transform like this is potentially suprising in some circumstances,
#and we don't necessarily know the intent of both the producer and consumer of the stream.
set stringdata [string map [list \x9b \x1b\[ ] $stringdata]
#------------------------------------------------------
set streaminfo [my Trackcodes $stringdata]
set emit [dict get $streaminfo emit]

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

@ -2116,7 +2116,7 @@ tcl::namespace::eval textblock {
set ansibase_header [tcl::dict::get $o_opts_table -ansibase_header] ;#merged to single during configure
set ansiborder_header [tcl::dict::get $o_opts_table -ansiborder_header]
if {[tcl::dict::get $o_opts_table -frametype_header] eq "block"} {
set extrabg [punk::ansi::codetype::sgr_merge_singles [list $ansibase_header] -filter_fg 1]
set extrabg [punk::ansi::codetype::sgr_merge_singles [list $ansibase_header] -filter_fg]
set ansiborder_final $ansibase_header$ansiborder_header$extrabg
} else {
set ansiborder_final $ansibase_header$ansiborder_header
@ -2504,7 +2504,7 @@ tcl::namespace::eval textblock {
if {[tcl::dict::get $o_opts_table -frametype] eq "block"} {
#block is the only style where bg colour can fill the frame content area exactly if the L-shaped border elements are styled
#we need to only accept background ansi codes from the columndef ansibase for this
set col_bg [punk::ansi::codetype::sgr_merge_singles [list $opt_col_ansibase] -filter_fg 1] ;#special merge for block borders - don't override fg colours
set col_bg [punk::ansi::codetype::sgr_merge_singles [list $opt_col_ansibase] -filter_fg] ;#special merge for block borders - don't override fg colours
set border_ansi $body_ansibase$body_ansiborder$col_bg
} else {
set border_ansi $body_ansibase$body_ansiborder
@ -2520,7 +2520,7 @@ tcl::namespace::eval textblock {
set row_bg ""
set row_ansibase [tcl::dict::get $o_rowdefs $r -ansibase]
if {$row_ansibase ne ""} {
set row_bg [punk::ansi::codetype::sgr_merge_singles [list $row_ansibase] -filter_fg 1]
set row_bg [punk::ansi::codetype::sgr_merge_singles [list $row_ansibase] -filter_fg]
}
#todo - joinleft,joinright,joindown based on opts in args
@ -2542,8 +2542,8 @@ tcl::namespace::eval textblock {
lappend ptlens [string length $pt]
}
#set takebg [lindex $parts end-1]
#set cell_bg [punk::ansi::codetype::sgr_merge_singles [list $takebg] -filter_fg 1]
set cell_bg [punk::ansi::codetype::sgr_merge_singles $codes -filter_fg 1 -filter_reset 1]
#set cell_bg [punk::ansi::codetype::sgr_merge_singles [list $takebg] -filter_fg]
set cell_bg [punk::ansi::codetype::sgr_merge_singles $codes -filter_fg -filter_reset]
#puts --->[ansistring VIEW $codes]
if {[punk::ansi::codetype::is_sgr_reset [lindex $codes end-1]]} {
@ -2554,7 +2554,7 @@ tcl::namespace::eval textblock {
set ansibase ""
set row_ansibase ""
if {$ftblock} {
set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_body_col_row] -filter_bg 1]
set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_body_col_row] -filter_bg]
set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_final $cell_bg]]
}
set cell_ansibase $cell_ansi_tail
@ -2577,7 +2577,7 @@ tcl::namespace::eval textblock {
# set ansibase ""
# set row_ansibase ""
# if {$ftblock} {
# set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_body_col_row] -filter_bg 1]
# set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_body_col_row] -filter_bg]
# }
# set cell_ansibase $cell_ansi_tail
# } else {
@ -2643,7 +2643,7 @@ tcl::namespace::eval textblock {
}
#return empty (zero content height) row if no rows
if {![llength $cells]} {
set basebg [punk::ansi::codetype::sgr_merge_singles [list $body_ansibase] -filter_fg 1]
set basebg [punk::ansi::codetype::sgr_merge_singles [list $body_ansibase] -filter_fg]
set ansiborder_final [punk::ansi::codetype::sgr_merge [list $basebg $body_ansiborder]]
set joins [lremove $joins [lsearch $joins down*]]
@ -4497,7 +4497,7 @@ tcl::namespace::eval textblock {
foreach {pt code} [lrange $parts 2 end] {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
#set parts [linsert $parts $code_idx+1 $base]
ledit parts $code_idx+1 $code_idx $base
ledit parts $code_idx+1 -1 $base
}
incr code_idx 2
}
@ -4527,8 +4527,9 @@ tcl::namespace::eval textblock {
}
}
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set parts [linsert $parts [expr {$code_idx+1+$offset}] $base]
#set parts [linsert $parts [expr {$code_idx+1+$offset}] $base]
#ledit parts [expr {$code_idx+1+$offset}] $code_idx+$offset $base
ledit parts [expr {$code_idx+1+$offset}] -1 $base
incr offset
}
incr code_idx 2
@ -4912,7 +4913,8 @@ tcl::namespace::eval textblock {
set colour2 [tcl::string::map [list rainbow [lindex $rainbow_list $i]] $colour]
set ansi [a+ {*}$colour2]
set ansicode [punk::ansi::codetype::sgr_merge_list "" $ansi]
#set ansicode [punk::ansi::codetype::sgr_merge_list "" $ansi]
set ansicode [punk::ansi::codetype::sgr_merge [list $ansi]]
lappend clist ${ansicode}$c$RST
}
if {$noreset} {
@ -4926,8 +4928,9 @@ tcl::namespace::eval textblock {
set block ""
for {set r 0} {$r < $size} {incr r} {
set colour2 [tcl::string::map [list rainbow [lindex $rainbow_list $r]] $colour]
set ansi [a+ {*}$colour2]
set ansicode [punk::ansi::codetype::sgr_merge_list "" $ansi]
set ansi [a+ {*}$colour2] ;#not always a single SGR sequence (ESC...m) e.g when contains 'underdotted'
#set ansicode [punk::ansi::codetype::sgr_merge_list "" $ansi]
set ansicode [punk::ansi::codetype::sgr_merge [list $ansi]]
set row "$ansicode"
foreach c $charsubset {
append row $c
@ -5393,10 +5396,11 @@ tcl::namespace::eval textblock {
}
r-1 {
if {[lindex $line_chunks end] eq ""} {
#Insert so that pad *ends* up at position end-2
set line_chunks [linsert $line_chunks end-2 $pad]
#breaks layout e.g subtables in: i i
#why?
#ledit line_chunks end-2 end-3 $pad
#Note that 'ledit line_chunks end-2 -1 $pad' is not equivalent,
#because linsert behaves differently depending on whether the index is start-relative or end-relative.
#(breaks layout e.g subtables in: i i)
} else {
lappend line_chunks $pad
}
@ -5487,6 +5491,9 @@ tcl::namespace::eval textblock {
r-2 {
if {[lindex $line_chunks end] eq ""} {
set line_chunks [linsert $line_chunks end-2 $pad]
#(ledit line_chunks end-2 -1 $pad) is not equivalent to linsert
#because of the different behaviour of end-relative vs start-relative indices with linsert
#- it can break layout e.g subtables in: i i
} else {
lappend line_chunks $pad
}

405
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/overtype-1.7.4.tm

@ -90,7 +90,9 @@ package require punk::assertion
# - need to extract and replace ansi codes?
tcl::namespace::eval overtype {
namespace import ::punk::assertion::assert
if {[info commands ::overtype::assert] eq ""} {
namespace import ::punk::assertion::assert
}
punk::assertion::active true
namespace path ::punk::lib
@ -625,7 +627,7 @@ tcl::namespace::eval overtype {
#set overtext [lpop inputchunks 0] ;#could be a list 'ansisplit' or text 'plain|mixed'
lassign [lpop inputchunks 0] overtext_type overtext
#use eq test with emptystring instead of 'string length' - test for emptiness shouldn't cause shimmering if popped inputchunks member if an 'ansisplit' list
#use eq test with emptystring instead of 'string length' - test for emptiness shouldn't cause shimmering if popped inputchunks member is an 'ansisplit' list
if {$overtext eq ""} {
incr loop
continue
@ -728,7 +730,7 @@ tcl::namespace::eval overtype {
set existing_reverse_state 0
#split_codes_single is single esc sequence - but could have multiple sgr codes within one esc sequence
#e.g \x1b\[0;31;7m has a reset,colour red and reverse
set codeinfo [punk::ansi::codetype::sgr_merge [list $replay_codes_overlay] -info 1]
set codeinfo [punk::ansi::codetype::sgr_merge [list $replay_codes_overlay] -info]
set codestate_reverse [dict get $codeinfo codestate reverse]
switch -- $codestate_reverse {
7 {
@ -863,7 +865,7 @@ tcl::namespace::eval overtype {
# ----
# review
set col $post_render_col
#just because it's out of range of the renderwidth - doesn't mean a move down should jump to witin the range - 2025
#just because it's out of range of the renderwidth - doesn't mean a move down should jump to within the range - 2025
#----
#set existingdata [lindex $outputlines [expr {$post_render_row -1}]]
@ -908,7 +910,7 @@ tcl::namespace::eval overtype {
#It would perhaps be more properly handled as a queue of instructions from our initial renderline call
#we don't need to worry about overflow next call (?)- but we should carry forward our gx and ansi stacks
puts stdout ">>>[a+ red bold]overflow_right during restore_cursor[a]"
puts stdout ">>>renderspace<<<[a+ red bold]overflow_right during restore_cursor[a]"
set sub_info [overtype::renderline\
-info 1\
@ -924,7 +926,7 @@ tcl::namespace::eval overtype {
tcl::dict::set vtstate autowrap_mode [tcl::dict::get $sub_info autowrap_mode] ;#nor this..
#todo!!!
# 2025 fix - this does nothing - so what uses it?? create a test!
# 2025 fix - this does nothing - so what is the intention?? create a test!
linsert outputlines $renderedrow $foldline
#review - row & col set by restore - but not if there was no save..
}
@ -1053,7 +1055,9 @@ tcl::namespace::eval overtype {
set overflow_right ""
} else {
if {[tcl::dict::get $vtstate autowrap_mode]} {
set outputlines [linsert $outputlines $renderedrow $overflow_right]
#set outputlines [linsert $outputlines $renderedrow $overflow_right]
#ledit outputlines $renderedrow $renderedrow-1 $overflow_right
ledit outputlines $renderedrow -1 $overflow_right
set overflow_right ""
set row [expr {$renderedrow + 2}]
} else {
@ -1150,7 +1154,8 @@ tcl::namespace::eval overtype {
if {$insert_lines_above > 0} {
set row $renderedrow
#set outputlines [linsert $outputlines $renderedrow-1 {*}[lrepeat $insert_lines_above ""]]
ledit outputlines $renderedrow-1 $renderedrow-2 {*}[lrepeat $insert_lines_above ""]
#ledit outputlines $renderedrow-1 $renderedrow-2 {*}[lrepeat $insert_lines_above ""]
ledit outputlines $renderedrow-1 -1 {*}[lrepeat $insert_lines_above ""]
incr row [expr {$insert_lines_above -1}] ;#we should end up on the same line of text (at a different index), with new empties inserted above
#? set row $post_render_row #can renderline tell us?
}
@ -1461,6 +1466,7 @@ tcl::namespace::eval overtype {
set nextprefix_list $overflow_right_pt_code_pt
} else {
#merge tail and head
#ledit <list> end end <val> will work with empty list (ledit <list> end <val> does not)
ledit nextprefix_list end end "[lindex $nextprefix_list end][lindex $overflow_right_pt_code_pt 0]"
lappend nextprefix_list {*}[lrange $overflow_right_pt_code_pt 1 end]
}
@ -1476,16 +1482,17 @@ tcl::namespace::eval overtype {
}
if 0 {
if {$nextprefix ne ""} {
set nextoveridx [expr {$overidx+1}]
if {$nextoveridx >= [llength $inputchunks]} {
lappend inputchunks $nextprefix
} else {
#lset overlines $nextoveridx $nextprefix[lindex $overlines $nextoveridx]
set inputchunks [linsert $inputchunks $nextoveridx $nextprefix]
if {$nextprefix ne ""} {
set nextoveridx [expr {$overidx+1}]
if {$nextoveridx >= [llength $inputchunks]} {
lappend inputchunks $nextprefix
} else {
#lset overlines $nextoveridx $nextprefix[lindex $overlines $nextoveridx]
#set inputchunks [linsert $inputchunks $nextoveridx $nextprefix]
ledit inputchunks $nextoveridx -1 $nextprefix
}
}
}
}
if {[llength $nextprefix_list]} {
#set inputchunks [linsert $inputchunks 0 $nextprefix]
@ -1669,13 +1676,17 @@ tcl::namespace::eval overtype {
}
}
}
lappend outputlines $rendered
#JULZ
#lappend outputlines $rendered
lappend outputlines $rendered\x1b\[m
#lappend outputlines [renderline -insert_mode 0 -transparent $opt_transparent $undertext $overtext]
} else {
#background block is wider than or equal to data for this line
#lappend outputlines [renderline -insert_mode 0 -startcolumn [expr {$left_exposed + 1}] -transparent $opt_transparent -exposed1 $opt_exposed1 -exposed2 $opt_exposed2 $undertext $overtext]
set rinfo [renderline -info 1 -insert_mode 0 -startcolumn [expr {$left_exposed + 1}] -transparent $opt_transparent -exposed1 $opt_exposed1 -exposed2 $opt_exposed2 $undertext $overtext]
lappend outputlines [tcl::dict::get $rinfo result]
#JULZ
#lappend outputlines [tcl::dict::get $rinfo result]
lappend outputlines [tcl::dict::get $rinfo result]\x1b\[m
}
set replay_codes_underlay [tcl::dict::get $rinfo replay_codes_underlay]
set replay_codes_overlay [tcl::dict::get $rinfo replay_codes_overlay]
@ -1787,6 +1798,9 @@ tcl::namespace::eval overtype {
set overflowlength [expr {$overtext_datalen - $renderwidth}]
if {$overflowlength > 0} {
#raw overtext wider than undertext column
#broken:
#todo - renderline -overflow is invalid.
# we need renderline to support -expand_left ??
set rinfo [renderline\
-info 1\
-insert_mode 0\
@ -1814,13 +1828,18 @@ tcl::namespace::eval overtype {
}
}
}
lappend outputlines $rendered
#JULZ
#lappend outputlines $rendered
lappend outputlines $rendered\x1b\[m
} else {
#padded overtext
#lappend outputlines [renderline -insert_mode 0 -transparent $opt_transparent -startcolumn [expr {$left_exposed + 1}] $undertext $overtext]
#Note - we still need overflow(exapnd_right) here - as although the overtext is short - it may oveflow due to the startoffset
set rinfo [renderline -info 1 -insert_mode 0 -transparent $opt_transparent -expand_right $opt_overflow -startcolumn [expr {$left_exposed + 1 + $startoffset}] $undertext $overtext]
lappend outputlines [tcl::dict::get $rinfo result]
#JULZ
#lappend outputlines [tcl::dict::get $rinfo result]
lappend outputlines [tcl::dict::get $rinfo result]\x1b\[m
}
set replay_codes [tcl::dict::get $rinfo replay_codes]
set replay_codes_underlay [tcl::dict::get $rinfo replay_codes_underlay]
@ -2014,7 +2033,8 @@ tcl::namespace::eval overtype {
# }
#}
}
lappend outputlines $rendered
#JULZ
lappend outputlines $rendered\x1b\[m
} else {
#padded overtext
#lappend outputlines [renderline -insert_mode 0 -transparent $opt_transparent -startcolumn [expr {$left_exposed + 1}] $undertext $overtext]
@ -2023,7 +2043,9 @@ tcl::namespace::eval overtype {
#puts stderr "--> [ansistring VIEW -lf 1 -nul 1 $rinfo] <--"
set overflow_right [tcl::dict::get $rinfo overflow_right]
set unapplied [tcl::dict::get $rinfo unapplied]
lappend outputlines [tcl::dict::get $rinfo result]
#JULZ
#lappend outputlines [tcl::dict::get $rinfo result]
lappend outputlines [tcl::dict::get $rinfo result]\x1b\[m
}
set replay_codes [tcl::dict::get $rinfo replay_codes]
set replay_codes_underlay [tcl::dict::get $rinfo replay_codes_underlay]
@ -2136,6 +2158,24 @@ tcl::namespace::eval overtype {
}]
}
proc stack_eq {a b} {
#single level list equality test to avoid generating internal string representations of the lists unnecessarily.
if {[llength $a] != [llength $b]} {
return 0
}
foreach code1 $a code2 $b {
if {$code1 ne $code2} {
return 0
}
}
return 1
}
#todo: tests
#set j [overtype::renderline -transparent " " -insert_mode 0 -expand_right 1 "[a+ red underline]xxx[a+ blue][a+ nounderline]" "[a green]J" ]yyy
# yyy should be blue with no underline - and the J should be green - and the x's should be red with underline and the J should overwrite the first x
#At the moment we return a reset at the end of the renderline result instead of the replay codes.
proc renderline {args} {
#todo - fix 'unapplied' mechanism.This is particularly inefficient for long lines, or data such as binarytext which is not line-based.
#All unapplied data is re-split/reprocessed repeatedly for each line! This is very wasteful and slow.
@ -2476,7 +2516,9 @@ tcl::namespace::eval overtype {
if {$maybemouse ne "<" && [tcl::string::index $code end] eq "m"} {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set u_codestack [list "\x1b\[m"]
#will normalize all resets to the same code - including 8bit reset.
#set u_codestack [list "\x1b\[m"]
set u_codestack [list $code]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set u_codestack [list $code]
} else {
@ -2557,6 +2599,17 @@ tcl::namespace::eval overtype {
}
}
#----------------------------------------
#set test_c [showlist $undercols]
##set test_s [showlist $understacks %ansiview]
#set sview [list]
#foreach us $understacks {
# lappend sview [ansistring VIEW $us]
#}
#set test_s [showlist $sview]
#puts stderr "undercols/stacks:\n[textblock::join -- $test_c " " $test_s]"
#----------------------------------------
if {$opt_width ne "\uFFEF"} {
set renderwidth $opt_width
} else {
@ -2567,7 +2620,10 @@ tcl::namespace::eval overtype {
#trailing codes in effect for underlay
if {[llength $u_codestack]} {
#set replay_codes_underlay [join $u_codestack ""]
set replay_codes_underlay [punk::ansi::codetype::sgr_merge_list {*}$u_codestack]
#set replay_codes_underlay [punk::ansi::codetype::sgr_merge_list {*}$u_codestack]
#u_codestack was built from codes split using split_codes_single
#- so should already be simplified to single codes with no multiple SGR params in one code
set replay_codes_underlay [punk::ansi::codetype::sgr_merge_singles $u_codestack]
} else {
set replay_codes_underlay ""
}
@ -2767,13 +2823,17 @@ tcl::namespace::eval overtype {
} else {
lappend overlay_grapheme_control_stacks $o_codestack
#there will always be an empty code at end due to foreach on 2 vars with odd-sized list ending with pt (overmap coming from perlish split)
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set o_codestack [list "\x1b\[m"] ;#reset better than empty list - fixes some ansi art issues
set code_endswith_m [expr {[tcl::string::index $code end] eq "m"}] ;#skip SGR regexp testing for cases that don't end with m - as they can't be SGR
if {$code_endswith_m && [punk::ansi::codetype::is_sgr_reset $code]} {
#reset better than empty list - fixes some ansi art issues
#set o_codestack [list "\x1b\[m"]
set o_codestack [list $code]
lappend overlay_grapheme_control_list [list sgr $code]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
} elseif {$code_endswith_m && [punk::ansi::codetype::has_sgr_leadingreset $code]} {
set o_codestack [list $code]
lappend overlay_grapheme_control_list [list sgr $code]
} elseif {[priv::is_sgr $code]} {
} elseif {$code_endswith_m && [priv::is_sgr $code]} {
#basic simplification first - remove straight dupes
set dup_posns [lsearch -all -exact $o_codestack $code] ;#must be -exact because of square-bracket glob chars
set o_codestack [lremove $o_codestack {*}$dup_posns]
@ -2827,7 +2887,12 @@ tcl::namespace::eval overtype {
lappend overstacks_gx $o_gxstack
#set replay_codes_overlay [join $o_codestack ""]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}$o_codestack]
if {[llength $o_codestack]} {
#set replay_codes_overlay [join $o_codestack ""]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_singles $o_codestack]
} else {
set replay_codes_overlay [list]
}
#if {[tcl::dict::exists $overstacks $max_overlay_grapheme_index]} {
# set replay_codes_overlay [join [tcl::dict::get $overstacks $max_overlay_grapheme_index] ""]
@ -2952,7 +3017,7 @@ tcl::namespace::eval overtype {
#specials - each shoud have it's own test of what to do if it happens after overflow_idx reached
switch -- $chtest {
"<lf>" {
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
if {$idx == 0} {
#puts "---a <lf> at col 1"
#linefeed at column 1
@ -3069,8 +3134,7 @@ tcl::namespace::eval overtype {
set next_gc [lindex $overlay_grapheme_control_list $gci+1] ;#next grapheme or control
lassign $next_gc next_type next_item
if {$autowrap_mode} {
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci-1]]
#set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci-1]]
#don't incr idx beyond the overflow_idx
#idx_over already incremented - decrement so current overlay grapheme stacks go to unapplied
incr idx_over -1
@ -3087,7 +3151,7 @@ tcl::namespace::eval overtype {
#no point throwing back to caller for each grapheme that is overflowing
#without this branch - renderline would be called with overtext reducing only by one grapheme per call
#processing a potentially long overtext each time (ie - very slow)
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#JMN4
}
@ -3427,7 +3491,7 @@ tcl::namespace::eval overtype {
switch -exact -- $code_end {
A {
#Row move - up
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#todo
lassign [split $param {;}] num modifierkey
if {$modifierkey ne ""} {
@ -3452,7 +3516,7 @@ tcl::namespace::eval overtype {
#CUD - Cursor Down
#Row move - down
lassign [split $param {;}] num modifierkey
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#move down
if {$modifierkey ne ""} {
puts stderr "modifierkey:$modifierkey"
@ -3503,7 +3567,7 @@ tcl::namespace::eval overtype {
incr cursor_column $num
} else {
if {$autowrap_mode} {
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#jmn
if {$idx == $overflow_idx} {
incr num
@ -3598,7 +3662,7 @@ tcl::namespace::eval overtype {
set cursor_column 1
set idx 0
} else {
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
incr cursor_column -$num
priv::render_to_unapplied $overlay_grapheme_control_list $gci
set instruction wrapmovebackward
@ -3626,7 +3690,9 @@ tcl::namespace::eval overtype {
set cursor_column 1
set cursor_row [expr {$cursor_row + $downmove}]
set idx [expr {$cursor_column -1}]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
#sgr_merge_list
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#sgr_merge_singles ??
incr idx_over
priv::render_to_unapplied $overlay_grapheme_control_list $gci
set instruction move
@ -3647,7 +3713,7 @@ tcl::namespace::eval overtype {
set cursor_row 1
}
set idx [expr {$cursor_column - 1}]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
incr idx_over
priv::render_to_unapplied $overlay_grapheme_control_list $gci
set instruction move
@ -3656,6 +3722,7 @@ tcl::namespace::eval overtype {
}
G {
#CHA - Cursor Horizontal Absolute (move to absolute column no)
#see also HPA - Horizontal Position Absolute (same functionality)
if {$param eq ""} {
set targetcol 1
} else {
@ -3680,6 +3747,29 @@ tcl::namespace::eval overtype {
set cursor_column $targetcol
#puts stderr "renderline absolute col move ESC G (TEST)"
}
` {
#https://vt100.net/docs/vt510-rm/HPA.html
#docs don't mention that it defaults to one if $parm omitted - but it seems to do in practice
if {$param eq ""} {
set targetcol 1
} else {
set targetcol $param
if {![string is integer -strict $targetcol]} {
puts stderr "renderline HPA (Horizontal Position Absolute) error. Unrecognised parameter '$param'"
}
set targetcol [expr {$param}]
set max [llength $outcols]
if {$overflow_idx == -1} {
incr max
}
if {$targetcol > $max} {
puts stderr "renderline HPA (Horizontal Position Absolute) error. Param '$param' > max: $max"
set targetcol $max
}
}
set idx [expr {($targetcol -1) + $opt_colstart -1}]
set cursor_column $targetcol
}
H - f {
#CSI n;m H - CUP - Cursor Position
@ -3727,7 +3817,7 @@ tcl::namespace::eval overtype {
set cursor_row $target_row
set cursor_column $target_column
set idx [expr {$cursor_column -1}]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
incr idx_over
priv::render_to_unapplied $overlay_grapheme_control_list $gci
set instruction move
@ -3758,7 +3848,7 @@ tcl::namespace::eval overtype {
set cursor_row 1
set cursor_column 1
set idx [expr {$cursor_column -1}]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
incr idx_over
if {[llength $outcols]} {
priv::render_erasechar 0 [llength $outcols]
@ -4000,7 +4090,8 @@ tcl::namespace::eval overtype {
}
}
#append cursor_saved_attributes [join $sgr_stack ""]
append cursor_saved_attributes [punk::ansi::codetype::sgr_merge_list {*}$sgr_stack]
#append cursor_saved_attributes [punk::ansi::codetype::sgr_merge_list {*}$sgr_stack]
append cursor_saved_attributes [punk::ansi::codetype::sgr_merge $sgr_stack]
#as there is apparently only one cursor storage element we don't need to throw back to the calling loop for a save.
@ -4024,7 +4115,7 @@ tcl::namespace::eval overtype {
# set replay_codes_overlay $cursor_saved_attributes ;#empty - or last save if it happend in this input chunk
#} else {
#jj
#set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
#set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay ""
#}
@ -4398,7 +4489,7 @@ tcl::namespace::eval overtype {
#vt102-docs: "Moves cursor up one line in same column. If cursor is at top margin, screen performs a scroll-down"
puts stderr "overtype::renderline ESC M not fully implemented"
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#move up
incr cursor_row -1
if {$cursor_row < 1} {
@ -4743,6 +4834,9 @@ tcl::namespace::eval overtype {
#puts stderr "first_tail_null_posn: $first_tail_null_posn"
#puts stderr "colview: [ansistring VIEW $outcols]"
#NOTE understacks has been updated with data from the overlay - so it should reflect the final state of the stacks for each grapheme in outcols
foreach ch $outcols {
#puts "---- [ansistring VIEW $ch]"
@ -4766,15 +4860,58 @@ tcl::namespace::eval overtype {
if {$i < [llength $understacks]} {
#set cstack [tcl::dict::get $understacks $i]
set cstack [lindex $understacks $i]
if {$cstack ne $prevstack} {
if {[llength $prevstack] && ![llength $cstack]} {
#This reset is important e.g testfile fruit.ans - we get overhang on rhs without it. But why is cstack empty?
append sgrleader \033\[m
#use stack_eq for depth 1 comparison without generating string rep.
if {![stack_eq $cstack $prevstack]} {
#possible SGR attribute change.
if {[llength $prevstack]} {
if {![llength $cstack]} {
#why is cstack empty?
#a) no ansi in underlay and we are at a position 2 after an overlay insertion.
# (position 1 after overlay insertion should already have had a reset inserted)
#b) no ansi in overlay and we are at an overlay insertion point.
#--------------
#review
#todo? consider testing next-char's understack when applying each overlay char in the main loop.
#if empty or has no leading reset - we need to add a leading reset at that point.
#--------------
#--------
#following statement is FALSE - (historical info). Doesn't seem to apply.
#This reset is important e.g testfile fruit.ans - we get overhang on rhs without it.
#append sgrleader \033\[m
#--------
##test
#set view_prev ""
#foreach ps $prevstack {
# append view_prev [ansistring VIEW -lf 1 -vt 1 -nul 1 $ps]
#}
#puts stderr "col $i, ch: $ch - cstack empty vs prevstack $view_prev"
} else {
#without this we get extra redundant codes in some places.
#e.g a continuous string of underlay that originally had \x1b\[31m red text,
#but then when an overlay char is inserted near the start, the following underlay char (insertion index +1) codestack had a reset added.
#All subsequent underlay chars in the same run of plaintext don't have the reset and so appear 'different' but are actually part of the same run.
#check if actually different. ie if current stack actually changes anything from previous stack when merged together.
set prevmerge [punk::ansi::codetype::sgr_merge $prevstack]
set currmerge [punk::ansi::codetype::sgr_merge $cstack]
set together [punk::ansi::codetype::sgr_merge [list $prevmerge $currmerge]]
if {$together ne $prevmerge} {
#stacks are different enough that we need to output something
#if {{[punk::ansi::codetype::has_sgr_leading_reset $currmerge]}} {
#}
append sgrleader $currmerge
}
}
} else {
append sgrleader [punk::ansi::codetype::sgr_merge_list {*}$cstack]
if {[llength $cstack]} {
append sgrleader [punk::ansi::codetype::sgr_merge $cstack]
}
}
set prevstack $cstack
}
set prevstack $cstack
} else {
set prevstack [list]
}
@ -4797,7 +4934,8 @@ tcl::namespace::eval overtype {
#if {[llength $prevstack] && ![llength $cstack]} {
# append sgrleader \033\[m
#}
append sgrleader [punk::ansi::codetype::sgr_merge_list {*}$cstack]
#append sgrleader [punk::ansi::codetype::sgr_merge_list {*}$cstack]
append sgrleader [punk::ansi::codetype::sgr_merge $cstack]
append overflow_right $sgrleader
append overflow_right $ch
} else {
@ -4853,14 +4991,50 @@ tcl::namespace::eval overtype {
set replay_codes ""
if {[llength $understacks] > 0} {
if {$overflow_idx == -1} {
#set tail_idx [tcl::dict::size $understacks]
set tail_idx [llength $understacks]
} else {
set tail_idx [llength $undercols]
}
if {$tail_idx-1 < [llength $understacks]} {
if {$tail_idx == [llength $undercols]} {
#we got to the end of the original underlay
#- so we want the full stack at the end of the original underlay ie including trailing codes which are not associated with any grapheme in the underlay
#but would be in effect for any text after the underlay.
#---------------------
#REVIEW - determine if last col was overwritten by overlay?
#how best to determine if last underlay column was overwritten by overlay?
#we could track in the main loop whether each underlay column was overwritten by overlay
#This seems like the best mechanism, because the overlay ANSI can include movement codes, so the underlay can be overwritten in any order.
#We should consider that just because the last grapheme was overwritten, that doesn't necessarily mean we should disregard the trailing codes
#perhaps trailing underlay codes are never overwritten unless the overlay extends beyond the end of the underlay - in which case we can just check if overlay extends beyond end of underlay to determine whether to include trailing underlay codes in replay or not.
#if overlay extends beyond end of underlay - we use the overlay stack at the end of the underlay as the replay codes, which won't include any trailing underlay codes.
#---------------------
if {[lindex $undermap end] eq ""} {
#there were trailing codes in the underlay with no grapheme - we want to include those in the replay as they would affect any text after the underlay
#we need to backtrack from the end of the underlay to find the last grapheme with codes, and merge those codes with any trailing codes in the underlay with no grapheme
set tailcodes [list] ;#build in reverse order.
foreach {pt code} [lreverse $undermap] {
if {$pt ne ""} {
break
}
lappend tailcodes $code
}
set tailcodes [lreverse $tailcodes]
#set tailcodes [lindex $undermap end-1]
set laststack [lindex $understacks $tail_idx-1]
lappend laststack {*}$tailcodes
set replay_codes [punk::ansi::codetype::sgr_merge $laststack] ;#stack at end of underlay including trailing codes
} else {
#last part of underlay was plain text with no trailing codes - we can just use the stack at the last grapheme of the underlay
set replay_codes [punk::ansi::codetype::sgr_merge [lindex $understacks $tail_idx-1]] ;#stack at end of underlay
}
} elseif {$tail_idx-1 < [llength $understacks]} {
#set replay_codes [join [lindex $understacks $tail_idx-1] ""] ;#tail replay codes
set replay_codes [punk::ansi::codetype::sgr_merge_list {*}[lindex $understacks $tail_idx-1]] ;#tail replay codes
#set replay_codes [punk::ansi::codetype::sgr_merge_list {*}[lindex $understacks $tail_idx-1]] ;#tail replay codes
set replay_codes [punk::ansi::codetype::sgr_merge [lindex $understacks $tail_idx-1]] ;#tail replay codes
}
if {$tail_idx-1 < [llength $understacks_gx]} {
set gx0 [lindex $understacks_gx $tail_idx-1]
@ -4876,10 +5050,33 @@ tcl::namespace::eval overtype {
#pdict $understacks
if {[punk::ansi::ta::detect_sgr $outstring]} {
append outstring [punk::ansi::a] ;#without this - we would get for example, trailing backgrounds after rightmost column
#JULZ
#The caller is responsible for adding a reset at the end of returned lines depending on how they want to use it - so we don't add one here.
#<deprecated>
#append outstring [punk::ansi::a] ;#without this - we would get for example, trailing backgrounds after rightmost column
#</deprecated>
#we only want to append the replay codes if they are different to those already in effect at the end of the rendered line.
if {$overflow_idx == -1} {
set tail_idx [llength $understacks]
} else {
set tail_idx [llength $undercols]
}
set laststack [lindex $understacks $tail_idx-1]
set laststackmerge [punk::ansi::codetype::sgr_merge $laststack]
if {$replay_codes ne $laststackmerge} {
append outstring $replay_codes
}
#review
#close off any open gx?
#probably should - and overflow_right reopen?
#probably not, this is akin to adding a reset to close off open SGR codes, which we specifically don't do.
#caller will need to close off any open gx at the end of the line if they want to, and provide appropriate replay codes for the next line if they want to maintain gx state across lines.
#we just need to make sure we provide all necessary info in the result dictionary.
#todo - tests and examples.
#and overflow_right reopen?
}
if {$opt_returnextra} {
@ -4902,29 +5099,29 @@ tcl::namespace::eval overtype {
set result [tcl::dict::create\
result $outstring\
visualwidth [punk::ansi::printing_length $outstring]\
instruction $instruction\
stringlen [string length $outstring]\
overflow_right_column $overflow_right_column\
overflow_right $overflow_right\
unapplied $unapplied\
unapplied_list $unapplied_list\
unapplied_ansisplit $unapplied_ansisplit\
insert_mode $insert_mode\
autowrap_mode $autowrap_mode\
crm_mode $crm_mode\
reverse_mode $reverse_mode\
insert_lines_above $insert_lines_above\
insert_lines_below $insert_lines_below\
cursor_saved_position $cursor_saved_position\
visualwidth [punk::ansi::printing_length $outstring]\
instruction $instruction\
stringlen [string length $outstring]\
overflow_right_column $overflow_right_column\
overflow_right $overflow_right\
unapplied $unapplied\
unapplied_list $unapplied_list\
unapplied_ansisplit $unapplied_ansisplit\
insert_mode $insert_mode\
autowrap_mode $autowrap_mode\
crm_mode $crm_mode\
reverse_mode $reverse_mode\
insert_lines_above $insert_lines_above\
insert_lines_below $insert_lines_below\
cursor_saved_position $cursor_saved_position\
cursor_saved_attributes $cursor_saved_attributes\
cursor_column $cursor_column\
cursor_row $cursor_row\
expand_right $opt_expand_right\
replay_codes $replay_codes\
replay_codes_underlay $replay_codes_underlay\
replay_codes_overlay $replay_codes_overlay\
pm_list $pm_list\
cursor_column $cursor_column\
cursor_row $cursor_row\
expand_right $opt_expand_right\
replay_codes $replay_codes\
replay_codes_underlay $replay_codes_underlay\
replay_codes_overlay $replay_codes_overlay\
pm_list $pm_list\
]
if {$opt_returnextra == 1} {
#puts stderr "renderline: $result"
@ -5073,6 +5270,11 @@ tcl::namespace::eval overtype::priv {
#caching the answer saves some regex expense - possibly a few uS to lookup vs under 1uS
#todo - test if still worthwhile after a large cache is built up. (limit cache size?)
proc is_sgr {code} {
set code_endswith_m [expr {[tcl::string::index $code end] eq "m"}] ;#skip SGR regexp testing for cases that don't end with m - as they can't be SGR
if {!$code_endswith_m} {
#don't even cache.
return 0
}
variable cache_is_sgr
if {[tcl::dict::exists $cache_is_sgr $code]} {
return [tcl::dict::get $cache_is_sgr $code]
@ -5081,6 +5283,7 @@ tcl::namespace::eval overtype::priv {
tcl::dict::set cache_is_sgr $code $answer
return $answer
}
proc render_to_unapplied {overlay_grapheme_control_list gci} {
upvar idx_over idx_over
@ -5104,7 +5307,8 @@ tcl::namespace::eval overtype::priv {
set unapplied_ansisplit [list ""]
#append unapplied [join [lindex $overstacks $idx_over] ""]
#append unapplied [punk::ansi::codetype::sgr_merge_list {*}[lindex $overstacks $idx_over]]
set sgr_merged [punk::ansi::codetype::sgr_merge_list {*}[lindex $og_stacks $gci]]
#set sgr_merged [punk::ansi::codetype::sgr_merge_list {*}[lindex $og_stacks $gci]]
set sgr_merged [punk::ansi::codetype::sgr_merge [lindex $og_stacks $gci]]
if {$sgr_merged ne ""} {
lappend unapplied_list $sgr_merged
lappend unapplied_ansisplit $sgr_merged ""
@ -5167,7 +5371,8 @@ tcl::namespace::eval overtype::priv {
set unapplied_list [list]
set unapplied_ansisplit [list ""] ;#remove empty entry at end if nothing added
set sgr_merged [punk::ansi::codetype::sgr_merge_list {*}[lindex $og_stacks $gci]]
#set sgr_merged [punk::ansi::codetype::sgr_merge_list {*}[lindex $og_stacks $gci]]
set sgr_merged [punk::ansi::codetype::sgr_merge [lindex $og_stacks $gci]]
if {$sgr_merged ne ""} {
lappend unapplied_list $sgr_merged
lappend unapplied_ansisplit $sgr_merged ""
@ -5217,9 +5422,13 @@ tcl::namespace::eval overtype::priv {
upvar understacks_gx gxstacks
set nxt [llength $o]
if {$i < $nxt} {
set o [lreplace $o $i $i]
set ustacks [lreplace $ustacks $i $i]
set gxstacks [lreplace $gxstacks $i $i]
#set o [lreplace $o $i $i]
ledit o $i $i
#set ustacks [lreplace $ustacks $i $i]
ledit ustacks $i $i
#review - do we need to ensure that stack at new $i has a reset code at the start?
#set gxstacks [lreplace $gxstacks $i $i]
ledit gxstacks $i $i
} elseif {$i == 0 || $i == $nxt} {
#nothing to do
} else {
@ -5329,6 +5538,27 @@ tcl::namespace::eval overtype::priv {
}
if {$i < [llength $ustacks]} {
lset ustacks $i $sgrstack
#check if next ustacks entry has a reset.
#It will need one if it doesn't already have one because our inserted char should not affect the pre-existing ansi state of the underlay.
#we have just replaced an entry into the ustacks at position i but we are still at the same position - so the next entry is still at position i+1
if {[llength $sgrstack] && $i+1 < [llength $ustacks]} {
set next_ustack [lindex $ustacks $i+1]
#could be a reset or just empty - either way we need to add a reset if it's not already there
#(empty if underlay had no ansi)
#temporarily emit something to stderr
if {![llength $next_ustack]} {
#puts -nonewline stderr " next_ustack (empty) at position [expr {$i+1}] after replacing position $i with '$c' and sgrstack '[join $sgrstack ""]'\n"
lset ustacks $i+1 [list "\x1b\[m"]
} else {
#review - next_ustack is a list - has_sgr_leadingreset will not work as expected if called on whole next_ustack as a list.
#As the stack will need merging anyway - we can just prepend a reset without checking.
#REVIEW.
#puts -nonewline stderr "check next_ustack '$next_ustack' for reset at position [expr {$i+1}] after replacing position $i with '$c' and sgrstack '[join $sgrstack ""]'\n"
#set next_ustack [linsert $next_ustack 0 [a+ reset]]
ledit next_ustack -1 -1 "\x1b\[m"
lset ustacks $i+1 $next_ustack
}
}
lset gxstacks $i $gx0stack
} else {
lappend ustacks $sgrstack
@ -5339,7 +5569,8 @@ tcl::namespace::eval overtype::priv {
if {$i < $nxt} {
#set o [linsert $o $i $c]
#JMN insert via ledit
ledit o $i $i-1 $c
#ledit o $i $i-1 $c
ledit o $i -1 $c
} else {
lappend o $c
}
@ -5347,8 +5578,10 @@ tcl::namespace::eval overtype::priv {
#set ustacks [linsert $ustacks $i $sgrstack]
#set gxstacks [linsert $gxstacks $i $gx0stack]
#insert via ledit
ledit ustacks $i $i-1 $sgrstack
ledit gxstacks $i $i-1 $gx0stack
#ledit ustacks $i $i-1 $sgrstack
ledit ustacks $i -1 $sgrstack
#ledit gxstacks $i $i-1 $gx0stack
ledit gxstacks $i -1 $gx0stack
} else {
lappend ustacks $sgrstack
lappend gxstacks $gx0stack

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

@ -1743,7 +1743,7 @@ namespace eval punk {
append script \n {set assigned [ansistring length $leveldata]}
set level_script_complete 1
}
%str {
%str - %string {
set active_key_type "string"
if {$get_not} {
error "!%str - not string-get is not supported"
@ -1752,6 +1752,9 @@ namespace eval punk {
append script \n {# set active_key_type "" index_operation: string-get}
append script \n {set assigned $leveldata}
set level_script_complete 1
#todo - %lpad- %lpadstr- %join- etc as in punk::lib::showdict
#review - merge code shared with showdict for these operations
}
%sp {
#experimental
@ -1822,6 +1825,8 @@ namespace eval punk {
set level_script_complete 1
}
%ansiview {
#review - implemented differently in showdict.
#(showdict uses ansistring VIEW -lf 1 <str>)
set active_key_type "string"
if {$get_not} {
error "!%# not string-ansiview is not supported"
@ -2446,7 +2451,41 @@ namespace eval punk {
set index <idx>
}]
}
%split-* {
#split on one or more chars - review
#set hidekey 1
#lassign [split $key -] _ splitchars
#set thisval [split $dval $splitchars]
set active_key_type "string"
set splitchars [string range $index 7 end]
append script \n [string map [list <splitchars> $splitchars] {
# set active_key_type "string" index_operation: split-string
#e.g supports %split-"\\n"= "l1\n\nl3" -> {l1 "" l3}
set splitchars "<splitchars>"
set assigned [split $leveldata $splitchars]
}]
set level_script_complete 1
#todo %splitat- %splitn- ??
}
%lpad-* {
#moved from punk::lib::showdict patterns.
#set hidekey 1
#lassign [split $key -] _ extra
#set width [expr {[textblock::width $dval] + $extra}]
#set thisval [textblock::pad $dval -which left -width $width]
set active_key_type "string"
set extra [string range $index 6 end]
append script \n [string map [list <extra> $extra] {
# set active_key_type "string" index_operation: lpad-string
set extra "<extra>"
set width [expr {[textblock::width $leveldata] + $extra}]
set assigned [textblock::pad $leveldata -which left -width $width]
}]
set level_script_complete 1
}
%* {
#see above re %lpad- etc and synchronizing with showdict
set active_key_type "string"
set do_bounds_check 0
set index [string range $index 1 end]
@ -2827,11 +2866,21 @@ namespace eval punk {
} else {
if {$is_range} {
lappend INDEX_OPERATIONS list-range
#todo - if we know it's a contiguous range, we could use lrange here instead of lindex
#we would also need to detect if it's a reverse range such as @5..1 and handle that correctly
#- lrange doesn't support reverse ranges, but we could resolve the indexset to a list of indices
#and then use lindex with that list of indices to get the correct result.
#we don't always know at this point if the range is in reverse or not because we don't know the size of the list until
#runtime - so we will handle both cases in the same way for now.
#e.g for index 5..end-6 - this could be forward or reverse depending on the length of the list.
set assign_script {
set assigned [lmap i [punk::lib::indexset_resolve [llength $leveldata] <idx>] {lindex $leveldata $i}]
}
} else {
lappend INDEX_OPERATIONS listindex
}
set assign_script {
set assigned [lmap i [punk::lib::indexset_resolve [llength $leveldata] <idx>] {lindex $leveldata $i}]
set assign_script {
set assigned [lindex $leveldata [punk::lib::indexset_resolve [llength $leveldata] <idx>]]
}
}
}
@ -2881,6 +2930,8 @@ namespace eval punk {
}
set script [string map [list <idx> $index] $script]
} elseif {[string first "end" $index] >=0} {
#review - obsoleted by indexset syntax. prune branch?
puts stderr "index with end detected - review if this branch still reachable - prune? $index"
if {[regexp {^end([-+]{1,2}[0-9]+)$} $index _match endspec]} {
if {$get_not} {
@ -2923,6 +2974,8 @@ namespace eval punk {
}
} elseif {[regexp {^([0-9]+|end|end[-+]{1,2}[0-9]+)-([0-9]+|end|end[-+]{1,2}([0-9]+))$} $index _ start end]} {
#review - obsoleted by indexset syntax. prune branch?
puts stderr "index with range and end detected - review if this branch still reachable - prune? $index"
if {$get_not} {
lappend INDEX_OPERATIONS list-range-not
set assign_script [string map [list <s> $start <e> $end ] {
@ -3012,6 +3065,10 @@ namespace eval punk {
error $listmsg "destructure $selector" [list pipesyntax destructure selector $selector]
}
} elseif {[string first - $index] > 0} {
puts stderr "index with - detected - review if this branch still reachable - prune? $index"
#review - we changed to detect indexset above.
#syntax @m-n should be deprecated in favour of @m..n
#todo - check if this branch still reachable - prune?
#e.g @1-3 gets here
#JMN
if {$get_not} {
@ -3089,19 +3146,61 @@ namespace eval punk {
}
}
} elseif {$active_key_type eq "string"} {
if {[string match *-* $index]} {
lappend INDEX_OPERATIONS string-range
set re_idxdashidx {^([-+]{0,1}\d+|end[-+]{1}\d+|end)-([-+]{0,1}\d+|end[-+]{1}\d+|end)$}
#todo - support more complex indices: 0-end-1 etc
#changed to indexset notation m..n allowing eg 2..end-1 etc.
#if {[string match *-* $index]} {}
if {[punk::lib::is_indexset $index]} {
#review - we are assuming a single element indexset here - ie no comma separated sets.
#todo - support $get_not
#todo - consider bounds_check for string indices.
# - Tcl doesn't do bounds checking for string index, but we need to consider in the context of pattern-matching
# whether we want to support syntaxes for with and without bounds checking on string indices.
set is_range [expr {[string first ".." $index] >= 0}]
if {$is_range} {
lappend INDEX_OPERATIONS string-range
#review - not efficient for contiguous monotonically increasing ranges
#because we are retrievinng each character individually and concatenating
#- but it is more flexible because it also supports reverse ranges and could support non-contiguous ranges such as @0,2,4..6
set assign_script {
set assigned [join [lmap i [punk::lib::indexset_resolve [string length $leveldata] <idx>] {string index $leveldata $i}] ""]
}
} else {
lappend INDEX_OPERATIONS string-index
set assign_script {
set assigned [string index $leveldata [punk::lib::indexset_resolve [string length $leveldata] <idx>]]
}
}
#set assign_script {
# set assigned [lmap i [punk::lib::indexset_resolve [llength $leveldata] <idx>] {lindex $leveldata $i}]
#}
lassign [split $index -] a b
#todo - consider where/if we can support 'ansistring INDEX' for ANSI strings.
#if so - it shouldn't overload the % operator we currently use for string access.
append script \n [tstr -return string -allowcommands {
# set active_key_type "string"
set assigned [string range $leveldata ${$a} ${$b}]
if {$leveldata eq ""} {
set assigned ""
} else {
${$assign_script}
}
}]
set script [string map [list <idx> $index] $script]
#set re_idxdashidx {^([-+]{0,1}\d+|end[-+]{1}\d+|end)-([-+]{0,1}\d+|end[-+]{1}\d+|end)$}
##todo - support more complex indices: 0-end-1 etc
#lassign [split $index -] a b
#append script \n [tstr -return string -allowcommands {
# # set active_key_type "string"
# set assigned [string range $leveldata ${$a} ${$b}]
#}]
} else {
if {$index eq "*"} {
#equivalent to indexset ".."
lappend INDEX_OPERATIONS string-all
append script \n [tstr -return string -allowcommands {
# set active_key_type "string"
@ -4294,6 +4393,7 @@ namespace eval punk {
}
#todo check end-x bounds?
}
#todo - change to ledit
if {$isint} {
append script [string map [list <listvar> $listvar <idx> $index <exp> $exp <val> $data] {
set <listvar> [linsert [lindex [list $<listvar> [unset <listvar>]] 0] <idx> <exp><val>]
@ -4350,7 +4450,8 @@ namespace eval punk {
#last element has no -, so we are inserting at the final position - not replacing
append script [string map [list <listvar> $listvar <containerkeys> [lrange $parts 0 end-1] <lastkey> $last <exp> $exp <val> $data] {
set target [lindex $<listvar> <containerkeys>]
set target [linsert $target <lastkey> <exp><val>]
#set target [linsert $target <lastkey> <exp><val>]
ledit target <lastkey> -1 <exp><val>
lset <listvar> <containerkeys> $target
}]
}
@ -8564,7 +8665,7 @@ namespace eval punk {
lappend chunks [list stdout $text]
}
console - term - terminal {
set term_env_vars {TERM TERM_PROGRAM TERM_PROGRAM_VERSION}
set term_env_vars {TERM TERM_PROGRAM TERM_PROGRAM_VERSION COLORTERM}
set term_dict [dict create]
foreach e $term_env_vars {
if {[info exists ::env($e)]} {
@ -8577,6 +8678,7 @@ namespace eval punk {
append text [punk::lib::showdict $term_dict] \n
lappend chunks [list stdout $text]
set text ""
set indent [string repeat " " [string length "WARNING: "]]
if {[catch {package require punk::console} result]} {
set text "Unable to load punk::console package - cannot test\n$result"
@ -8591,7 +8693,6 @@ namespace eval punk {
}
lappend chunks [list stdout $text]
set indent [string repeat " " [string length "WARNING: "]]
lappend cstring_tests [dict create\
type "PM "\
msg "UN"\
@ -8686,10 +8787,45 @@ namespace eval punk {
}
}
}
set posn [punk::console::get_cursor_pos] ;#warmup call - and test if works
if {$posn eq ""} {
append warningblock \n "WARNING: terminal doesn't respond to cursor position query - may cause display bugs in some cases."
} else {
set timeresult [timerate {set cpos [punk::console::get_cursor_pos]}]
lassign [split $cpos {;}] row col
if {![string is integer -strict $row] || ![string is integer -strict $col]} {
append warningblock \n "WARNING: terminal returns non-integer values for cursor position query - may cause display bugs in some cases. got row:'$row' col:'$col'"
} else {
set micros [lindex $timeresult 0]
if {$micros > 2000} {
append warningblock \n "WARNING: terminal cursor position query is very slow ($micros microseconds - expect < 2000us )"
append warningblock \n $indent "- may cause display lag/bugs in some cases."
} else {
if {$micros > 1000} {
set text "\n[a+ yellow]Terminal cursor position query test passed."
append text \n $indent "Response time: ${micros} microseconds (OK, good would be <= 1000us).[a]"
} else {
set text "[a+ green]Terminal cursor position query test passed."
append text \n $indent "Response time: ${micros} microseconds (GOOD).[a]"
}
lappend chunks [list stdout $text]
}
}
}
if {![string length $warningblock]} {
set text "[a+ green]No terminal warnings[a]\n"
lappend chunks [list stdout $text]
} else {
set mode [punk::console::mode]
if {$mode eq "line"} {
append warningblock \n "Terminal appears to be in line mode. Consider switching to raw mode and re-testing (command: punk::console::mode raw)."
}
}
puts stdout [punk::ansi::move_back 200] ;#hack for some horizontal position bugs where the above tests can leave the cursor in the wrong place for the next output.
#200 is arbitrary large number to move back enough to get to start of line.
}
}
topics - help {
@ -8815,10 +8951,11 @@ namespace eval punk {
#interp alias {} c {} clear ;#external executable 'clear' may not always be available
#todo - review
interp alias {} clear {} ::punk::reset
interp alias {} c {} ::punk::reset
#interp alias {} clear {} ::punk::reset
#interp alias {} c {} ::punk::reset
interp alias {} reset {} ::punk::reset
proc reset {} {
if {[llength [info commands ::punk::repl::reset_terminal]]} {
#punk::repl::reset_terminal notifies prompt system of reset
@ -8828,6 +8965,91 @@ namespace eval punk {
}
}
namespace eval argdoc {
punk::args::define {
@id -id ::punk::ansi8
@cmd -name punk::ansi8\
-summary\
"Tell terminal to enable 8-bit ANSI codes."\
-help\
"Enable 8-bit ANSI codes in the terminal.
May not be supported by all terminals.
Some terminals may already have 8-bit ANSI enabled, but some may require an explicit command to enable it.
7-bit ANSI codes are generally preferred - and will still work on terminals with 8-bit ANSI support.
(This is nothing to do with 8-bit colors - it is about the underlying bytes used for ANSI control sequences).
The ANSI sequence sent to the terminal to enable 8-bit codes is: ESC <sp> 7
To disable 8-bit ANSI support - a reset of the terminal may be required.
"
@opts
@values -min 0 -max 0
}
}
proc ansi8 {} {
punk::console::S8C1R
}
namespace eval argdoc {
punk::args::define {
@id -id ::punk::clear
@cmd -name punk::clear\
-summary\
"Clear the terminal screen (and scrollback buffer by default)."\
-help\
"Clear the terminal screen.
By default this will also clear scrollback if supported by the terminal.
With -x option it will preserve scrollback but clear the screen.
"
@opts
-x -optional 1 -type none -mash 1 -help\
"Preserve scrollback (if supported by terminal) but clear screen."
-s -optional 1 -type none -mash 1 -help\
"Stay at the current cursor position instead of moving to top-left after clearing."
@values -min 0 -max 0
}
}
proc clear {args} {
set argd [punk::args::parse $args withid ::punk::clear]
lassign [dict values $argd] leaders opts values received
set opt_x [dict exists $received -x]
set opt_s [dict exists $received -s]
# -x preserves scrollback but clears screen
if {$opt_s} {
#set pre_move_cmd [punk::ansi::move_up 1]
#review - terminal support for save/restore.
#we can just move up one line before clearing to preserve the line we're on,
#but this won't work if we're already at the last line.
#save/restore would be better if widely supported.
#review - get_size already calls get_cursor pos - maybe we can optimize by not calling get_cursor_pos separately?
#review - consider turning off cursor updating while doing this to avoid flicker?
set cpos [punk::console::get_cursor_pos]
set row [lindex $cpos 0]
set size [punk::console::get_size]
set lastrow [dict get $size rows]
if {$row >= $lastrow} {
set pre_move_cmd [punk::ansi::cursor_save_dec]
} else {
set pre_move_cmd [punk::ansi::move_up 1][punk::ansi::cursor_save_dec]
}
set move_cmd [punk::ansi::cursor_restore_dec]
#set pre_move_cmd [punk::ansi::move_up 1]
#set move_cmd ""
} else {
set pre_move_cmd ""
set move_cmd [punk::ansi::move 1 1]
}
if {$opt_x} {
puts -nonewline stdout $pre_move_cmd[punk::ansi::clear]$move_cmd
} else {
puts -nonewline stdout $pre_move_cmd[punk::ansi::clear_all]$move_cmd
}
}
#c aliased to clear -xs
#cc aliases to clear -x
#fileutil::cat except with checking for windows illegal path names (when on windows platform)

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

@ -125,6 +125,10 @@ tcl::namespace::eval punk::aliascore {
grepstr ::punk::ansi::grepstr\
colour ::punk::console::colour\
color ::punk::console::colour\
ansi8 ::punk::ansi8\
clear ::punk::clear\
c {::punk::clear -xs}\
cc {::punk::clear -x}\
ansi ::punk::console::ansi\
a? ::punk::console::code_a?\
A? {::punk::console::code_a? forcecolor}\

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

File diff suppressed because it is too large Load Diff

2
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/ansi/colourmap-0.1.0.tm

@ -103,7 +103,7 @@ tcl::namespace::eval ::punk::ansi::colourmap {
name -type string|stringstartswith(#)
}]
proc get_rgb_using_tk {name} {
package require tk
package require Tk ;#package require tk (lowercase) doesn't always work
#assuming 'winfo depth .' is always 32 ?
set RGB [winfo rgb . $name]
set rgb [lmap n $RGB {expr {$n / 256}}]

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

@ -1250,6 +1250,11 @@ tcl::namespace::eval punk::args {
}
set optionspecs [list]
#REVIEW - whilst this is only done once for each command definition, the -help section processing is sometimes expensive,
#and isn't required for parsing of arguments, so it unnecessarily slows first use of a command that uses punk::args and is heavily documented,
#especially if it has tcl syntax highlighted examples.
#- ideally we would delay expansion of -help sections until needed for display,
#and use a different cache key for the parsing vs display versions of the resolved definition.
foreach block $normargs {
if {[string first \$\{ $block] >= 0} {
if {$defspace ne ""} {
@ -2550,7 +2555,7 @@ tcl::namespace::eval punk::args {
tcl::dict::set spec_merged -typesynopsis $specval
}
-parsekey - -group {
tcl::dict::set spec_merged -typesynopsis $specval
tcl::dict::set spec_merged $spec $specval
}
-mash {
#allow when any alt in argname is a single letter flag such s -a or -Z
@ -8535,7 +8540,7 @@ tcl::namespace::eval punk::args {
}
#todo - move block below up here.
if {!$all_mashable} {
puts stderr "Debug: flagsupplied '$flagsupplied' not a valid flagname and not a valid mash of flags - treating as value"
#puts stderr "Debug: flagsupplied '$flagsupplied' not a valid flagname and not a valid mash of flags - treating as value"
#- probably isn't a flag at all - could be a value
#treat as value
set optionset ""
@ -8668,9 +8673,8 @@ tcl::namespace::eval punk::args {
#tcl::dict::set opts $flag_ident $tdflt
if {$flag_ident_is_parsekey} {
#(shimmer - but required for ordering correctness during override)
puts stderr "Debug: flag '$mashflagname' in mash '$flagsupplied' flag_ident '$flag_ident' is the same as parsekey '$api_opt' tdflt: $tdflt - using lappend to ensure it ends up after any previous flag in the mash that had the same parsekey"
#puts stderr "Debug: flag '$mashflagname' in mash '$flagsupplied' flag_ident '$flag_ident' is the same as parsekey '$api_opt' tdflt: $tdflt - using lappend to ensure it ends up after any previous flag in the mash that had the same parsekey"
lappend opts $flag_ident $tdflt
puts stderr "opts after lappend: $opts"
} else {
tcl::dict::set opts $flag_ident $tdflt
}
@ -10241,6 +10245,128 @@ tcl::namespace::eval punk::args {
}
}
proc _synopsis_form_arg_display {formdict argname} {
#non-colour SGR such as bold/italic/strike - so we don't need to worry about NOCOLOR settings
set I "\x1b\[3m" ;#[punk::ansi::a+ italic]
set NI "\x1b\[23m" ;# [punk::ansi::a+ noitalic]
#for inner question marks marking optional type
set IS "\x1b\[3\;9m" ;#[punk::ansi::a+ italic strike]
set NIS "\x1b\[23\;29m" ;#[punk::ansi::a+ noitalic nostrike]
set RST "\x1b\[m" ;#[punk::ansi::a]
set arginfo [dict get $formdict ARG_INFO $argname]
set typelist [dict get $arginfo -type]
set ts [Dict_getdef $arginfo -typesynopsis ""]
set n [expr {[llength $typelist]-1}]
set name_tail [lrange $argname end-$n end];#if there are enough tail words in the argname to match -types
set clause ""
if {$ts ne ""} {
set tp_displaylist $ts
} else {
set tp_displaylist [lrepeat [llength $typelist] ""]
}
foreach typespec $typelist td $tp_displaylist elementname $name_tail {
#elementname will commonly be empty
if {[string match {\?*\?} $typespec]} {
set tp [string range $typespec 1 end-1]
set member_optional 1
} else {
set tp $typespec
set member_optional 0
}
if {$td ne ""} {
set c $td
} else {
#handle alternate-types e.g literal(text)|literal(binary)
set alternates [list]
set type_alternatives [_split_type_expression $tp]
foreach tp_alternative $type_alternatives {
set tp_alternative_word1 [lindex $tp_alternative 0]
set match [lindex $tp_alternative 1]
switch -exact -- $tp_alternative_word1 {
literal {
lappend alternates [list $match]
}
literalprefix {
#todo - trie styling on prefix calc
lappend alternates [list $match]
}
stringstartswith {
lappend alternates [list $match*]
}
stringendswith {
lappend alternates [list *$match]
}
default {
#we'll only take display hints from the name itself if there was no defined typesynopsis element for this position in the type,
#and if the type-alternatives don't specify a literal or string match that we can use for display
#and if there are enough tail words in the argname to match the position in the type list
#empty strings can be put in -typesynopsis positions to only override the type information for certain elements of the clause
#- e.g for a type list of {string int} we could specify a typesynopsis of {"" "count"} to get display of "FILENAME count" for an argname of "file FILENAME FILECOUNT"
if {[llength $name_tail] >= [llength $typelist]} {
#important to list protect $elementname e.g look at ::apply
#The name may contain spaces e.g "{args body ?namespace?}"
#This must not be split into multiple words - it is a single element name that happens to contain spaces.
lappend alternates $I[list $elementname]$NI
} else {
lappend alternates $I<$tp_alternative>$NI
}
}
}
}
set alternates [punk::args::lib::lunique $alternates]
set c [join $alternates |]
}
if {$member_optional} {
#append clause " " "(?$c?)"
append clause " " "\[$c\]"
} else {
append clause " " $c
}
}
set clause [string trimleft $clause]
#set ARGD [dict create argname $argname class leader]
if {[dict get $arginfo -optional] || [dict exists $arginfo -default]} {
if {[dict get $arginfo -multiple]} {
#set display "?$I$argname$NI?..."
set display "\[$clause\]..."
} else {
set display "\[$clause\]"
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "?[lindex [dict get $arginfo -choices] 0]?"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display "?$argname?"
#} else {
# set display "?$I$argname$NI?"
#}
}
} else {
if {[dict get $arginfo -multiple]} {
#set display "$I$argname$NI ?$I$argname$NI?..."
set display "$clause \[$clause\]..."
} else {
set display $clause
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "[lindex [dict get $arginfo -choices] 0]"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display $argname
#} else {
# set display "$I$argname$NI"
#}
}
}
return $display
}
lappend PUNKARGS [list {
@id -id ::punk::args::synopsis
@cmd -name punk::args::synopsis\
@ -10295,7 +10421,19 @@ tcl::namespace::eval punk::args {
if {$spec eq ""} {
return
}
set form_names [dict get $spec form_names]
set dict_idx_to_name [dict create]
set dict_name_to_idx [dict create]
set all_form_names [dict get $spec form_names]
set idx 0
#assert: form_names is ordered as defined in the command definition - so idx into it is stable.
foreach fn $all_form_names {
dict set dict_idx_to_name $idx $fn
dict set dict_name_to_idx $fn $idx
incr idx
}
set form_names $all_form_names
if {$form ne "*"} {
if {[string is integer -strict $form]} {
set f [lindex $form_names $form]
@ -10314,171 +10452,51 @@ tcl::namespace::eval punk::args {
}
set SYND [dict create]
dict set SYND cmd_info [dict get $spec cmd_info]
set c_info [dict get $spec cmd_info]
set cmd_info [dict create]
dict for {k v} $c_info {
if {[string match -* $k]} {
dict set cmd_info [string range $k 1 end] $v
}
}
dict set SYND COMMAND $cmd_info
#leading "# " required (punk::ns::synopsis will pass through)
if {![dict exists $received -noheader]} {
set syn "# [Dict_getdef $spec cmd_info -summary ""]\n"
set GRY "\x1b\[38\;5\;8m"
set RST "\x1b\[m"
}
#todo - -multiple etc
foreach f $form_names {
set SYNLIST [list]
dict set SYND FORMS $f [list]
append syn "$id"
set forminfo [dict get $spec FORMS $f]
#foreach argname [dict get $forminfo LEADER_NAMES] {
# set arginfo [dict get $forminfo ARG_INFO $argname]
# set ARGD [dict create argname $argname class leader]
# if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display [lindex [dict get $arginfo -choices] 0]
# } elseif {[dict get $arginfo -type] eq "literal"} {
# set display $argname
# } else {
# set display $I$argname$RST
# }
# if {[dict get $arginfo -optional]} {
# append syn " ?$display?"
# } else {
# append syn " $display"
# }
# dict set ARGD type [dict get $arginfo -type]
# dict set ARGD optional [dict get $arginfo -optional]
# dict set ARGD display $display
# dict lappend SYND $f $ARGD
#}
set idx [dict get $dict_name_to_idx $f]
dict set SYND FORMS $f [dict create]
if {![dict exists $received -noheader]} {
set formsummary "FORM $idx $f"
if {[dict exists $forminfo -summary]} {
append formsummary " - [dict get $forminfo -summary]"
}
append syn "## $GRY$formsummary$RST\n"
}
append syn "$id"
set FORMARGS [list]
foreach argname [dict get $forminfo LEADER_NAMES] {
set arginfo [dict get $forminfo ARG_INFO $argname]
set typelist [dict get $arginfo -type]
if {[llength $typelist] == 1} {
set tp [lindex $typelist 0]
set ts [Dict_getdef $arginfo -typesynopsis ""]
if {$ts ne ""} {
#set arg_display [dict get $arginfo -typesynopsis]
set clause $ts
} else {
#set arg_display $argname
set alternates [list];#alternate acceptable types e.g literal(yes)|literal(ok) or indexpression|literal(first)
set type_alternatives [_split_type_expression $tp]
foreach tp_alternative $type_alternatives {
set tp_alternative_word1 [lindex $tp_alternative 0]
switch -exact -- $tp_alternative_word1 {
literal {
set match [lindex $tp_alternative 1]
lappend alternates $match
}
literalprefix {
#todo - trie styling on prefix calc
set match [lindex $tp_alternative 1]
lappend alternates $match
}
stringstartswith {
set match [lindex $tp_alternative 1]
lappend alternates $match*
}
stringendswith {
set match [lindex $tp_alternative 1]
lappend alternates *$match
}
default {
lappend alternates $I$argname$NI
}
}
#if {$tp_alternative eq "literal"} {
# lappend alternates [lindex $argname end]
#} elseif {[string match literal(*) $tp_alternative]} {
# set match [string range $tp_alternative 8 end-1]
# lappend alternates $match
#} elseif {[string match literalprefix(*) $tp_alternative]} {
# set match [string range $tp_alternative 14 end-1]
# lappend alternates $match
#} else {
# lappend alternates $I$argname$NI
#}
}
#remove dupes - but keep order (e.g of dupes -type string|int when no -typesynopsis was specified)
#todo - trie prefixes display
set alternates [punk::args::lib::lunique $alternates]
set clause [join $alternates |]
}
} else {
set n [expr {[llength $typelist]-1}]
set name_tail [lrange $argname end-$n end];#if there are enough tail words in the argname to match -types
set clause ""
set ts [Dict_getdef $arginfo -typesynopsis ""]
if {$ts ne ""} {
set tp_displaylist $ts
} else {
set tp_displaylist [lrepeat [llength $typelist] ""]
}
foreach typespec $typelist td $tp_displaylist elementname $name_tail {
#elementname will commonly be empty
if {[string match {\?*\?} $typespec]} {
set tp [string range $typespec 1 end-1]
set member_optional 1
} else {
set tp $typespec
set member_optional 0
}
if {$tp eq "literal"} {
set c $elementname
} elseif {[string match literal(*) $tp]} {
set match [string range $tp 8 end-1]
set c $match
} else {
if {$td eq ""} {
set c $I$tp$NI
} else {
set c $td
}
}
if {$member_optional} {
append clause " " "(?$c?)"
} else {
append clause " " $c
}
}
set clause [string trimleft $clause]
}
foreach argname [dict get $forminfo LEADER_NAMES] {
set display [_synopsis_form_arg_display $forminfo $argname]
append syn " $display"
set arginfo [dict get $forminfo ARG_INFO $argname]
set ARGD [dict create argname $argname class leader]
if {[dict get $arginfo -optional] || [dict exists $arginfo -default]} {
if {[dict get $arginfo -multiple]} {
#set display "?$I$argname$NI?..."
set display "?$clause?..."
} else {
set display "?$clause?"
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "?[lindex [dict get $arginfo -choices] 0]?"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display "?$argname?"
#} else {
# set display "?$I$argname$NI?"
#}
}
} else {
if {[dict get $arginfo -multiple]} {
#set display "$I$argname$NI ?$I$argname$NI?..."
set display "$clause ?$clause?..."
} else {
set display $clause
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "[lindex [dict get $arginfo -choices] 0]"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display $argname
#} else {
# set display "$I$argname$NI"
#}
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
foreach k {choices choiceprefix choicerestricted choicemultiple} {
if {[dict exists $arginfo -$k]} {
dict set ARGD $k [dict get $arginfo -$k]
}
}
append syn " $display"
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
dict set ARGD display $display
#dict lappend SYND $f $ARGD
lappend FORMARGS $ARGD
}
foreach argname [dict get $forminfo OPT_NAMES] {
@ -10490,7 +10508,7 @@ tcl::namespace::eval punk::args {
#(disallowed in punk::args::define)
set argdisplay $argname
} else {
#assert [llength $tp] == 1 (multiple values for flag unspported in punk::args::define)
#assert [llength $tp] == 1 (multiple values for flag unsupported in punk::args::define)
if {[string match {\?*\?} $tp]} {
set tp [string range $tp 1 end-1]
set value_is_optional true
@ -10509,19 +10527,30 @@ tcl::namespace::eval punk::args {
} else {
set alternates [list];#alternate acceptable types e.g literal(yes)|literal(ok) or indexpression|literal(first)
foreach tp_alternative [split $tp |] {
#-type literal not valid for opt - review
if {[string match literal(*) $tp_alternative]} {
set match [string range $tp_alternative 8 end-1]
lappend alternates $match
} elseif {[string match literalprefix(*) $tp_alternative]} {
set match [string range $tp_alternative 14 end-1]
lappend alternates $match
} else {
lappend alternates <$I$tp_alternative$NI>
set type_alternatives [_split_type_expression $tp]
foreach tp_alternative $type_alternatives {
set match [lindex $tp_alternative 1]
switch -- [lindex $tp_alternative 0] {
literal {
lappend alternates [list $match]
}
literalprefix {
lappend alternates [list $match]
}
stringstartswith {
lappend alternates [list $match*]
}
stringendswith {
lappend alternates [list *$match]
}
default {
lappend alternates $I<$tp_alternative>$NI
}
}
}
#todo - trie prefixes display?
#trie prefixes display?
#we probably don't want to show prefixes in synopsis.
#AI agents should be encouraged to use full values for clarity, and human users can refer to help for the prefix info if they care.
set alternates [punk::args::lib::lunique $alternates]
set tp_display [join $alternates |]
}
@ -10529,44 +10558,102 @@ tcl::namespace::eval punk::args {
#need to bracket alternate-types to distinguish pipes delimiting flag aliases
set tp_display "($tp_display)"
}
#consider optional: -f|--file|--file= -type string|num
#we can't show this as [-f|--file|--file= string|num]
#because the pipes make visually parsing it ambiguous.
#we *could* show this as [-f|--file|--file= (string|num)]
# but it lacks clarity in descripting we can supply --file string or --file=string
#showing it as [-f (string|num)|--file (string|num)|--file=(string|num)] is not as compact as it could be, but is reasonably precise.
#we could merge the first two to avoid repeating the type info - but then we would also need brackets to clarify the pipe applicability:
#e.g
# [(-f|--file (string|num))|--file=(string|num)]
#
#we choose to only merge in the case where there are no trailing= aliases or they are all trailing= aliases.
set aliasflags [split $argname |]
#set has_longopt_inlinevalue_alias [expr {[lsearch -glob $aliasflags *=] >= 0}]
set num_longopt_inlinevalue_aliases [llength [lsearch -all -glob $aliasflags *=]] ;#count list of indices of aliasflags that end with =
set homogenous_aliases [expr {$num_longopt_inlinevalue_aliases == 0 || $num_longopt_inlinevalue_aliases == [llength $aliasflags]}]
set argdisplay ""
foreach aliasflag [split $argname |] {
if {[string match --* $aliasflag]} {
if {[string index $aliasflag end] eq "="} {
set alias [string range $aliasflag 0 end-1]
if {$value_is_optional} {
append argdisplay "$alias$IS?$NIS=$tp_display$IS?$NIS|"
if {!$homogenous_aliases} {
foreach aliasflag $aliasflags {
if {[string match --* $aliasflag]} {
if {[string index $aliasflag end] eq "="} {
set alias [string range $aliasflag 0 end-1]
if {$value_is_optional} {
#append argdisplay "$alias$IS\[$NIS=$tp_display$IS\]$NIS|"
append argdisplay "$alias$I\[$NI=$tp_display$I\]$NI|"
} else {
append argdisplay "$alias=$tp_display|"
}
} else {
append argdisplay "$alias=$tp_display|"
if {$value_is_optional} {
#double-dashed flag without trailing = can't accept optional value
#append argdisplay "$aliasflag $IS\[$NIS$tp_display$IS\]$NIS|"
append argdisplay "$aliasflag|"
} else {
append argdisplay "$aliasflag $tp_display|"
}
}
} else {
if {$value_is_optional} {
append argdisplay "$aliasflag $IS?$NIS$tp_display$IS?$NIS|"
#flag can't accept optional value
append argdisplay "$aliasflag|"
} else {
append argdisplay "$aliasflag $tp_display|"
}
}
}
set argdisplay [string trimright $argdisplay |]
} else {
if {$num_longopt_inlinevalue_aliases > 0} {
#all aliases are longopt inlinevalue aliases
#review
# --file=|--fname= -type string
# -> (--file|--fname)=type
# or
# -> (--file|--fname)[=type]
#first transform the argname to remove the trailing = and bracket the aliases if there are multiple
#review - we don't expect any arguments to be defined with inner = in the name.
#todo - enforce no inner = in argname in punk::args::define for options?
#
set argname "[string map {= ""} $argname]"
if {$num_longopt_inlinevalue_aliases > 1} {
set argname "($argname)"
}
if {$value_is_optional} {
set argdisplay "$argname$I\[$NI=$tp_display$I\]$NI"
} else {
set argdisplay "$argname=$tp_display"
}
} else {
#no longopts with trailing = aliases, so we can show the type info without ambiguity as applying to all aliases
if {$value_is_optional} {
#single dash flag can't accept optional value
append argdisplay "$aliasflag|"
set argdisplay "$argname $I\[$NI$tp_display$I\]$NI"
} else {
append argdisplay "$aliasflag $tp_display|"
set argdisplay "$argname $tp_display"
}
}
}
set argdisplay [string trimright $argdisplay |]
}
if {[dict get $arginfo -optional]} {
if {[dict get $arginfo -multiple]} {
set display "?$argdisplay?..."
#set display "?$argdisplay?..."
set display "\[$argdisplay\]..."
} else {
set display "?$argdisplay?"
#set display "?$argdisplay?"
set display "\[$argdisplay\]"
}
} else {
if {[dict get $arginfo -multiple]} {
set display "$argdisplay ?$argdisplay?..."
#set display "$argdisplay ?$argdisplay?..."
set display "$argdisplay \[$argdisplay\]..."
} else {
set display $argdisplay
}
@ -10606,136 +10693,43 @@ tcl::namespace::eval punk::args {
# }
# }
#}
#todo -mash
append syn " $display"
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
foreach k {choices choiceprefix choicerestricted choicemultiple} {
if {[dict exists $arginfo -$k]} {
dict set ARGD $k [dict get $arginfo -$k]
}
}
dict set ARGD display $display
#dict lappend SYND $f $ARGD
lappend FORMARGS $ARGD
}
foreach argname [dict get $forminfo VAL_NAMES] {
set arginfo [dict get $forminfo ARG_INFO $argname]
set typelist [dict get $arginfo -type]
if {[llength $typelist] == 1} {
set tp [lindex $typelist 0]
set ts [Dict_getdef $arginfo -typesynopsis ""]
if {$ts ne ""} {
#set arg_display [dict get $arginfo -typesynopsis]
set clause $ts
} else {
#set arg_display $argname
set alternates [list];#alternate acceptable types e.g literal(yes)|literal(ok) or indexpression|literal(first)
foreach tp_alternative [split $tp |] {
if {$tp_alternative eq "literal"} {
lappend alternates [lindex $argname end]
} elseif {[string match literal(*) $tp_alternative]} {
set match [string range $tp_alternative 8 end-1]
lappend alternates $match
} elseif {[string match literalprefix(*) $tp_alternative]} {
set match [string range $tp_alternative 14 end-1]
lappend alternates $match
} else {
lappend alternates $I$argname$NI
}
}
#remove dupes - but keep order (e.g of dupes -type string|int when no -typesynopsis was specified)
#todo - trie prefixes display
set alternates [punk::args::lib::lunique $alternates]
set clause [join $alternates |]
}
} else {
set n [expr {[llength $typelist]-1}]
set name_tail [lrange $argname end-$n end];#if there are enough tail words in the argname to match -types
set clause ""
set ts [Dict_getdef $arginfo -typesynopsis ""]
if {$ts ne ""} {
set tp_displaylist $ts
} else {
set tp_displaylist [lrepeat [llength $typelist] ""]
}
foreach typespec $typelist td $tp_displaylist elementname $name_tail {
#elementname will commonly be empty
if {[string match {\?*\?} $typespec]} {
set tp [string range $typespec 1 end-1]
set member_optional 1
} else {
set tp $typespec
set member_optional 0
}
#handle alternate-types e.g literal(text)|literal(binary)
set alternates [list]
foreach tp_alternative [split $tp |] {
if {$tp_alternative eq "literal"} {
lappend alternates $elementname
} elseif {[string match literal(*) $tp_alternative]} {
set match [string range $tp_alternative 8 end-1]
lappend alternates $match
} elseif {[string match literalprefix(*) $tp_alternative]} {
set match [string range $tp_alternative 14 end-1]
lappend alternates $match
} else {
if {$td eq ""} {
lappend alternates $I$tp$NI
} else {
lappend alternates $td
}
}
}
set alternates [punk::args::lib::lunique $alternates]
set c [join $alternates |]
if {$member_optional} {
append clause " " "(?$c?)"
} else {
append clause " " $c
}
}
set clause [string trimleft $clause]
}
set display [_synopsis_form_arg_display $forminfo $argname]
append syn " $display"
set ARGD [dict create argname $argname class value]
if {[dict get $arginfo -optional] || [dict exists $arginfo -default]} {
if {[dict get $arginfo -multiple]} {
#set display "?$I$argname$NI?..."
set display "?$clause?..."
} else {
set display "?$clause?"
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "?[lindex [dict get $arginfo -choices] 0]?"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display "?$argname?"
#} else {
# set display "?$I$argname$NI?"
#}
}
} else {
if {[dict get $arginfo -multiple]} {
#set display "$I$argname$NI ?$I$argname$NI?..."
set display "$clause ?$clause?..."
} else {
set display $clause
#if {[dict exists $arginfo -choices] && [llength [dict get $arginfo -choices]] == 1} {
# set display "[lindex [dict get $arginfo -choices] 0]"
#} elseif {[dict get $arginfo -type] eq "literal"} {
# set display $argname
#} else {
# set display "$I$argname$NI"
#}
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
foreach k {choices choiceprefix choicerestricted choicemultiple} {
if {[dict exists $arginfo -$k]} {
dict set ARGD $k [dict get $arginfo -$k]
}
}
append syn " $display"
dict set ARGD type [dict get $arginfo -type]
dict set ARGD optional [dict get $arginfo -optional]
dict set ARGD multiple [dict get $arginfo -multiple]
dict set ARGD display $display
#dict lappend SYND $f $ARGD
lappend FORMARGS $ARGD
}
#accepts unnamed extra arguments e.g toplevel docid for ensembles and ensemble-like commands
if {[dict get $forminfo VAL_UNNAMED]} {
set display "?<unnamed>...?"
set display {[<unnamed>...]}
append syn " $display"
set ARGD [dict create argname "" class value]
dict set ARGD type any
@ -10745,7 +10739,7 @@ tcl::namespace::eval punk::args {
lappend FORMARGS $ARGD
}
append syn \n
dict set SYND FORMS $f $FORMARGS
dict set SYND FORMS $f args $FORMARGS
}
switch -- $opt_return {
full {
@ -10757,7 +10751,8 @@ tcl::namespace::eval punk::args {
set summary "# [Dict_getdef $spec cmd_info -summary ""]\n"
}
set FORMS [dict get $SYND FORMS]
dict for {form arglist} $FORMS {
dict for {form arginfo} $FORMS {
set arglist [dict get $arginfo args]
append summary $id
set class_state leader
set option_count 0
@ -10774,7 +10769,7 @@ tcl::namespace::eval punk::args {
incr value_count
if {$class_state ne "value"} {
if {$option_count > 0} {
append summary " ?options ($option_count defined)?"
append summary " \[OPTIONS ($option_count defined)\]"
}
set class_state value
}
@ -10783,7 +10778,7 @@ tcl::namespace::eval punk::args {
}
}
if {$value_count == 0 && $option_count > 0} {
append summary " ?options ($option_count defined)?"
append summary " \[OPTIONS ($option_count defined)\]"
}
append summary \n
}
@ -10803,6 +10798,7 @@ tcl::namespace::eval punk::args {
}
#REVIEW
lappend PUNKARGS [list {
@id -id ::punk::args::synopsis_summary
@cmd -name punk::args::synopsis_summary -help\
@ -10852,9 +10848,10 @@ tcl::namespace::eval punk::args {
}
}
}
if {$code ne ""} {
if {$code ne "" && [tcl::string::index $code end] eq "m"} {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set codestack [list "\x1b\[m"]
#set codestack [list "\x1b\[m"]
set codestack [list $code]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set codestack [list $code]
} elseif {[punk::ansi::codetype::is_sgr $code]} {
@ -10862,10 +10859,9 @@ tcl::namespace::eval punk::args {
set dup_posns [lsearch -all -exact $codestack $code] ;#must be -exact because of square-bracket glob chars
set codestack [lremove $codestack {*}$dup_posns]
lappend codestack $code
} else {
#? ignore other ANSI codes?
}
}
#? ignore other ANSI codes?
}
if {[string match -* $plain_s] || [string match ?- $plain_s]} {
}

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

@ -2986,6 +2986,71 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
time -type integer -optional 1
} "@doc -name Manpage: -url [manpage_tcl file]" ]
lappend PUNKARGS [list {
@id -id ::tcl::file::attributes
@cmd -name "Built-in: tcl::file::attributes"\
-summary\
"Get/Set platform-specific values associated with a file/directory."\
-help\
"This subcommand returns or sets platform-specific values associated with a file.
The first form without specificing option, returns a list of the platform-specific options and their values.
The first form with an option returns the value for the given option.
The last form sets one or more of the values. The values are as follows:
On Unix, ${$B}-group${$N} gets or sets the group name for the file. A group id can be given to the command, but it
returns a group name. ${$B}-owner${$N} gets or sets the user name of the owner of the file. The command returns the
owner name, but the numerical id can be passed when setting the owner. ${$B}-permissions${$N} retrieves or sets a
file's access permissions, using octal notation by default. This option also provides limited support for
setting permissions using the symbolic notation accepted by the chmod command, following the form
${$B}[ugo]?[[+-=][rwxst],[...]]${$N}. Multiple permission specifications may be given, separated by commas.
E.g., ${$B}u+s,go-rw${$N} would set the setuid bit for a file's owner as well as remove read and write permission for
the file's group and other users. An ls-style string of the form rwxrwxrwx is also accepted but must always
be 9 characters long. E.g., ${$B}rwxr-xr-t${$N} is equivalent to ${$B}01755${$N}. On versions of Unix supporting file flags,
${$B}-readonly${$N} returns the value of, or sets, or clears the readonly attribute of a file, i.e., the user
immutable flag (${$B}uchg${$N}) to the ${$B}chflags${$N} command.
On Windows, ${$B}-archive${$N} gives the value or sets or clears the archive attribute of the file. ${$B}-hidden${$N} gives the
value or sets or clears the hidden attribute of the file. ${$B}-longname${$N} will expand each path element to its long
version. This attribute cannot be set. ${$B}-readonly${$N} gives the value or sets or clears the readonly attribute of
the file. ${$B}-shortname${$N} gives a string where every path element is replaced with its short (8.3) version of the
name if possible. For path elements that cannot be mapped to short names, the long name is retained. This
attribute cannot be set. ${$B}-system${$N} gives or sets or clears the value of the system attribute of the file.
On macOS and Darwin, ${$B}-creator${$N} gives or sets the Finder creator type of the file. ${$B}-hidden${$N} gives or sets or
clears the hidden attribute of the file. ${$B}-readonly${$N} gives or sets or clears the readonly attribute of the file.
${$B}-rsrclength${$N} gives the length of the resource fork of the file, this attribute can only be set to the value 0,
which results in the resource fork being stripped off the file.
On all platforms, files in ${$B}zipfs${$N} mounted archives return the following attributes.
These are all read-only and cannot be directly set.
${$B}-archive${$N}
The path of the mounted ZIP archive containing the file.
${$B}-compsize${$N}
The compressed size of the file within the archive. This is 0 for directories.
${$B}-crc${$N}
The CRC of the file if present, else 0.
${$B}-mount${$N}
The path where the containing archive is mounted.
${$B}-offset${$N}
The offset of the file within the archive.
${$B}-uncompsize${$N}
The uncompressed size of the file. This is ${$B}0${$N} for directories.
Other attributes may be present in the returned list. These should be ignored."
@form -form "get"
@values -min 1 -max 2
name -type string -optional 0
option -type stringstartswith(-) -typesynopsis {-${$I}option${$NI}} -optional 1
@form -form "set"
@values -min 3 -max -1
name -type string -optional 0
option_value -type {stringstartswith(-) string} -typesynopsis {-${$I}option${$NI} ${$I}value${$NI}} -optional 0 -multiple 1
} "@doc -name Manpage: -url [manpage_tcl file]" ]
lappend PUNKARGS [list {
@id -id ::tcl::file::channels
@cmd -name "Built-in: tcl::file::channels"\
@ -3026,6 +3091,26 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
pathname -optional 1 -type string -multiple 1
} "@doc -name Manpage: -url [manpage_tcl file]" ]
lappend PUNKARGS [list {
@id -id ::tcl::file::dirname
@cmd -name "Built-in: tcl::file::dirname"\
-summary\
"Return a path excluding last element."\
-help\
"Returns a name comprised of all of the path components in name excluding the last element.
If name is a relative file name and only contains one path element, then returns “.”. If name
refers to a root directory, then the root directory is returned. For example,
${[punk::args::helpers::example {
${$B} file dirname c:/
}]}
returns ${$B}c:/${$N}.
"
@values -min 1 -max 1
name -type string
} "@doc -name Manpage: -url [manpage_tcl file]" ]
lappend PUNKARGS [list {
@id -id ::tcl::file::copy
@cmd -name "Built-in: tcl::file::copy"\
@ -3104,7 +3189,10 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
#tcl 9+
lappend PUNKARGS [list {
@id -id ::tcl::file::home
@cmd -name "Built-in: tcl::file::home" -help\
@cmd -name "Built-in: tcl::file::home"\
-summary\
"Return the home directory for a user."\
-help\
"If no argument is specified, the command returns the home directory of the current user.
This is generally the value of the ${$B}$HOME${$N} environment variable except that on Windows
platforms backslashes in the path are replaced by forward slashes. An error is raised if
@ -3134,7 +3222,29 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
} "@doc -name Manpage: -url [manpage_tcl file]" ]
#join
#link
lappend PUNKARGS [list {
@id -id ::tcl::file::join
@cmd -name "Built-in: tcl::file::join"\
-summary\
"Join directory/file components into a single path."\
-help\
"Takes one or more file names and combines them, using the correct path separator for the current platform.
If a particular name is relative, then it will be joined to the previous file name argument. Otherwise, any
earlier arguments will be discarded, and joining will proceed from the current argument. For example,
${[punk::args::helpers::example {
${$B}file join ${$N} a b /foo bar
}]}
returns ${$B}/foo/bar${$N}.
Note that any of the names can contain separators, and that the result is always canonical for the current
platform: ${$B}/${$N} for Unix and Windows.
"
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]" ]
lappend PUNKARGS [list {
@id -id ::tcl::file::link
@cmd -name "Built-in: tcl::file::link"\
@ -3242,8 +3352,33 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
#owned
#pathtype
lappend PUNKARGS [list {
@id -id ::tcl::file::owned
@cmd -name "Built-in: tcl::file::owned"\
-summary\
"Test file owned by current user."\
-help\
"Returns ${$B}1${$N} if the file ${$I}name${$NI} is owned by the current user, ${$B}0${$N} otherwise."
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
lappend PUNKARGS [list {
@id -id ::tcl::file::pathtype
@cmd -name "Built-in: tcl::file::pathtype"\
-summary\
{Return path type. Either absolute, relative or volumerelative.}\
-help\
"Returns one of ${$B}absolute${$N}, ${$B}relative${$N}, ${$B}volumerelative${$N}. If name refers to a specific file on a specific
volume, the path type will be ${$B}absolute${$N}. If name refers to a file relative to the current working
directory, then the path type will be ${$B}relative${$N}. If name refers to a file relative to the current
working directory on a specified volume, or to a specific file on the current working volume, then
the path type is ${$B}volumerelative${$N}."
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
lappend PUNKARGS [list {
@id -id ::tcl::file::readable
@cmd -name "Built-in: tcl::file::readable"\
@ -3299,9 +3434,46 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
#separator
#size
#split
lappend PUNKARGS [list {
@id -id ::tcl::file::separator
@cmd -name "Built-in: tcl::file::separator"\
-summary\
{File separator character}\
-help\
"If no argument is given, returns the character which is used to separate path segments for native
files on this platform. If a path is given, the filesystem responsible for that path is asked to
return its separator character. If no file system accepts name, an error is generated."
@values -min 0 -max 1
name -optional 1 -type string -help\
"Path to query for separator character."
} "@doc -name Manpage: -url [manpage_tcl file]"]
lappend PUNKARGS [list {
@id -id ::tcl::file::size
@cmd -name "Built-in: tcl::file::size"\
-summary\
{Size of named file in bytes.}\
-help\
"Returns a decimal string giving the size of file ${$I}name${$NI} in bytes.
If the file does not exist or its size cannot be queried then an error is generated."
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
lappend PUNKARGS [list {
@id -id ::tcl::file::split
@cmd -name "Built-in: tcl::file::split"\
-summary\
{Split a path into list of components.}\
-help\
"Returns a list whose elements are the path components in ${$I}name${$NI}. The first element of the list will have
the same path type as ${$I}name${$NI}. All other elements will be relative. Path separators will be discarded unless
they are needed to ensure that an element is unambiguously relative."
@values -min 1 -max 1
name -optional 0 -type string
} "@doc -name Manpage: -url [manpage_tcl file]"]
lappend PUNKARGS [list {
@id -id ::tcl::file::stat
@cmd -name "Built-in: tcl::file::stat"\
@ -3399,8 +3571,20 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
As such, they can be relied upon to be used with operating-system native APIs
and external programs that require a filename."
@values -min 0 -max 2
nameVar -type string -optional 1
template -type string -optional 1
nameVar -type string -optional 1 -help\
"Variable to *receive* the name of the created temporary file.
Any existing value in the variable will not be read, and is just overwritten."
template -type string -optional 1 -help\
"On some platforms, such as windows:
- file extension is ignored.
- any directory components are ignored and
the last segment is used as a prefix for the temporary file name.
- If the TMP or TEMP environment variables are set, they are used
as the directory for the temporary file, otherwise the user's home
directory is used if it can be determined. (may depend on existence
of HOME or USERPROFILE environment variables.)
On other platforms, such as unix, the template may be handled
differently."
} "@doc -name Manpage: -url [manpage_tcl file]"]
#tildeexpand
@ -4528,11 +4712,16 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
}]}
}
@values -min 1
#{args body ?namespace?} is a single argument that is a list of two or three elements,
#as opposed to a clause of separate arguments.
#we don't have a way to validate the type of each element in a list - we can only check the length of the whole list.
@values -min 1 -max -1
"{args body ?namespace?}" -optional 0 -type list -minsize 2 -maxsize 3
arg -type any -optional 1 -multiple 1
} "@doc -name Manpage: -url [manpage_tcl apply]"\
{
@examples -help {
@ -7094,7 +7283,7 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
start -type number|expr
count -type literalprefix(count)
countelements -type number|expr
"by step" -type {literalprefix(by) number|expr} -optional 1
"by step" -type {?literalprefix(by)? number|expr} -optional 1
@form -form count
@leaders -min 0 -max 0
@ -10621,15 +10810,34 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
#force all on_handlers to be together and all try_handlers to be together, and it would force
#one type of handler to be listed always before or always after the other.
handler -optional 1 -multiple 1 -type {literal(on)|literal(trap) string list string}\
-typesynopsis {"" code|pattern variableList script}
-typesynopsis {"" oncode_or_trappattern variableList script}
#in our typesynopsis we deliberately don't put a pipe symbol in oncode_or_trappattern.
# e.g code|pattern would imply either on or trap could be combined with either code or pattern, which is not the case.
#todo?
#a way to define a compound type?
#handler -optional 1 -multiple 1 -type {<on_handler>|<try_handler>}
##<on_handler> -type {literal(on) <code> <variableList> <script>}
##<code> -type int -choices {0|ok 1|error 2|return 3|break 4|continue} -choicelabels {...}
#consider bracketed forms for -type - but we would have to do more complex parsing to determine size of clauses
##handler -type {(literal(on) code variableList script)|(literal(trap) pattern variableList script)}
## in this case either possible handler has length 4 - but we could easily imagine cases where different handlers have different lengths
#this gets unwieldy in synopsis listings.
#a way to define a compound type? perhaps with arity indicators for the component types? e.g
#handler -optional 1 -multiple 1 -type {<on_handler:4>|<try_handler:4>}
##on_handler:4 -type {literal(on) code variableList script}
##code -type int -choices {0|ok 1|error 2|return 3|break 4|continue} -choicelabels {...}
#..
##<try_handler> -type {literal(trap) <pattern> <variableList> <script>}
##<pattern> -type list
##try_handler -type {literal(trap) pattern variableList script}
##pattern -type list
##etc
#how would we declare arity for a compound type that has alternate subtypes of different arity?
#e.g <generalhandler>:3..4 -type {<on_handler:4>|<other_handler:3>}
#would these types be global or per definition?
#if both allowed - what about documentation packages clashing names?
#require some kind of namespacing for types? e.g package::types::code ?)
#e.g punk::args::moduledoc::tkcore::anchor (n|ne|e|se|s|sw|w|nw|center)
#could we provide a way to import for a definition eg @typeimport -package punk::args::moduledoc::tkcore
# so that the types defined there could be used in our definitions without needing to namespace them?
#consider also RPN for compound type definitions
##<mytype1> -type {{int double OR}}
@ -12052,7 +12260,7 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
@form -form "basic"
pattern -type string -optional 1 -help "glob pattern"
@form -form "controlledglob"
@form -form "controlled"
@values -min 2 -max 2
patterntype -type string -choices {-glob -regexp} -typesynopsis -glob|-regex -optional 0
pattern -type string -optional 0

21
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/auto_exec-0.1.0.tm

@ -96,7 +96,24 @@ tcl::namespace::eval punk::auto_exec {
-summary\
"Manage the hash table of autoexec commands cached in ::auto_execs."\
-help\
{see also ::punk::auto_exec::rehash}
{Manage the cache of autoexec commands in the ::auto_execs array.
This is analogous to the 'hash' command in shells such as csh, tcsh and zsh, or 'hash' in bash.
It can be used to display the current cached ${$B}auto_execok${$N} commands, to add new commands to the cache,
to delete commands from the cache, and to clear the cache.
When adding new commands to the cache, it will attempt to find the command string associated with
the given name by calling auto_execok for that name, and if found it will add it to the cache.
If not found, it will display an error message on stderr for that name and add an empty string to
the cache for that name if the name is an absolute path or a bare word.
When displaying commands with ${$B}hash -t ${$I}name${$NI}${$N}, if only a single name is provided, then the output will
be the raw command string associated with that autoexec command in the hash table. If multiple names
are provided, then the output will be a string containing each name and its associated command string
on a separate line.
see also ::punk::auto_exec::rehash}
#---------------------
@form -form {show_or_set}
@ -125,7 +142,7 @@ tcl::namespace::eval punk::auto_exec {
If multiple names are provided, then the output will be a string containing each
name and its associated command string on a separate line."
#---------------------
@form -form {delete}
@form -form {delete} -summary "Delete autoexec commands from the hash table."
@opts
-d -type none -optional 0 -help\
"Delete specified autoexec commands from the hash table."

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

@ -1860,8 +1860,10 @@ tcl::namespace::eval punk::char {
lappend settype_list [tcl::dict::get $charsets $setname settype]
}
set charset_names [linsert $charset_names 0 "Set Name"]
set settype_list [linsert $settype_list 0 "Set Type"]
#set charset_names [linsert $charset_names 0 "Set Name"]
ledit charset_names 0 -1 "Set Name"
#set settype_list [linsert $settype_list 0 "Set Type"]
ledit settype_list 0 -1 "Set Type"
return [textblock::join -- [list_as_lines -- $charset_names] " " [list_as_lines $settype_list]]
}

20
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/console-0.1.1.tm

@ -2608,7 +2608,7 @@ namespace eval punk::console {
error "dec_request_setting unrecognised name $name. Known values: [dict keys $DECRQSS_DICT]"
}
set str [dict get $DECRQSS_DICT $name]
set re_str [string map [list * \\* \$ \\\$ + \\+ ( \\(] $str] ;#regex escaped
set re_str [string map [list | \\| * \\* \$ \\\$ + \\+ ( \\( ) \\)] $str] ;#regex escaped
#review {[0-9;:]} - too restrictive? - what values can be returned? alnum? - we perhaps at least need to exclude ESC so we don't overmatch
set capturingregex [string map [list %s% $re_str] {(.*)(\x1bP([0-1]\$r[0-9;:]*)(?:%s%){0,1}\x1b\\)$}] ;#must capture prefix,entire-response,response-payload
#todo - handle xterm : [0-1] $ r D...D ST
@ -2938,6 +2938,13 @@ namespace eval punk::console {
proc clear_all {} {
puts -nonewline stdout [punk::ansi::clear_all]
}
proc clear_scrollback {} {
puts -nonewline stdout [punk::ansi::clear_scrollback]
}
proc S8C1R {} {
puts -nonewline stdout [punk::ansi::S8C1R]
}
proc reset {} {
puts -nonewline stdout [punk::ansi::reset]
}
@ -3073,11 +3080,12 @@ namespace eval punk::console {
proc move_emitblock_return {row col textblock} {
lassign [punk::console::get_cursor_pos_list] orig_row orig_col
set commands ""
foreach ln [split $textblock \n] {
append commands [punk::ansi::move_emit $row $col $ln]
incr row
}
set commands [punk::ansi::move_emit $row $col $textblock] ;#move_emit can handle multiple line blocks.
#set commands ""
#foreach ln [split $textblock \n] {
# append commands [punk::ansi::move_emit $row $col $ln]
# incr row
#}
append commands [punk::ansi::move $orig_row $orig_col]
puts -nonewline $commands
return

438
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/lib-0.1.6.tm

@ -2190,6 +2190,7 @@ namespace eval punk::lib {
} else {
set qry $key
}
#pipeline - use punk patterns.
% thisval.= $qry= $dval
}
@ -2219,7 +2220,7 @@ namespace eval punk::lib {
string {
set hidekey 1
switch -- $key {
"%string" {
"%string" - "%str" {
set hidekey 1
set thisval $dval
}
@ -2231,7 +2232,9 @@ namespace eval punk::lib {
}
default {
switch -glob -- $key {
*lpad-* {
%XXXlpad-* {
#todo - remove
#moved to punk patterns
set hidekey 1
lassign [split $key -] _ extra
set width [expr {[textblock::width $dval] + $extra}]
@ -2255,7 +2258,10 @@ namespace eval punk::lib {
set width [expr {[textblock::width $dval] + [tcl::string::length $extra]}]
set thisval [textblock::pad $dval -which right -width $width -padchar $extra]
}
%split-* {
%XXXsplit-* {
#todo - remove
# moved to punk patterns.
#supported here by default branch.
#split on one or more chars - review
set hidekey 1
lassign [split $key -] _ splitchars
@ -2271,7 +2277,7 @@ namespace eval punk::lib {
if {[string index $key 0] ne "%"} {
set key %$key
}
#pipeline
#pipeline - use punk patterns.
% thisval.= $key= $thisval
}
}
@ -3250,7 +3256,7 @@ namespace eval punk::lib {
We will get something like 10+1 - which can be resolved safely with expr
"
@values -min 2 -max 2
datalength -type integer
datalength -type integer -range {0 ""}
index -type indexexpression
}
proc lindex_resolve {len index {base 0}} {
@ -3280,6 +3286,7 @@ namespace eval punk::lib {
#basic forward compatibility with integers such as 1_000 for 8.6.x
set index [tcl::string::map {_ {}} $index]
set len [tcl::string::map {_ {}} $len]
set base [tcl::string::map {_ {}} $base]
}
if {![string is integer -strict $len] || $len < 0} {
@ -3339,10 +3346,10 @@ namespace eval punk::lib {
return $based_max
}
} else {
#plain +-<int> already handled above.
#plain +-<int> already handled above. (but not +-<int>+-<int> etc)
#we are trying to avoid evaluating unbraced expr of potentially insecure origin
#regexp must split a++b to a + +b (not a+ + b) ie first +/- is the op
if {[regexp {([^+-]*)([+-])(.*)} $index _match a op b]} {
if {[regexp {([+-]{0,1}[^+-]*)([+-])(.*)} $index _match a op b]} {
if {[string is integer -strict $a] && [string is integer -strict $b]} {
if {$op eq "-"} {
set index [expr {$a - $b}]
@ -3374,6 +3381,16 @@ namespace eval punk::lib {
#[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
#REVIEW - we need compat for 1_000 etc to handle things like toml even in 8.6?
#A basic string map means we aren't properly validating
#todo - be stricter about malformations such as 1000_
if {![string is integer -strict 1_0]} {
#basic forward compatibility with integers such as 1_000 for 8.6.x
set index [tcl::string::map {_ {}} $index]
set len [tcl::string::map {_ {}} $len]
set base [tcl::string::map {_ {}} $base]
}
if {![string is integer -strict $len] || $len < 0} {
error "lindex_resolve_basic len must be an integer greater than or equal to zero"
}
@ -4196,6 +4213,7 @@ namespace eval punk::lib {
# important for pipeline & match_assign
# -line trimline|trimleft|trimright -block trimhead|trimtail|triminner|trimall|trimhead1|trimtail1|collateempty -commandprefix {string length} ?
# -block trimming only trims completely empty lines. use -line trimming to remove whitespace e.g -line trimright will clear empty lines without affecting leading whitespace on other lines that aren't pure whitespace
set linelist_body {
set usage "linelist ?-ansiresets auto|<bool>? ?-ansireplays 0|1? ?-line trimline|trimleft|trimright? ?-block trimhead|trimtail|triminner|trimall|trimhead1|trimtail1|collateempty? -commandprefix <cmdlist> text"
if {[llength $args] == 0} {
@ -4487,7 +4505,8 @@ namespace eval punk::lib {
}
#set newreplay [join $codestack ""]
set newreplay [punk::ansi::codetype::sgr_merge_list {*}$codestack]
#set newreplay [punk::ansi::codetype::sgr_merge_list {*}$codestack]
set newreplay [punk::ansi::codetype::sgr_merge $codestack]
if {$line_has_sgr && $newreplay ne $replaycodes} {
#adjust if it doesn't already does a reset at start
@ -4823,7 +4842,8 @@ namespace eval punk::lib {
}
#set newreplay [join $codestack ""]
set newreplay [punk::ansi::codetype::sgr_merge_list {*}$codestack]
#set newreplay [punk::ansi::codetype::sgr_merge_list {*}$codestack]
set newreplay [punk::ansi::codetype::sgr_merge $codestack]
if {$RST ne "" && $line_has_sgr && $newreplay ne $replaycodes} {
#adjust if it doesn't already does a reset at start
@ -4868,6 +4888,406 @@ namespace eval punk::lib {
set linelist_body [string map {<require_punk_ansi> "package require punk::ansi"} $linelist_body]
}
proc linelist {args} $linelist_body
set linelist_body2 {
set usage "linelist ?-ansiresets auto|<bool>? ?-ansireplays 0|1? ?-line trimline|trimleft|trimright? ?-block trimhead|trimtail|triminner|trimall|trimhead1|trimtail1|collateempty? -commandprefix <cmdlist> text"
if {[llength $args] == 0} {
error "linelist missing textchunk argument usage:$usage"
}
set text [lindex $args end]
set text [string map {\r\n \n} $text] ;#review - option?
set arglist [lrange $args 0 end-1]
set opts [tcl::dict::create\
-block {trimhead1 trimtail1}\
-line {}\
-commandprefix ""\
-ansiresets auto\
-ansireplays 0\
]
foreach {o v} $arglist {
switch -- $o {
-block - -line - -commandprefix - -ansiresets - -ansireplays {
tcl::dict::set opts $o $v
}
default {
error "linelist: Unrecognized option '$o' usage:$usage"
}
}
}
# -- --- --- --- --- ---
set opt_block [tcl::dict::get $opts -block]
if {[llength $opt_block]} {
foreach bo $opt_block {
switch -- $bo {
trimhead - trimtail - triminner - trimall - trimhead1 - trimtail1 - collateempty {}
default {
set known_blockopts [list trimhead trimtail triminner trimall trimhead1 trimtail1 collateempty]
error "linelist: unknown -block option value: $bo known values: $known_blockopts"
}
}
}
#normalize certain combos
if {"trimhead" in $opt_block && [set posn [lsearch $opt_block trimhead1]] >=0} {
set opt_block [lreplace $opt_block $posn $posn]
}
if {"trimtail" in $opt_block && [set posn [lsearch $opt_block trimtail1]] >=0} {
set opt_block [lreplace $opt_block $posn $posn]
}
if {"trimall" in $opt_block} {
#no other block options make sense in combination with this
set opt_block [list "trimall"]
}
#TODO
if {"triminner" in $opt_block } {
error "linelist -block triminner not implemented - sorry"
}
}
# -- --- --- --- --- ---
set opt_line [tcl::dict::get $opts -line]
set tl_left 0
set tl_right 0
set tl_both 0
foreach lo $opt_line {
switch -- $lo {
trimline {
set tl_both 1
}
trimleft {
set tl_left 1
}
trimright {
set tl_right 1
}
default {
set known_lineopts [list trimline trimleft trimright]
error "linelist: unknown -line option value: $lo known values: $known_lineopts"
}
}
}
#normalize trimleft trimright combo
if {$tl_left && $tl_right} {
set opt_line [list "trimline"]
set tl_both 1
}
# -- --- --- --- --- ---
set opt_commandprefix [tcl::dict::get $opts -commandprefix]
# -- --- --- --- --- ---
set opt_ansiresets [tcl::dict::get $opts -ansiresets]
# -- --- --- --- --- ---
set opt_ansireplays [tcl::dict::get $opts -ansireplays]
if {$opt_ansireplays} {
if {$opt_ansiresets eq "auto"} {
set opt_ansiresets 1
}
} else {
if {$opt_ansiresets eq "auto"} {
set opt_ansiresets 0
}
}
# -- --- --- --- --- ---
#set linelist [list]
#set nlsplit [split $text \n]
set linelist [split $text \n]
set original_length [llength $linelist]
#---------------------------
#todo - consider applying these inline later
if {![llength $opt_line]} {
#set linelist $nlsplit
#lappend linelist {*}$nlsplit
} else {
#already normalized trimleft+trimright to trimline
set nlsplit $linelist
#set linelist [list]
if {$tl_both} {
set i 0
foreach ln $linelist {
#lappend linelist [string trim $ln]
lset linelist $i [string trim $ln]
incr i
}
} elseif {$tl_left} {
set i 0
foreach ln $linelist {
#lappend linelist [string trimleft $ln]
lset linelist $i [string trimleft $ln]
incr i
}
} elseif {$tl_right} {
set i 0
foreach ln $nlsplit {
#lappend linelist [string trimright $ln]
lset linelist $i [string trimright $ln]
incr i
}
}
}
#---------------------------
set remove_indices [list]
if {"collateempty" in $opt_block} {
set last "-"
for {set i 0} {$i < $original_length} {incr i} {
if {[lindex $linelist $i] ne ""} {
set last "-"
} else {
if {$last ne ""} {
lappend remove_indices $i
set last ""
}
}
}
}
if {"trimall" in $opt_block} {
#we have already made sure there are no other block options that would conflict with this
#set linelist [lsearch -all -inline -not -exact $linelist[set linelist {}] ""]
#set remove_indices [list]
for {set i 0} {$i < $original_length} {incr i} {
if {[lindex $linelist $i] eq ""} {
lappend remove_indices $i
}
}
} else {
if {"trimhead" in $opt_block} {
#set remove_indices [list]
for {set i 0} {$i < $original_length} {incr i} {
if {[lindex $linelist $i] ne ""} {
break
} else {
lappend remove_indices $i
}
}
}
if {"trimtail" in $opt_block} {
set remove_indices [list]
for {set i [expr {$original_length-1}]} {$i >=0} {incr i -1} {
if {[lindex $linelist $i] ne ""} {
break
} else {
lappend remove_indices $i
}
}
#set revlinelist [lreverse $linelist][set linelist {}]
#set i 0
#foreach ln $revlinelist {
# if {$ln ne ""} {
# set linelist [lreverse [lrange $revlinelist $i end]]
# break
# }
# incr i
#}
}
# --- ---
set start 0
set end "end"
if {"trimhead1" in $opt_block} {
if {[lindex $linelist 0] eq ""} {
lappend remove_indices 0
}
}
if {"trimtail1" in $opt_block} {
if {[lindex $linelist end] eq ""} {
lappend remove_indices [expr {$original_length-1}]
}
}
#set linelist [lrange $linelist $start $end]
}
#review - we need to make sure ansiresets don't accumulate/grow on any line
#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} {
<require_punk_ansi> ;#package require punk::ansi
if {$opt_ansiresets} {
set RST "\x1b\[0m"
} else {
set RST ""
}
set replaycodes $RST ;#todo - default?
#set transformed [list]
#shortcircuit common case of no ansi
#NOTE: running ta::detect on a list (or dict) as a whole can be problematic if items in the list have backslash escapes due to Tcl list quoting and escaping behaviour.
#This commonly happens if there is an unbalanced brace (which is a normal occurrence and needs to be handled)
#ta::detect on a list of ansi-containing string may appear to work for some simple inputs but is not reliable
#detect_in_list/detectcode_in_list will check at first level. (not intended for detecting ansi in deeper structures)
#we use detectcode_in_list instead of detect_in_list
#detectcode_in_list will detect unclosed (or unopened) paired sequences such as PM (privacy message)
# - but the main reason is it is slightly faster.
if {![punk::ansi::ta::detectcode_in_list $linelist]} {
if {$opt_ansiresets} {
for {set i 0} {$i < $original_length} {incr i} {
if {$i in $remove_indices} {
continue
}
lset linelist $i $RST[lindex $linelist $i]$RST
}
}
} else {
#INLINE punk::ansi::codetype::is_sgr_reset
#regexp {\x1b\[0*m$} $code
set re_is_sgr_reset {\x1b\[0*m$}
#INLINE punk::ansi::codetype::is_sgr
#regexp {\033\[[0-9;:]*m$} $code
set re_is_sgr {\x1b\[[0-9;:]*m$}
#foreach ln $linelist {}
for {set i 0} {$i < $original_length} {incr i} {
if {$i in $remove_indices} {
continue
}
#set ln [lindex $linelist $i]
#set is_replay_pure_reset [regexp {\x1b\[0*m$} $replaycodes] ;#only looks at tail code - but if tail is pure reset - any prefix is ignorable
#set ansisplits [punk::ansi::ta::split_codes_single $ln] ;#REVIEW - this split accounts for a large portion of the time taken to run this function.
#get_codes_single lists only the codes. no plaintext or empty elements
set ansisplits [punk::ansi::ta::get_codes_single [lindex $linelist $i]] ;#REVIEW - this split accounts for a large portion of the time taken to run this function.
if {[llength $ansisplits] == 0} {
#plaintext only - no ansi codes in line
#lappend transformed [string cat $replaycodes $ln $RST]
lset linelist $i $replaycodes[lindex $linelist $i]$RST
#leave replaycodes as is for next line
set nextreplay $replaycodes
} else {
set tail $RST
set lastcode [lindex $ansisplits end] ;#may or may not be SGR
set lastcodeoffset [expr {[string length $lastcode]-1}]
if {[punk::ansi::codetype::is_sgr_reset $lastcode]} {
if {[string range [lindex $linelist $i] end-$lastcodeoffset end] eq $lastcode} {
#last plaintext is empty. So the line is already suffixed with a reset
set tail ""
} else {
#trailing text has been reset within line - but no tail reset present
#we normalize by putting a tail reset on anyway
set tail $RST
}
set nextreplay $RST
} elseif {[string range [lindex $linelist $i] end-$lastcodeoffset end] eq $lastcode && [punk::ansi::codetype::has_sgr_leadingreset $lastcode]} {
#code is at tail (no trailing plaintext)
#No tail reset - and no need to examine whole line to determine stack that is in effect
set tail $RST
set nextreplay $lastcode
} else {
#last codeset doesn't reset from earlier codes or isn't SGR - so we have to look at whole line to determine codes in effect
#last codeset doesn't end in a pure-reset
#whether code was at very end or not - add a reset tail
set tail $RST
#determine effective replay for line
set codestack [list start]
foreach code $ansisplits {
if {[tcl::string::index $code end] eq "m"} {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set codestack [list] ;#different from 'start' marked - this means we've had a reset
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set codestack [list $code]
} else {
if {[punk::ansi::codetype::is_sgr $code]} {
#todo - proper test of each code - so we only take latest background/foreground etc.
#requires handling codes with varying numbers of parameters.
#basic simplification - remove straight dupes.
set dup_posns [lsearch -all -exact $codestack $code] ;#!must use -exact as codes have square brackets which are interpreted as glob chars.
set codestack [lremove $codestack {*}$dup_posns]
lappend codestack $code
}
}
}
;#else gx0 or other code - we don't want to stack it with SGR codes
}
if {[llength $codestack] == 1 && [lindex $codestack 0] eq "start"} {
#No SGRs - may have been other codes
set line_has_sgr 0
} else {
#list is either empty or begins with start - empty means it had SGR reset - so it still invalidates current state of replaycodes
set line_has_sgr 1
if {[lindex $codestack 0] eq "start"} {
#set codestack [lrange $codestack 1 end]
ledit codestack 0 0
}
}
if {$line_has_sgr} {
#set newreplay [punk::ansi::codetype::sgr_merge_list {*}$codestack]
set newreplay [punk::ansi::codetype::sgr_merge $codestack]
if {$newreplay ne $replaycodes} {
#adjust if it doesn't already does a reset at start
if {$RST ne ""} {
if {[punk::ansi::codetype::has_sgr_leadingreset $newreplay]} {
set nextreplay $newreplay
} else {
set nextreplay $RST$newreplay
}
} else {
set nextreplay $newreplay
}
} else {
set nextreplay $replaycodes
}
} else {
set nextreplay $replaycodes
}
}
if {"$replaycodes$tail" ne ""} {
if {[punk::ansi::codetype::has_sgr_leadingreset [lindex $linelist $i]]} {
#no point attaching any replay
#lappend transformed [string cat $ln $tail]
if {$tail ne ""} {
lset linelist $i [lindex $linelist $i]$tail
}
} else {
#lappend transformed [string cat $replaycodes $ln $tail]
lset linelist $i $replaycodes[lindex $linelist $i]$tail
}
}
}
set replaycodes $nextreplay
}
#jjj
#set linelist $transformed
}
}
#todo - run this before ansireplay processing and adjust indices accordingly? or just run it after as is and accept that commandprefix will be added to each line after replay processing?
if {[llength $opt_commandprefix]} {
for {set i 0} {$i < $original_length} {incr i} {
if {$i in $remove_indices} {
continue
}
lset linelist $i [{*}$opt_commandprefix [lindex $linelist $i]]
}
#set transformed [list]
#foreach ln $linelist {
# lappend transformed [{*}$opt_commandprefix $ln]
#}
#set linelist $transformed
}
if {[llength $remove_indices]} {
set linelist [lremove $linelist {*}$remove_indices]
}
return $linelist
}
if {$has_punk_ansi} {
#optimise linelist as much as possible
set linelist_body2 [string map {<require_punk_ansi> ""} $linelist_body2]
} else {
#punk ansi not avail at time of package load.
#by putting in calls to punk::ansi the user will get appropriate error messages
set linelist_body2 [string map {<require_punk_ansi> "package require punk::ansi"} $linelist_body2]
}
proc linelist {args} $linelist_body2
interp alias {} errortime {} punk::lib::errortime

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

@ -950,6 +950,7 @@ tcl::namespace::eval ::punk::libunknown {
}
if {$has_prefix} {
set update [linsert $update end-$offset $new]
#end based index used with linsert - so can't replace with ledit.
} else {
lappend update $new
}

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

@ -43,7 +43,7 @@ namespace eval punk::mix::commandset::repo {
lappend PUNKARGS [list {
@id -id ::punk::mix::commandset::repo::fossilize
@cmd -name punk::mix::commandset::repo::fossilize
@cmd -name punk::mix::commandset::repo::fossilize\
-summary\
"Initialise and check in a project to fossil (unimplemented)."\
-help\
@ -56,7 +56,7 @@ namespace eval punk::mix::commandset::repo {
lappend PUNKARGS [list {
@id -id ::punk::mix::commandset::repo::unfossilize
@cmd -name punk::mix::commandset::repo::unfossilize
@cmd -name punk::mix::commandset::repo::unfossilize\
-summary\
"Remove/archive .fossil (unimplemented)."\
-help\
@ -92,9 +92,9 @@ namespace eval punk::mix::commandset::repo {
#punk::args
lappend PUNKARGS [list {
@id -id ::punk::mix::commandset::repo::fossil-move-repository
@cmd -name punk::mix::commandset::repo::fossil-move-repository
@cmd -name punk::mix::commandset::repo::fossil-move-repository\
-summary\
"Move a fossil repository database file."\
"Interactively move a fossil repository database file."\
-help\
"Move the fossil repository file (usually named with .fossil extension).
This is an interactive function which will prompt for answers on stdin

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

@ -1170,22 +1170,24 @@ tcl::namespace::eval punk::ns {
#NOTE aliases may not be commands in current namespace - but we want to show them (marked red and with R)
#
set children [list]
set commands [list]
set exported [list]
set imported [list]
set aliases [list]
set procs [list]
set ensembles [list]
set ooclasses [list]
set ooobjects [list]
set children [list]
set packagetails [list]
set packageprefixes [list]
set commands [list]
set exported [list]
set imported [list]
set aliases [list]
set procs [list]
set ensembles [list]
set ooclasses [list]
set ooobjects [list]
set ooprivateobjects [list]
set ooprivateclasses [list]
set native [list]
set interps [list]
set coroutines [list]
set zlibstreams [list]
set usageinfo [list]
set native [list]
set interps [list]
set coroutines [list]
set zlibstreams [list]
set usageinfo [list]
if {![dict size $opt_nsdict]} {
set nsmatches [get_ns_dicts $fq_glob -allbelow 0]
@ -1216,6 +1218,8 @@ tcl::namespace::eval punk::ns {
package require overtype
if {"children" in $types} {
set children [dict get $contents children]
set packagetails [dict get $contents packagetails]
set packageprefixes [dict get $contents packageprefixes]
}
if {"commands" in $types} {
set commands [dict get $contents commands]
@ -1368,12 +1372,26 @@ tcl::namespace::eval punk::ns {
set c_ooC [a+ term-cornflowerblue] ;#privateClass
set c_zst [a+ term-yellow] ;#zlibstreams
set a1 [a][a+ cyan]
set a1 [a][a+ cyan] ;#child namespace SGR code.
foreach ch1 $children1 ch2 $children2 cmd1 $elements1 cmd2 $elements2 cmd3 $elements3 cmd4 $elements4 {
set c1 [a+ white]
set c2 [a+ white]
set c3 [a+ white]
set c4 [a+ white]
foreach nsvar {ch1 ch2} {
set v [set $nsvar]
if {$v in $packagetails} {
#may also be a packageprefix.
if {$v in $packageprefixes} {
set $nsvar [a+ underdouble]$v
} else {
#just a package - no prefix - we want to underline but not doubled
set $nsvar [a+ underline]$v
}
} elseif {$v in $packageprefixes} {
set $nsvar [a+ underdotted]$v
}
}
for {set i 1} {$i <= 4} {incr i} {
if {[llength [set cmd$i]]} {
@ -1441,7 +1459,7 @@ tcl::namespace::eval punk::ns {
}
#lappend displaylist $a1[overtype::left $col1 $ch1][a+]$a1[overtype::left $col2 $ch2][a+]$c1[overtype::left $col3 $cmd1][a+]$c2[overtype::left $col4 $cmd2][a+]$c3[overtype::left $col5 $cmd3][a+]$c4$cmd4[a+]
lappend displaylist $a1[overtype::left $col1 $ch1][a]$a1[overtype::left $col2 $ch2][a]$c1[overtype::left $col3 $cmd1][a]$c2[overtype::left $col4 $cmd2][a]$c3[overtype::left $col5 $cmd3][a]$c4$cmd4[a]
lappend displaylist $a1[overtype::left $col1 $ch1[a]][a]$a1[overtype::left $col2 $ch2][a]$c1[overtype::left $col3 $cmd1][a]$c2[overtype::left $col4 $cmd2][a]$c3[overtype::left $col5 $cmd3][a]$c4$cmd4[a]
}
return [list_as_lines $displaylist]
@ -3043,8 +3061,11 @@ y" {return quirkykeyscript}
set nspathcommands [dict get $opts -nspathcommands]
# -- --- --- --- --- --- --- --- --- --- --- ---
set packagetails [list] ;#child namespaces which are an exact match for a package name
set packageprefixes [list] ;#child namespaces which are a prefix match for a package name - but not an exact match
#set location [nsprefix $fq_glob]
set commands [list]
set commands [list]
set nsglob [nsprefix $fq_glob]
set glob [nstail $fq_glob]
@ -3471,10 +3492,27 @@ y" {return quirkykeyscript}
# set childtailmatches [lsort $childtailmatches]
#}
set childtailmatches [lsort -dictionary $childtailmatches]
foreach ct $childtailmatches {
set fqchild [nsjoin $location $ct]
set searchname [string trimleft $fqchild :]
foreach pkgname [lsearch -all -inline [package names] $searchname*] {
if {$pkgname eq $searchname} {
#exact match.
lappend packagetails $ct
} else {
if {[string match ${searchname}::* $pkgname]} {
#prefix match - but not exact match
lappend packageprefixes $ct
}
}
}
}
set nsdict [dict create\
location $location\
children $childtailmatches\
packagetails $packagetails\
packageprefixes $packageprefixes\
commands $commands\
procs $procs\
exported $exported\
@ -4807,7 +4845,8 @@ y" {return quirkykeyscript}
set scriptcmd [dict get $scriptinfo which]
set scriptargs [lrange $origin 1 end]
#ledit args -1 -1 {*}$scriptargs ;#prepend
set args [linsert $args 1 {*}$scriptargs]
#set args [linsert $args 1 {*}$scriptargs]
ledit args 1 -1 {*}$scriptargs ;#insert scriptargs before arg at index 1
#JJJ review
#set resolvedargs $scriptargs
punk::args::update_definitions [list [namespace qualifiers $scriptcmd]]
@ -5240,7 +5279,7 @@ y" {return quirkykeyscript}
the synopsis for that form.
"
@opts
-form -type string -default * -help\
-form -type number|name -default * -help\
"Ordinal index or name of command form."
-return -type string -default full -choices {full summary dict}
@values -min 1 -max -1
@ -5291,7 +5330,7 @@ y" {return quirkykeyscript}
full - summary {
set resultstr ""
foreach synline [split $syn \n] {
if {[string range $synline 0 1] eq "# "} {
if {[string range $synline 0 1] in {"# " "##"}} {
append resultstr $synline \n
} else {
#puts stderr [textblock::frame $syn]
@ -5447,9 +5486,9 @@ y" {return quirkykeyscript}
}
if {$opt_grepstr ne ""} {
if {[llength $opt_grepstr] == 1} {
set result [punk::ansi::grepstr --ignore-case -returnlines all [lindex $opt_grepstr 0] $result]
set result [punk::ansi::grepstr --ignore-case -return all [lindex $opt_grepstr 0] $result]
} else {
set result [punk::ansi::grepstr --ignore-case -returnlines all -highlight [lrange $opt_grepstr 1 end] [lindex $opt_grepstr 0] $result]
set result [punk::ansi::grepstr --ignore-case -return all -highlight [lrange $opt_grepstr 1 end] [lindex $opt_grepstr 0] $result]
}
}
return $result
@ -5529,9 +5568,9 @@ y" {return quirkykeyscript}
}
if {$opt_grepstr ne ""} {
if {[llength $opt_grepstr] == 1} {
set result [punk::ansi::grepstr --ignore-case -returnlines all [lindex $opt_grepstr 0] $result]
set result [punk::ansi::grepstr --ignore-case -return all [lindex $opt_grepstr 0] $result]
} else {
set result [punk::ansi::grepstr --ignore-case -returnlines all -highlight [lrange $opt_grepstr 1 end] [lindex $opt_grepstr 0] $result]
set result [punk::ansi::grepstr --ignore-case -return all -highlight [lrange $opt_grepstr 1 end] [lindex $opt_grepstr 0] $result]
}
}
return $result
@ -6674,7 +6713,7 @@ y" {return quirkykeyscript}
set body [punk::ansi::grepstr -return all -highlight term-teal {^(\{)|[^\\](\{+)} $body]
set body [punk::ansi::grepstr -return all -highlight term-teal {[^\\](\}+)} $body]
set body [punk::ansi::grepstr -return all -highlight term-orange {\[|\]} $body]
set body [punk::ansi::grepstr -return all -highlight term-orange1 {\[|\]} $body]
}
default {
set is_highlighted 0

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

@ -1078,7 +1078,8 @@ namespace eval punk::repl::class {
# incr nextrow -1
#}
#set o_rendered_lines [linsert $o_rendered_lines $cursor_row_idx ""]
ledit o_rendered_lines $cursor_row_idx $cursor_row_idx-1 ""
#ledit o_rendered_lines $cursor_row_idx $cursor_row_idx-1 ""
ledit o_rendered_lines $cursor_row_idx -1 ""
set o_cursor_col 1
}
@ -1151,7 +1152,9 @@ namespace eval punk::repl::class {
lappend o_rendered_lines ""
set activeline ""
}
lset o_rendered_lines $cursor_row_idx $result
#JULZ
#lset o_rendered_lines $cursor_row_idx $result
lset o_rendered_lines $cursor_row_idx $result\x1b[m
incr i
}
@ -1289,7 +1292,9 @@ namespace eval punk::repl::class {
set charhighlight [punk::ansi::a+ reverse]$char_at_cursor[a]
}
set cursorline [overtype::renderline -transparent 1 -insert_mode 0 -expand_right 0 $cursorline $prefix$charhighlight$suffix]
lset lines $o_cursor_row-1 $cursorline
#JULZ
#lset lines $o_cursor_row-1 $cursorline
lset lines $o_cursor_row-1 $cursorline\x1b[m
}
set numcol "$ANSI_linenum[join $nums \n][a]"
@ -1765,7 +1770,7 @@ proc punk::repl::console_debugview {editbuf consolewidth args} {
set patch_height [expr {2 + $debug_height + 2}]
set spacepatch [textblock::block $debug_width $patch_height " "]
#puts -nonewline [punk::ansi::cursor_off]
punk::console::cursor_off
#punk::console::cursor_off
#use non cursorsave versions - cursor save/restore will interfere with any concurrent ansi rendering that uses save/restore - because save/restore is a single item, not a stack.
set debug_offset [expr {$consolewidth - $debug_width - $opt_rightmargin}]
set row_clear [expr {$opt_row -2}]
@ -1773,7 +1778,7 @@ proc punk::repl::console_debugview {editbuf consolewidth args} {
punk::console::move_emitblock_return $opt_row $debug_offset $info
set topleft [list $debug_offset $opt_row] ;#col,row REVIEW
#puts -nonewline [punk::ansi::cursor_on]
punk::console::cursor_on
#punk::console::cursor_on
flush stdout
return [dict create width $debug_width height $debug_height topleft $topleft]
@ -2000,8 +2005,12 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
#if {$chunk eq "\x1b\[C"} {
#}
punk::console::cursor_off
flush stdout
$editbuf add_chunk $chunk
#--------------------------
# editbuf and debugview rhs frames
#for now disable entirely on vt52 - we can only do cursor save restore - nothing that requires responses on stdin (?)
@ -2058,7 +2067,9 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
flush stdout
#move_column is more efficient than move since it doesn't require a response on stdin to determine current column,
#but doesn't seem to be universally supported (kermit95 vt modes for example)
#the Horizontal Position Absolute sequence ESC \[ n ` seems to be a possible alternative.
set leftmargin 3
if {!$is_vt52} {
puts -nonewline stdout [a+ cyan][punk::ansi::move_column [expr {$leftmargin +1}]][punk::ansi::erase_eol][$editbuf line $cursor_row][a][punk::ansi::move_column [expr {$leftmargin + [$editbuf cursor_column]}]]
@ -2089,6 +2100,9 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
lappend input_chunks_waiting($inputchan) $waiting
}
}
punk::console::cursor_on
flush stdout
if {$editbuf_linenum_submitted == 0} {
#(there is no line 0 - lines start at 1)
if {[$editbuf last_char] eq "\n"} {
@ -2685,8 +2699,10 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
#editbuf
#----------------------------------------------------------------------------
#after any external command - raw mode as the console sees it can be disabled
#set it to match current state of the tsv
#----------------------------------------------------------------------------
if {[tsv::get console is_raw]} {
if {$::tcl_platform(platform) eq "windows"} {
#review
@ -2696,22 +2712,24 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
set sinfo [chan configure stdin]
if {[dict exists $sinfo -inputmode]} {
if {[dict get $sinfo -inputmode] ne "raw"} {
set re_enable_required 1
set re_enable_raw_required 1
} else {
set re_enable_required 0
set re_enable_raw_required 0
}
} else {
# -inputmode unavailable
#tcl 8.6 doesn't have -inputmode - meaning it has to call punk:console::enableRaw each time
#enableRaw on windows without twapi involves launching a pwsh process - which gives a noticeable lag in keyboard input.
#enableRaw on Unix involves a call to stty - which is generally fast - but still to be avoided if not required.
set re_enable_required 1
set re_enable_raw_required 1
}
#puts stderr "-here- re-enabling raw"
if {$re_enable_required} {
if {$re_enable_raw_required} {
punk::console::enableRaw
}
}
#----------------------------------------------------------------------------
} else {
#append commandstr \n
if {$::punk::repl::signal_control_c} {
@ -3801,7 +3819,8 @@ namespace eval repl {
#puts stderr [thread::id]
if {[llength $::codethread_initstatus] == 1} {
set ::codethread_initstatus [linsert $::codethread_initstatus 0 ok]
#set ::codethread_initstatus [linsert $::codethread_initstatus 0 ok]
ledit ::codethread_initstatus 0 -1 ok
}
thread::id
}

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

@ -249,7 +249,7 @@ namespace eval punk::repo {
@form -form "parsed"
${[punk::repo::get_fossil_subcommand_usage add]}
@form -form "raw" -synopsis "exec fossil add ?OPTIONS? FILE1 ?FILE2 ...?"
@form -form "raw" -synopsis "exec fossil add \[OPTIONS\] FILE1 \[FILE2\]..."
@formdisplay -header "fossil help add" -body {${[runout -n fossil help add]}}
} ""]
@ -263,7 +263,7 @@ namespace eval punk::repo {
@form -form "parsed"
${[punk::repo::get_fossil_subcommand_usage diff]}
@form -form "raw" -synopsis "exec fossil diff ?OPTIONS? FILE1 ?FILE2 ...?"
@form -form "raw" -synopsis "exec fossil diff \[OPTIONS\] FILE1 \[FILE2\]..."
@formdisplay -header "fossil help diff" -body {${[runout -n fossil help diff]}}
} ""]

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

@ -324,7 +324,7 @@ namespace eval punkcheck {
lappend record_list $o_fileset_record
} else {
#set record_list [linsert $record_list[unset record_list] $oldposition $o_fileset_record]
ledit record_list $oldposition $oldposition-1 $o_fileset_record
ledit record_list $oldposition -1 $o_fileset_record
}
if {$o_operation ne "QUERY"} {
punkcheck::save_records_to_file $record_list $punkcheck_file
@ -796,7 +796,7 @@ namespace eval punkcheck {
lappend record_list $file_record
} else {
#set record_list [linsert $record_list[unset record_list] $oldposition $file_record]
ledit record_list $oldposition $oldposition-1 $file_record
ledit record_list $oldposition -1 $file_record
}
save_records_to_file $record_list $punkcheck_file

42
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/shellfilter-0.2.1.tm

@ -755,6 +755,8 @@ namespace eval shellfilter::chan {
#puts stdout "===[ansistring VIEW -lf 1 $o_buffered]"
set buf $o_buffered$chunk
set emit ""
#Note 8-bit csi \x9b has already been mapped in the chunk to 7-bit form \x1b\[ by the caller - so we only need to check for \x1b here
#(under review - ideally we might not want to normalize 8-bit to 7-bit in a channel transform))
if {[string last \x1b $buf] >= 0} {
#detect will detect ansi SGR and gron groff and other codes
#REVIEW - ta::detect won't detect SOS without paired ST for things like PM
@ -798,18 +800,21 @@ namespace eval shellfilter::chan {
] $c1c2] 0 3]
switch -- $leadernorm {
7CSI - 8CSI {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set o_codestack [list "\x1b\[m"]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set o_codestack [list $code]
} elseif {[punk::ansi::codetype::is_sgr $code]} {
#todo - make caching is_sgr method
set dup_posns [lsearch -all -exact $o_codestack $code]
set o_codestack [lremove $o_codestack {*}$dup_posns]
lappend o_codestack $code
} else {
set code_endswith_m [expr {[tcl::string::index $code end] eq "m"}]
if {$code_endswith_m} {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
#review this normalizing of reset to a single form.
set o_codestack [list "\x1b\[m"]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set o_codestack [list $code]
} elseif {[punk::ansi::codetype::is_sgr $code]} {
#todo - make caching is_sgr method
set dup_posns [lsearch -all -exact $o_codestack $code]
set o_codestack [lremove $o_codestack {*}$dup_posns]
lappend o_codestack $code
}
}
}
7GFX {
switch -- [tcl::string::index $code 2] {
@ -1029,6 +1034,21 @@ namespace eval shellfilter::chan {
return ""
}
}
#------------------------------------------------------
# REVIEW
#Trackcodes logic is primarily designed for 7-bit codes
#It would be complex for it to support 8-bit as well
#- we can do a simple pre-map to convert 8-bit CSI to 7-bit CSI before processing
#we already normalize things like resets to a single 7-bit form anyway.
#review - is there a need for an ansiwrap channel that preserves 8-bit codes?
#8-bit are rarely used these days - and many terminals don't support them.
#We could take the view here that we should understand them but not emit them in general.
#Nonetheless - converting them on a channel transform like this is potentially suprising in some circumstances,
#and we don't necessarily know the intent of both the producer and consumer of the stream.
set stringdata [string map [list \x9b \x1b\[ ] $stringdata]
#------------------------------------------------------
set streaminfo [my Trackcodes $stringdata]
set emit [dict get $streaminfo emit]

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

@ -2116,7 +2116,7 @@ tcl::namespace::eval textblock {
set ansibase_header [tcl::dict::get $o_opts_table -ansibase_header] ;#merged to single during configure
set ansiborder_header [tcl::dict::get $o_opts_table -ansiborder_header]
if {[tcl::dict::get $o_opts_table -frametype_header] eq "block"} {
set extrabg [punk::ansi::codetype::sgr_merge_singles [list $ansibase_header] -filter_fg 1]
set extrabg [punk::ansi::codetype::sgr_merge_singles [list $ansibase_header] -filter_fg]
set ansiborder_final $ansibase_header$ansiborder_header$extrabg
} else {
set ansiborder_final $ansibase_header$ansiborder_header
@ -2504,7 +2504,7 @@ tcl::namespace::eval textblock {
if {[tcl::dict::get $o_opts_table -frametype] eq "block"} {
#block is the only style where bg colour can fill the frame content area exactly if the L-shaped border elements are styled
#we need to only accept background ansi codes from the columndef ansibase for this
set col_bg [punk::ansi::codetype::sgr_merge_singles [list $opt_col_ansibase] -filter_fg 1] ;#special merge for block borders - don't override fg colours
set col_bg [punk::ansi::codetype::sgr_merge_singles [list $opt_col_ansibase] -filter_fg] ;#special merge for block borders - don't override fg colours
set border_ansi $body_ansibase$body_ansiborder$col_bg
} else {
set border_ansi $body_ansibase$body_ansiborder
@ -2520,7 +2520,7 @@ tcl::namespace::eval textblock {
set row_bg ""
set row_ansibase [tcl::dict::get $o_rowdefs $r -ansibase]
if {$row_ansibase ne ""} {
set row_bg [punk::ansi::codetype::sgr_merge_singles [list $row_ansibase] -filter_fg 1]
set row_bg [punk::ansi::codetype::sgr_merge_singles [list $row_ansibase] -filter_fg]
}
#todo - joinleft,joinright,joindown based on opts in args
@ -2542,8 +2542,8 @@ tcl::namespace::eval textblock {
lappend ptlens [string length $pt]
}
#set takebg [lindex $parts end-1]
#set cell_bg [punk::ansi::codetype::sgr_merge_singles [list $takebg] -filter_fg 1]
set cell_bg [punk::ansi::codetype::sgr_merge_singles $codes -filter_fg 1 -filter_reset 1]
#set cell_bg [punk::ansi::codetype::sgr_merge_singles [list $takebg] -filter_fg]
set cell_bg [punk::ansi::codetype::sgr_merge_singles $codes -filter_fg -filter_reset]
#puts --->[ansistring VIEW $codes]
if {[punk::ansi::codetype::is_sgr_reset [lindex $codes end-1]]} {
@ -2554,7 +2554,7 @@ tcl::namespace::eval textblock {
set ansibase ""
set row_ansibase ""
if {$ftblock} {
set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_body_col_row] -filter_bg 1]
set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_body_col_row] -filter_bg]
set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_final $cell_bg]]
}
set cell_ansibase $cell_ansi_tail
@ -2577,7 +2577,7 @@ tcl::namespace::eval textblock {
# set ansibase ""
# set row_ansibase ""
# if {$ftblock} {
# set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_body_col_row] -filter_bg 1]
# set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_body_col_row] -filter_bg]
# }
# set cell_ansibase $cell_ansi_tail
# } else {
@ -2643,7 +2643,7 @@ tcl::namespace::eval textblock {
}
#return empty (zero content height) row if no rows
if {![llength $cells]} {
set basebg [punk::ansi::codetype::sgr_merge_singles [list $body_ansibase] -filter_fg 1]
set basebg [punk::ansi::codetype::sgr_merge_singles [list $body_ansibase] -filter_fg]
set ansiborder_final [punk::ansi::codetype::sgr_merge [list $basebg $body_ansiborder]]
set joins [lremove $joins [lsearch $joins down*]]
@ -4497,7 +4497,7 @@ tcl::namespace::eval textblock {
foreach {pt code} [lrange $parts 2 end] {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
#set parts [linsert $parts $code_idx+1 $base]
ledit parts $code_idx+1 $code_idx $base
ledit parts $code_idx+1 -1 $base
}
incr code_idx 2
}
@ -4527,8 +4527,9 @@ tcl::namespace::eval textblock {
}
}
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set parts [linsert $parts [expr {$code_idx+1+$offset}] $base]
#set parts [linsert $parts [expr {$code_idx+1+$offset}] $base]
#ledit parts [expr {$code_idx+1+$offset}] $code_idx+$offset $base
ledit parts [expr {$code_idx+1+$offset}] -1 $base
incr offset
}
incr code_idx 2
@ -4912,7 +4913,8 @@ tcl::namespace::eval textblock {
set colour2 [tcl::string::map [list rainbow [lindex $rainbow_list $i]] $colour]
set ansi [a+ {*}$colour2]
set ansicode [punk::ansi::codetype::sgr_merge_list "" $ansi]
#set ansicode [punk::ansi::codetype::sgr_merge_list "" $ansi]
set ansicode [punk::ansi::codetype::sgr_merge [list $ansi]]
lappend clist ${ansicode}$c$RST
}
if {$noreset} {
@ -4926,8 +4928,9 @@ tcl::namespace::eval textblock {
set block ""
for {set r 0} {$r < $size} {incr r} {
set colour2 [tcl::string::map [list rainbow [lindex $rainbow_list $r]] $colour]
set ansi [a+ {*}$colour2]
set ansicode [punk::ansi::codetype::sgr_merge_list "" $ansi]
set ansi [a+ {*}$colour2] ;#not always a single SGR sequence (ESC...m) e.g when contains 'underdotted'
#set ansicode [punk::ansi::codetype::sgr_merge_list "" $ansi]
set ansicode [punk::ansi::codetype::sgr_merge [list $ansi]]
set row "$ansicode"
foreach c $charsubset {
append row $c
@ -5393,10 +5396,11 @@ tcl::namespace::eval textblock {
}
r-1 {
if {[lindex $line_chunks end] eq ""} {
#Insert so that pad *ends* up at position end-2
set line_chunks [linsert $line_chunks end-2 $pad]
#breaks layout e.g subtables in: i i
#why?
#ledit line_chunks end-2 end-3 $pad
#Note that 'ledit line_chunks end-2 -1 $pad' is not equivalent,
#because linsert behaves differently depending on whether the index is start-relative or end-relative.
#(breaks layout e.g subtables in: i i)
} else {
lappend line_chunks $pad
}
@ -5487,6 +5491,9 @@ tcl::namespace::eval textblock {
r-2 {
if {[lindex $line_chunks end] eq ""} {
set line_chunks [linsert $line_chunks end-2 $pad]
#(ledit line_chunks end-2 -1 $pad) is not equivalent to linsert
#because of the different behaviour of end-relative vs start-relative indices with linsert
#- it can break layout e.g subtables in: i i
} else {
lappend line_chunks $pad
}

1
src/vendormodules/include_modules.config

@ -27,6 +27,7 @@ set local_modules [list\
c:/repo/jn/tclmodules/pattern/modules pattern::IPatternInterface\
c:/repo/jn/tclmodules/pattern/modules pattern::IPatternSystem\
c:/repo/jn/tclmodules/pattern/modules test::pattern\
c:/repo/jn/tclmodules/voo/modules voo\
c:/repo/jn/tarjar/modules tarjar\
]

764
src/vendormodules/voo-1.0.0.tm

@ -0,0 +1,764 @@
namespace eval voo {
# package version
variable version 1.0.0
variable handlerToObjectMap {}
variable handlerCounter 0
##\brief Check if a namespace is a valid voo class
# \param[in] namespaceName the namespace to check
# \return 1 if valid voo class, 0 otherwise
proc isVooClass {namespaceName} {
if {![uplevel [list namespace exists $namespaceName]]} {
return 0
}
return [expr {[uplevel [list namespace eval $namespaceName {
info exists __defaultObj
}]]}]
}
##\brief Declare a new voo class namespace and process its class body
# \param[in] args Arguments for class declaration: <className> <body> and optional -extends parent
# \note Creates the class namespace, imports parent fields/methods when using -extends,
# and registers constructors and exports
proc class {args} {
set optDict {}
set defaultArgs {}
set numArgs [llength $args]
for {set i 0} {$i < $numArgs} {incr i} {
set arg [lindex $args $i]
if {$arg eq "-extends"} {
if {$i + 1 >= $numArgs} {
error "Constructor option ’$arg’ requires an argument"
}
dict set optDict $arg [lindex $args [incr i]]
} elseif {$arg eq "-virtual" || $arg eq "-v"} {
dict set optDict "-virtual" {}
} else {
lappend defaultArgs $arg
}
}
lassign $defaultArgs className body
set vooNs [namespace current]
# create the namespace for the class
uplevel [list namespace eval $className [subst -nocommands {
namespace path [list $vooNs]
variable __defaultObj {}
variable __fields {}
variable __tmp_isPublicEnabled 1
}]]
uplevel [list namespace eval $className {
##\brief Access default object for this class
# \return Default class instance (list)
# \note Used for inheritance and constructor defaults
proc class.defaultObj {} {
variable __defaultObj
return $__defaultObj
}
##\brief Get list of field names for this class
# \return List of field names in declaration order
# \note Useful for introspection and constructor -name new.args
proc class.fields {} {
variable __fields
return $__fields
}
}]
if {[dict exists $optDict -virtual] && [dict exists $optDict -extends]} {
error "voo::class: cannot use -virtual with -extends; child classes inherit virtual automatically from a -virtual parent"
}
if {[dict exists $optDict -virtual]} {
set normalizedClassName [uplevel [list namespace eval $className {namespace current}]]
uplevel [list namespace eval $className [list variable __voo_is_virtual_class 1]]
uplevel [list namespace eval $className [list variable __voo_class_namespace $normalizedClassName]]
# Pre-populate __defaultObj with namespace tag at index 0 BEFORE field declarations
# so that _getClassCurrNumFields returns 1 for the first field declared
uplevel [list namespace eval $className [list set __defaultObj [list $normalizedClassName]]]
}
#81
# variable __parentClassNamespace {}
if {[dict exists $optDict -extends]} {
set parentClassName [dict get $optDict -extends]
if {![uplevel [list namespace exists $parentClassName]]} {
error "Parent class ’$parentClassName’ does not exist."
}
# check if parent class exists
if {![uplevel [list namespace eval $parentClassName {info exists __defaultObj}]]} {
error "Parent class ’$parentClassName’ is not a valid voo class."
}
# normalize namespace name of parent class
set parentClassName [uplevel [list namespace eval $parentClassName {
namespace current
}]]
uplevel [list namespace eval $className [subst -nocommands {
variable __parentClassNamespace $parentClassName
}]]
# import parent’s default object values
set parentDefaultObj [${parentClassName}::class.defaultObj]
uplevel [list namespace eval $className [list set __defaultObj $parentDefaultObj]]
# if parent is virtual, update namespace tag at index 0 to child’s namespace
set parentIsVirtual [uplevel [list namespace eval $parentClassName {info exists __voo_is_virtual_class}]]
if {$parentIsVirtual} {
set normalizedChildName [uplevel [list namespace eval $className {namespace current}]]
uplevel [list namespace eval $className \
[list set __defaultObj [lreplace $parentDefaultObj 0 0 $normalizedChildName]]]
uplevel [list namespace eval $className [list variable __voo_is_virtual_class 1]]
uplevel [list namespace eval $className [list variable __voo_class_namespace $normalizedChildName]]
}
# 121
# import parent’s field index variables by copying actual index values from parent
set parentFields [${parentClassName}::class.fields]
foreach field $parentFields {
set fieldIdx [uplevel [list namespace eval $parentClassName [list set $field]]]
uplevel [list namespace eval $className [list variable $field $fieldIdx]]
uplevel [list namespace eval $className [list lappend __fields $field]]
}
# import parent’s acessors in child class with namespace import
uplevel [list namespace eval $className [subst -nocommands {
namespace import ${parentClassName}::get.*
namespace import ${parentClassName}::set.*
namespace import ${parentClassName}::update.*
}]]
}
# 136
uplevel [list namespace eval $className $body]
uplevel [list namespace eval $className {
if {[info commands new] eq ""} {
constructor
}
if {[info commands new()] eq ""} {
constructor -noargs [_buildConstructorNoArgsBody]
}
if {[info commands new.args] eq ""} {
constructor -name new.args {args} [_buildConstructorArgsBody]
}
}]
# 151
uplevel [list namespace eval $className {
# export class methods
namespace export *
}]
uplevel [list namespace eval $className {
# clean temporary variable
unset __tmp_isPublicEnabled
}]
return
}
# 161
##\brief Return the default value for a given field type
# \param[in] type the field type token (double,int,bool,...)
# \return The default value appropriate for the type
proc _getDefaultValueByType {type} {
switch -- $type {
double { return 0.0 }
int { return 0 }
bool { return 0 }
default { return {} }
}
}
##\brief Get the current number of fields declared in the current class
# \return Number of fields (integer)
proc _getClassCurrNumFields {} {
return [uplevel 2 {llength $__defaultObj}]
}
##\brief Check whether public mode is enabled during class body parsing
# \return 1 if public mode is enabled, 0 otherwise
proc _getClassIsPublicEnabled {} {
return [uplevel 2 {set __tmp_isPublicEnabled}]
}
##\brief Declare getter/setter/updater accessors for a class field
# \param[in] fieldName name of the field
# \param[in] isPublic boolean whether accessors are public
# \param[in] isStatic boolean whether field is static (class-level)
proc _declareFieldAcessors {fieldName isPublic isStatic} {
set prefix {}
if {$isStatic} {
append prefix class.
}
if {!$isPublic} {
append prefix my.
}
set getterName "${prefix}get.$fieldName"
set setterName "${prefix}set.$fieldName"
set updaterName "${prefix}update.$fieldName"
if {$isStatic} {
uplevel 2 [list proc $getterName {} [subst -nocommands {
variable $fieldName
return $$fieldName
}]]
uplevel 2 [list proc $setterName {value} [subst -nocommands {
variable $fieldName
set $fieldName "\$value"
}]]
uplevel 2 [list proc $updaterName {tempVar body} [subst -nocommands {
variable $fieldName
upvar "\$tempVar" temp
set temp $$fieldName
# break link with class variable to avoid copy-on-write
set $fieldName {}
try {
uplevel \$body
} finally {
set $fieldName "\$temp"
}
}]]
} else {
uplevel 2 [list getter $getterName $fieldName]
uplevel 2 [list setter $setterName $fieldName]
uplevel 2 [list updater $updaterName $fieldName]
}
return
}
##\brief Validate a field name for illegal characters
# \param[in] fieldName the field name to validate
# \return Raises an error if invalid
proc _validateFieldName {fieldName} {
if {[string first "." $fieldName] != -1 || [string first "::" $fieldName] != -1} {
error "Field name ’$fieldName’ cannot contain ’.’ or ’::’ substrings."
}
}
##\brief Ensure a field name does not already exist in the class
# \param[in] fieldName the field name to check
# \return Raises an error if the field already exists
# \note Uses __fields for instance fields and fully-qualified namespace lookup for static
# fields to avoid false positives from global variables with the same name
proc _validateFieldDoesNotExist {fieldName} {
# Check instance fields tracked in __fields (class-scoped, no global bleed)
if {$fieldName in [uplevel 2 {set __fields}]} {
error "Field name ’$fieldName’ already exists in the class."
}
# Check static fields via fully-qualified namespace variable; info exists ::Ns::var
# only matches that exact namespace variable, never a same-named global
set classNs [uplevel 2 {namespace current}]
if {[info exists ${classNs}::$fieldName]} {
error "Field name ’$fieldName’ already exists in the class."
}
}
##\brief Validate a variable initial value according to its declared type
# \param[in] type the declared type (double,int,bool,list,dict)
# \param[in] value the value to validate
# \return Raises an error if the value does not match the type
proc _validateVarValueByType {type value} {
switch -- $type {
double {
if {[string is double -strict $value] == 0} {
error "Value for t_double must be a double, got ’$value’"
}
}
int {
if {[string is integer -strict $value] == 0} {
error "Value for t_int must be an integer, got ’$value’"
}
}
bool {
if {[string is boolean -strict $value] == 0} {
error "Value for t_bool must be a boolean, got ’$value’"
}
}
list {
if {[catch {llength $value}]} {
error "Value for t_list must be a list, got ’$value’"
}
}
dict {
if {[catch {dict size $value}]} {
error "Value for t_dict must be a dict, got ’$value’"
}
}
}
}
##\brief Declare a field variable inside the class body
# \param[in] type the field type token (double,int,string,bool,list,dict,obj)
# \param[in] argList arguments: ?-static? <name> ?<initialValue>?
proc _var {type argList} {
set defaultArgs {}
set optDict {}
set numArgs [llength $argList]
for {set i 0} {$i < $numArgs} {incr i} {
set arg [lindex $argList $i]
if {$arg eq "-static"} {
dict set optDict $arg {}
} else {
lappend defaultArgs $arg
}
}
if {[llength $defaultArgs] == 0} {
error "Variable definition requires: ?<option>? <name> ?<initialValue>?"
}
if {[llength $defaultArgs] == 2} {
lassign $defaultArgs name initVal
} else {
lassign $defaultArgs name
set initVal [_getDefaultValueByType $type]
}
_validateFieldName $name
_validateFieldDoesNotExist $name
_validateVarValueByType $type $initVal
if {[dict exists $optDict -static]} {
# static field
uplevel [list variable $name $initVal]
} else {
set currNumFields [_getClassCurrNumFields]
uplevel [list variable $name $currNumFields]
uplevel [list lappend __defaultObj $initVal]
uplevel [list lappend __fields $name]
}
set isPublicEnabled [_getClassIsPublicEnabled]
_declareFieldAcessors $name $isPublicEnabled [dict exists $optDict -static]
return
}
# 341
##\brief Declare a double-typed field
# \param[in] args same arguments accepted by _var (name and optional initial value)
proc double_t {args} {
uplevel [list _var "double" $args]
}
##\brief Declare an integer-typed field
# \param[in] args same arguments accepted by _var (name and optional initial value)
proc int_t {args} {
uplevel [list _var "int" $args]
}
##\brief Declare a string-typed field
# \param[in] args same arguments accepted by _var (name and optional initial value)
proc string_t {args} {
uplevel [list _var "string" $args]
}
##\brief Declare a boolean-typed field
# \param[in] args same arguments accepted by _var (name and optional initial value)
proc bool_t {args} {
uplevel [list _var "bool" $args]
}
##\brief Declare a list-typed field
# \param[in] args same arguments accepted by _var (name and optional initial value)
proc list_t {args} {
uplevel [list _var "list" $args]
}
##\brief Declare a dict-typed field
# \param[in] args same arguments accepted by _var (name and optional initial value)
proc dict_t {args} {
uplevel [list _var "dict" $args]
}
##\brief Declare an object-typed field (nested vanilla object)
# \param[in] args same arguments accepted by _var (name and optional initial value)
proc obj_t {args} {
uplevel [list _var "object" $args]
}
# 386
##\brief Enable public mode for declarations inside the provided body
# \param[in] body script to execute with public accessors enabled
# \return Result of executing body
proc public {body} {
uplevel $body
}
##\brief Execute the provided body with private mode enabled (temporarily disables public accessors)
# \param[in] body script to execute with private accessors
# \return Result of executing body
proc private {body} {
uplevel {variable __tmp_isPublicEnabled 0}
try {
uplevel $body
} finally {
uplevel {variable __tmp_isPublicEnabled 1}
}
}
##\brief Build the body for a no-argument constructor
# \return A script chunk used as constructor body that returns the class default object
proc _buildConstructorNoArgsBody {} {
return {
variable __defaultObj
return $__defaultObj;
}
}
##\brief Build the body for a constructor that accepts named args (-field value pairs)
# \return A script chunk used as constructor body that applies named arguments to the default object
proc _buildConstructorArgsBody {} {
return {
variable __defaultObj
set obj $__defaultObj
if {[catch {dict size $args}]} {
error "Constructor argument must be a list of ’-<field> <value>’ pairs"
}
dict for {key value} $args {
if {[string index $key 0] ne "-"} {
error "Constructor argument keys must start with ’-’, got ’$key’"
}
set field [string range $key 1 end]
set setter set.$field
if {[info commands $setter] ne ""} {
$setter obj $value
} else {
set setter my.set.$field
if {[info commands $setter] ne ""} {
$setter obj $value
} else {
error "Unknown field option: $field"
}
}
}
return $obj
}
}
##\brief Build constructor parameter list and body for positional constructors
# \return A list of two elements: argument names list and a body script that returns them as a list
# \note For virtual classes, the concrete class namespace is embedded as a literal string at
# class-definition time (not looked up at runtime), producing:
# return [list ::ClassName $f1 $f2 ...]
# This avoids all runtime proc calls (class.defaultObj, set.*) and variable lookups,
# making virtual object creation as cheap as non-virtual.
proc _buildConstructorParams {} {
set argList [uplevel 2 {set __fields}]
set isVirtual [uplevel 2 {info exists __voo_is_virtual_class}]
set spacedArgVarListStr {}
foreach arg $argList {
append spacedArgVarListStr "\$$arg "
}
if {$isVirtual} {
# Read the normalized class namespace at definition time so subst embeds it
# as a literal in the generated body - no runtime variable lookup required.
set classNs [uplevel 2 {set __voo_class_namespace}]
set spacedArgVarListStr "{$classNs} $spacedArgVarListStr"
set body [subst -nocommands {
return [list $spacedArgVarListStr]
}]
} else {
set body [subst -nocommands {
return [list $spacedArgVarListStr]
}]
}
return [list $argList $body]
}
##\brief Define a constructor for the current class
# \param[in] args Constructor declaration options and body
# \note Supports -name, -noargs and -typed variants
proc constructor {args} {
set defaultArgs {}
set optDict {}
set numArgs [llength $args]
for {set i 0} {$i < $numArgs} {incr i} {
set arg [lindex $args $i]
if {$arg eq "-name" || $arg eq "-noargs" || $arg eq "-typed"} {
if {$i + 1 >= $numArgs} {
error "Constructor option ’$arg’ requires an argument"
}
dict set optDict $arg [lindex $args [incr i]]
} else {
lappend defaultArgs $arg
}
}
# check valid option combinations
if {[dict exists $optDict -name]} {
if {[dict exists $optDict -noargs] || [dict exists $optDict -typed]} {
error "Constructor cannot have -name option with -noargs or -typed options"
}
}
if {[dict exists $optDict -noargs] && [dict exists $optDict -typed]} {
error "Constructor cannot have both -noargs and -typed options"
}
if {[dict exists $optDict -name]} {
set constructorName [dict get $optDict -name]
} elseif {[dict exists $optDict -noargs]} {
set constructorName "new()"
} elseif {[dict exists $optDict -typed]} {
set constructorName "new([join [dict get $optDict -typed] ,])"
} else {
set constructorName "new"
}
if {[dict exists $optDict -noargs]} {
if {[llength $defaultArgs] != 0} {
error "Invalid constructor definition, expected ’?...? ?<body>?’ for -noargs"
}
set argList {}
set body [dict get $optDict -noargs]
} else {
if {[llength $defaultArgs] == 0} {
lassign [_buildConstructorParams] argList body
} else {
if {[llength $defaultArgs] != 2} {
error "Invalid constructor definition, expected ’?...? ?<argList> <body>?’"
}
lassign $defaultArgs argList body
}
}
uplevel [list proc $constructorName $argList $body]
return
}
# 531
##\brief Generate a getter procedure for a field
# \param[in] methodName name of the generated getter (may include namespace prefix)
# \param[in] fieldName name of the field to read
proc getter {methodName fieldName} {
# implementation of getter definition
set fieldIdx [uplevel [list set $fieldName]]
uplevel [subst -nocommands {
##\\brief Getter for $fieldName
# \\param\[in\] this class instance
# \\return $fieldName value
proc $methodName {this} {
return [lindex \$this $fieldIdx]
}
}]
return
}
##\brief Generate a setter procedure for a field
# \param[in] methodName name of the generated setter (may include namespace prefix)
# \param[in] fieldName name of the field to write
proc setter {methodName fieldName} {
# implementation of setter definition
set fieldIdx [uplevel [list set $fieldName]]
uplevel [subst -nocommands {
##\\brief Setter for $fieldName
# \\param\[in\] thisVar name of variable containing class instance
# \\param\[in\] value new value for $fieldName
proc $methodName {thisVar value} {
upvar \$thisVar this
lset this $fieldIdx \$value
}
}]
return
}
##\brief Generate an updater procedure for a field (copy-on-write safe)
# \param[in] methodName name of the generated updater (may include namespace prefix)
# \param[in] fieldName name of the field to update by reference
# \note The updater detaches the field to avoid unnecessary copying during updates
proc updater {methodName fieldName} {
# implementation of updater definition
set fieldIdx [uplevel [list set $fieldName]]
uplevel [subst -nocommands {
##\\brief Update $fieldName by reference
# \\param\[in\] thisVar name of variable containing class instance
# \\param\[out\] tempVar name of variable to hold $fieldName during update
# \\param\[in\] body script to execute with $fieldName in tempVar
# \\note Avoids copy-on-write by detaching field during update
proc $methodName {thisVar tempVar body} {
upvar \$thisVar this
upvar \$tempVar temp
set temp [lindex \$this $fieldIdx]
# break link with object to avoid copy-on-write
lset this $fieldIdx {}
try {
uplevel \$body
} finally {
lset this $fieldIdx \$temp
}
}
}]
}
##\brief Declare a method in the current class namespace
# \param[in] args Method declaration arguments: name, argList, body and options (-static, -upvar, -update, -override)
proc method {args} {
set isPublicEnabled [_getClassIsPublicEnabled]
set defaultArgs {}
set optDict {}
set numArgs [llength $args]
for {set i 0} {$i < $numArgs} {incr i} {
set arg [lindex $args $i]
if {$arg eq "-static" || $arg eq "-upvar"} {
dict set optDict $arg {}
} elseif {$arg eq "-update"} {
if {$i + 1 >= $numArgs} {
error "Method option ’$arg’ requires an argument"
}
dict set optDict $arg [lindex $args [incr i]]
} elseif {$arg eq "-override"} {
# Explicit override indicator
dict set optDict $arg {}
} elseif {$arg eq "-virtual"} {
dict set optDict $arg {}
} else {
lappend defaultArgs $arg
}
}
lassign $defaultArgs name argList body
# check valid option combinations
if {[dict exists $optDict -static]} {
if {[dict exists $optDict -upvar] || [dict exists $optDict -update]} {
error "Method cannot have both -static and -upvar or -update options"
}
}
if {[dict exists $optDict -update]} {
if {![dict exists $optDict -upvar]} {
# automatically add -upvar if -update is specified
dict set optDict -upvar {}
}
}
set finalArgList {}
set finalBody {}
if {[dict exists $optDict -upvar]} {
lappend finalArgList "thisVar"
append finalBody {
upvar $thisVar this
}
} elseif {![dict exists $optDict -static]} {
lappend finalArgList "this"
}
lappend finalArgList {*}$argList
set className [uplevel {namespace current}]
if {[dict exists $optDict -update]} {
set updateFields [dict get $optDict -update]
if {[llength $updateFields] == 0} {
error "-update option requires at least one field name"
}
foreach field $updateFields {
try {
set fieldIdx [uplevel [list set $field]]
} trap {} {} {
error "Field ’$field’ specified in -update option does not exist in class ’$className’"
}
append finalBody [subst -nocommands {
set $field [lindex \$this $fieldIdx]
lset this $fieldIdx {}
}]
}
append finalBody "try \{"
}
append finalBody $body
if {[dict exists $optDict -update]} {
append finalBody "\} finally \{"
foreach field $updateFields {
set fieldIdx [uplevel [list set $field]]
append finalBody [subst -nocommands {
lset this $fieldIdx \$$field
}]
}
append finalBody "\}"
}
if {!$isPublicEnabled} {
set name "my.$name"
}
if {[dict exists $optDict -override]} {
set parentNs [uplevel {set __parentClassNamespace}]
if {[info commands "${parentNs}::$name"] eq ""} {
error "Method ’$name’ does not override any method in parent class ’$parentNs’"
}
# If parent’s method is virtual (has base.<name>), auto-promote this override
# to a dispatcher so that deep inheritance dispatch works correctly
if {[uplevel {info exists __voo_is_virtual_class}] && \
[info commands "${parentNs}::base.$name"] ne ""} {
dict set optDict -virtual {}
}
}
if {[dict exists $optDict -virtual]} {
if {![uplevel {info exists __voo_is_virtual_class}]} {
error "Method ’$name’ is declared -virtual but ’[uplevel {namespace current}]’ is not a virtual class"
}
if {[dict exists $optDict -upvar] || [dict exists $optDict -update] || [dict exists $optDict -static]} {
error "Method ’$name’ cannot combine -virtual with -upvar, -update, or -static"
}
# Register base.<name> with the original body for direct parent calls from subclasses
uplevel [list proc "base.$name" $finalArgList $finalBody]
# Build dispatch body: route to concrete class implementation at runtime
set dispatchBody "set __voo_cls \[lindex \$this 0\]\n"
append dispatchBody "if \{\$__voo_cls ne \[namespace current\] && \[info commands \${__voo_cls}::$name\] ne {}\} \{\n"
append dispatchBody " return \[\${__voo_cls}::$name \$this"
foreach arg $argList {
append dispatchBody " \$$arg"
}
append dispatchBody "\]\n\}\n"
append dispatchBody "return \[base.$name \$this"
foreach arg $argList {
append dispatchBody " \$$arg"
}
append dispatchBody "\]"
set finalBody $dispatchBody
}
uplevel [list proc $name $finalArgList $finalBody]
return
}
# 726
##\brief Import one or more methods from parent class into the current (child) class namespace.
# \param[in] methods List of method names (or a single method name) to import from parent.
# \note Must be called inside a class declared with -extends. Methods are copied at class-definition time.
proc importMethods {methods} {
set parentNs [uplevel {set __parentClassNamespace}]
# Validate caller context and get parent namespace stored by -extends handling
if {$parentNs eq ""} {
error "importMethods can only be used inside a class declared with -extends"
}
# Normalize to a list of method names
if {[string length [string trim $methods]] == 0} {
return
}
if {[catch {llength $methods}]} {
set methodList [list $methods]
} else {
set methodList $methods
}
foreach methodName $methodList {
set fullMethodName "${parentNs}::$methodName"
# Validate parent method exists
if {[info commands $fullMethodName] eq ""} {
error "Method ’$methodName’ not found in parent class ’$parentNs’"
}
# Define a copy in the child namespace so unqualified calls resolve to child
set argList [info args $fullMethodName]
set body [info body $fullMethodName]
uplevel [list proc $methodName $argList $body]
}
return
}
namespace export *
}
# provide the package
package provide voo $::voo::version

405
src/vfs/_vfscommon.vfs/modules/overtype-1.7.4.tm

@ -90,7 +90,9 @@ package require punk::assertion
# - need to extract and replace ansi codes?
tcl::namespace::eval overtype {
namespace import ::punk::assertion::assert
if {[info commands ::overtype::assert] eq ""} {
namespace import ::punk::assertion::assert
}
punk::assertion::active true
namespace path ::punk::lib
@ -625,7 +627,7 @@ tcl::namespace::eval overtype {
#set overtext [lpop inputchunks 0] ;#could be a list 'ansisplit' or text 'plain|mixed'
lassign [lpop inputchunks 0] overtext_type overtext
#use eq test with emptystring instead of 'string length' - test for emptiness shouldn't cause shimmering if popped inputchunks member if an 'ansisplit' list
#use eq test with emptystring instead of 'string length' - test for emptiness shouldn't cause shimmering if popped inputchunks member is an 'ansisplit' list
if {$overtext eq ""} {
incr loop
continue
@ -728,7 +730,7 @@ tcl::namespace::eval overtype {
set existing_reverse_state 0
#split_codes_single is single esc sequence - but could have multiple sgr codes within one esc sequence
#e.g \x1b\[0;31;7m has a reset,colour red and reverse
set codeinfo [punk::ansi::codetype::sgr_merge [list $replay_codes_overlay] -info 1]
set codeinfo [punk::ansi::codetype::sgr_merge [list $replay_codes_overlay] -info]
set codestate_reverse [dict get $codeinfo codestate reverse]
switch -- $codestate_reverse {
7 {
@ -863,7 +865,7 @@ tcl::namespace::eval overtype {
# ----
# review
set col $post_render_col
#just because it's out of range of the renderwidth - doesn't mean a move down should jump to witin the range - 2025
#just because it's out of range of the renderwidth - doesn't mean a move down should jump to within the range - 2025
#----
#set existingdata [lindex $outputlines [expr {$post_render_row -1}]]
@ -908,7 +910,7 @@ tcl::namespace::eval overtype {
#It would perhaps be more properly handled as a queue of instructions from our initial renderline call
#we don't need to worry about overflow next call (?)- but we should carry forward our gx and ansi stacks
puts stdout ">>>[a+ red bold]overflow_right during restore_cursor[a]"
puts stdout ">>>renderspace<<<[a+ red bold]overflow_right during restore_cursor[a]"
set sub_info [overtype::renderline\
-info 1\
@ -924,7 +926,7 @@ tcl::namespace::eval overtype {
tcl::dict::set vtstate autowrap_mode [tcl::dict::get $sub_info autowrap_mode] ;#nor this..
#todo!!!
# 2025 fix - this does nothing - so what uses it?? create a test!
# 2025 fix - this does nothing - so what is the intention?? create a test!
linsert outputlines $renderedrow $foldline
#review - row & col set by restore - but not if there was no save..
}
@ -1053,7 +1055,9 @@ tcl::namespace::eval overtype {
set overflow_right ""
} else {
if {[tcl::dict::get $vtstate autowrap_mode]} {
set outputlines [linsert $outputlines $renderedrow $overflow_right]
#set outputlines [linsert $outputlines $renderedrow $overflow_right]
#ledit outputlines $renderedrow $renderedrow-1 $overflow_right
ledit outputlines $renderedrow -1 $overflow_right
set overflow_right ""
set row [expr {$renderedrow + 2}]
} else {
@ -1150,7 +1154,8 @@ tcl::namespace::eval overtype {
if {$insert_lines_above > 0} {
set row $renderedrow
#set outputlines [linsert $outputlines $renderedrow-1 {*}[lrepeat $insert_lines_above ""]]
ledit outputlines $renderedrow-1 $renderedrow-2 {*}[lrepeat $insert_lines_above ""]
#ledit outputlines $renderedrow-1 $renderedrow-2 {*}[lrepeat $insert_lines_above ""]
ledit outputlines $renderedrow-1 -1 {*}[lrepeat $insert_lines_above ""]
incr row [expr {$insert_lines_above -1}] ;#we should end up on the same line of text (at a different index), with new empties inserted above
#? set row $post_render_row #can renderline tell us?
}
@ -1461,6 +1466,7 @@ tcl::namespace::eval overtype {
set nextprefix_list $overflow_right_pt_code_pt
} else {
#merge tail and head
#ledit <list> end end <val> will work with empty list (ledit <list> end <val> does not)
ledit nextprefix_list end end "[lindex $nextprefix_list end][lindex $overflow_right_pt_code_pt 0]"
lappend nextprefix_list {*}[lrange $overflow_right_pt_code_pt 1 end]
}
@ -1476,16 +1482,17 @@ tcl::namespace::eval overtype {
}
if 0 {
if {$nextprefix ne ""} {
set nextoveridx [expr {$overidx+1}]
if {$nextoveridx >= [llength $inputchunks]} {
lappend inputchunks $nextprefix
} else {
#lset overlines $nextoveridx $nextprefix[lindex $overlines $nextoveridx]
set inputchunks [linsert $inputchunks $nextoveridx $nextprefix]
if {$nextprefix ne ""} {
set nextoveridx [expr {$overidx+1}]
if {$nextoveridx >= [llength $inputchunks]} {
lappend inputchunks $nextprefix
} else {
#lset overlines $nextoveridx $nextprefix[lindex $overlines $nextoveridx]
#set inputchunks [linsert $inputchunks $nextoveridx $nextprefix]
ledit inputchunks $nextoveridx -1 $nextprefix
}
}
}
}
if {[llength $nextprefix_list]} {
#set inputchunks [linsert $inputchunks 0 $nextprefix]
@ -1669,13 +1676,17 @@ tcl::namespace::eval overtype {
}
}
}
lappend outputlines $rendered
#JULZ
#lappend outputlines $rendered
lappend outputlines $rendered\x1b\[m
#lappend outputlines [renderline -insert_mode 0 -transparent $opt_transparent $undertext $overtext]
} else {
#background block is wider than or equal to data for this line
#lappend outputlines [renderline -insert_mode 0 -startcolumn [expr {$left_exposed + 1}] -transparent $opt_transparent -exposed1 $opt_exposed1 -exposed2 $opt_exposed2 $undertext $overtext]
set rinfo [renderline -info 1 -insert_mode 0 -startcolumn [expr {$left_exposed + 1}] -transparent $opt_transparent -exposed1 $opt_exposed1 -exposed2 $opt_exposed2 $undertext $overtext]
lappend outputlines [tcl::dict::get $rinfo result]
#JULZ
#lappend outputlines [tcl::dict::get $rinfo result]
lappend outputlines [tcl::dict::get $rinfo result]\x1b\[m
}
set replay_codes_underlay [tcl::dict::get $rinfo replay_codes_underlay]
set replay_codes_overlay [tcl::dict::get $rinfo replay_codes_overlay]
@ -1787,6 +1798,9 @@ tcl::namespace::eval overtype {
set overflowlength [expr {$overtext_datalen - $renderwidth}]
if {$overflowlength > 0} {
#raw overtext wider than undertext column
#broken:
#todo - renderline -overflow is invalid.
# we need renderline to support -expand_left ??
set rinfo [renderline\
-info 1\
-insert_mode 0\
@ -1814,13 +1828,18 @@ tcl::namespace::eval overtype {
}
}
}
lappend outputlines $rendered
#JULZ
#lappend outputlines $rendered
lappend outputlines $rendered\x1b\[m
} else {
#padded overtext
#lappend outputlines [renderline -insert_mode 0 -transparent $opt_transparent -startcolumn [expr {$left_exposed + 1}] $undertext $overtext]
#Note - we still need overflow(exapnd_right) here - as although the overtext is short - it may oveflow due to the startoffset
set rinfo [renderline -info 1 -insert_mode 0 -transparent $opt_transparent -expand_right $opt_overflow -startcolumn [expr {$left_exposed + 1 + $startoffset}] $undertext $overtext]
lappend outputlines [tcl::dict::get $rinfo result]
#JULZ
#lappend outputlines [tcl::dict::get $rinfo result]
lappend outputlines [tcl::dict::get $rinfo result]\x1b\[m
}
set replay_codes [tcl::dict::get $rinfo replay_codes]
set replay_codes_underlay [tcl::dict::get $rinfo replay_codes_underlay]
@ -2014,7 +2033,8 @@ tcl::namespace::eval overtype {
# }
#}
}
lappend outputlines $rendered
#JULZ
lappend outputlines $rendered\x1b\[m
} else {
#padded overtext
#lappend outputlines [renderline -insert_mode 0 -transparent $opt_transparent -startcolumn [expr {$left_exposed + 1}] $undertext $overtext]
@ -2023,7 +2043,9 @@ tcl::namespace::eval overtype {
#puts stderr "--> [ansistring VIEW -lf 1 -nul 1 $rinfo] <--"
set overflow_right [tcl::dict::get $rinfo overflow_right]
set unapplied [tcl::dict::get $rinfo unapplied]
lappend outputlines [tcl::dict::get $rinfo result]
#JULZ
#lappend outputlines [tcl::dict::get $rinfo result]
lappend outputlines [tcl::dict::get $rinfo result]\x1b\[m
}
set replay_codes [tcl::dict::get $rinfo replay_codes]
set replay_codes_underlay [tcl::dict::get $rinfo replay_codes_underlay]
@ -2136,6 +2158,24 @@ tcl::namespace::eval overtype {
}]
}
proc stack_eq {a b} {
#single level list equality test to avoid generating internal string representations of the lists unnecessarily.
if {[llength $a] != [llength $b]} {
return 0
}
foreach code1 $a code2 $b {
if {$code1 ne $code2} {
return 0
}
}
return 1
}
#todo: tests
#set j [overtype::renderline -transparent " " -insert_mode 0 -expand_right 1 "[a+ red underline]xxx[a+ blue][a+ nounderline]" "[a green]J" ]yyy
# yyy should be blue with no underline - and the J should be green - and the x's should be red with underline and the J should overwrite the first x
#At the moment we return a reset at the end of the renderline result instead of the replay codes.
proc renderline {args} {
#todo - fix 'unapplied' mechanism.This is particularly inefficient for long lines, or data such as binarytext which is not line-based.
#All unapplied data is re-split/reprocessed repeatedly for each line! This is very wasteful and slow.
@ -2476,7 +2516,9 @@ tcl::namespace::eval overtype {
if {$maybemouse ne "<" && [tcl::string::index $code end] eq "m"} {
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set u_codestack [list "\x1b\[m"]
#will normalize all resets to the same code - including 8bit reset.
#set u_codestack [list "\x1b\[m"]
set u_codestack [list $code]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
set u_codestack [list $code]
} else {
@ -2557,6 +2599,17 @@ tcl::namespace::eval overtype {
}
}
#----------------------------------------
#set test_c [showlist $undercols]
##set test_s [showlist $understacks %ansiview]
#set sview [list]
#foreach us $understacks {
# lappend sview [ansistring VIEW $us]
#}
#set test_s [showlist $sview]
#puts stderr "undercols/stacks:\n[textblock::join -- $test_c " " $test_s]"
#----------------------------------------
if {$opt_width ne "\uFFEF"} {
set renderwidth $opt_width
} else {
@ -2567,7 +2620,10 @@ tcl::namespace::eval overtype {
#trailing codes in effect for underlay
if {[llength $u_codestack]} {
#set replay_codes_underlay [join $u_codestack ""]
set replay_codes_underlay [punk::ansi::codetype::sgr_merge_list {*}$u_codestack]
#set replay_codes_underlay [punk::ansi::codetype::sgr_merge_list {*}$u_codestack]
#u_codestack was built from codes split using split_codes_single
#- so should already be simplified to single codes with no multiple SGR params in one code
set replay_codes_underlay [punk::ansi::codetype::sgr_merge_singles $u_codestack]
} else {
set replay_codes_underlay ""
}
@ -2767,13 +2823,17 @@ tcl::namespace::eval overtype {
} else {
lappend overlay_grapheme_control_stacks $o_codestack
#there will always be an empty code at end due to foreach on 2 vars with odd-sized list ending with pt (overmap coming from perlish split)
if {[punk::ansi::codetype::is_sgr_reset $code]} {
set o_codestack [list "\x1b\[m"] ;#reset better than empty list - fixes some ansi art issues
set code_endswith_m [expr {[tcl::string::index $code end] eq "m"}] ;#skip SGR regexp testing for cases that don't end with m - as they can't be SGR
if {$code_endswith_m && [punk::ansi::codetype::is_sgr_reset $code]} {
#reset better than empty list - fixes some ansi art issues
#set o_codestack [list "\x1b\[m"]
set o_codestack [list $code]
lappend overlay_grapheme_control_list [list sgr $code]
} elseif {[punk::ansi::codetype::has_sgr_leadingreset $code]} {
} elseif {$code_endswith_m && [punk::ansi::codetype::has_sgr_leadingreset $code]} {
set o_codestack [list $code]
lappend overlay_grapheme_control_list [list sgr $code]
} elseif {[priv::is_sgr $code]} {
} elseif {$code_endswith_m && [priv::is_sgr $code]} {
#basic simplification first - remove straight dupes
set dup_posns [lsearch -all -exact $o_codestack $code] ;#must be -exact because of square-bracket glob chars
set o_codestack [lremove $o_codestack {*}$dup_posns]
@ -2827,7 +2887,12 @@ tcl::namespace::eval overtype {
lappend overstacks_gx $o_gxstack
#set replay_codes_overlay [join $o_codestack ""]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}$o_codestack]
if {[llength $o_codestack]} {
#set replay_codes_overlay [join $o_codestack ""]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_singles $o_codestack]
} else {
set replay_codes_overlay [list]
}
#if {[tcl::dict::exists $overstacks $max_overlay_grapheme_index]} {
# set replay_codes_overlay [join [tcl::dict::get $overstacks $max_overlay_grapheme_index] ""]
@ -2952,7 +3017,7 @@ tcl::namespace::eval overtype {
#specials - each shoud have it's own test of what to do if it happens after overflow_idx reached
switch -- $chtest {
"<lf>" {
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
if {$idx == 0} {
#puts "---a <lf> at col 1"
#linefeed at column 1
@ -3069,8 +3134,7 @@ tcl::namespace::eval overtype {
set next_gc [lindex $overlay_grapheme_control_list $gci+1] ;#next grapheme or control
lassign $next_gc next_type next_item
if {$autowrap_mode} {
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci-1]]
#set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci-1]]
#don't incr idx beyond the overflow_idx
#idx_over already incremented - decrement so current overlay grapheme stacks go to unapplied
incr idx_over -1
@ -3087,7 +3151,7 @@ tcl::namespace::eval overtype {
#no point throwing back to caller for each grapheme that is overflowing
#without this branch - renderline would be called with overtext reducing only by one grapheme per call
#processing a potentially long overtext each time (ie - very slow)
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#JMN4
}
@ -3427,7 +3491,7 @@ tcl::namespace::eval overtype {
switch -exact -- $code_end {
A {
#Row move - up
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#todo
lassign [split $param {;}] num modifierkey
if {$modifierkey ne ""} {
@ -3452,7 +3516,7 @@ tcl::namespace::eval overtype {
#CUD - Cursor Down
#Row move - down
lassign [split $param {;}] num modifierkey
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#move down
if {$modifierkey ne ""} {
puts stderr "modifierkey:$modifierkey"
@ -3503,7 +3567,7 @@ tcl::namespace::eval overtype {
incr cursor_column $num
} else {
if {$autowrap_mode} {
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#jmn
if {$idx == $overflow_idx} {
incr num
@ -3598,7 +3662,7 @@ tcl::namespace::eval overtype {
set cursor_column 1
set idx 0
} else {
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
incr cursor_column -$num
priv::render_to_unapplied $overlay_grapheme_control_list $gci
set instruction wrapmovebackward
@ -3626,7 +3690,9 @@ tcl::namespace::eval overtype {
set cursor_column 1
set cursor_row [expr {$cursor_row + $downmove}]
set idx [expr {$cursor_column -1}]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
#sgr_merge_list
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#sgr_merge_singles ??
incr idx_over
priv::render_to_unapplied $overlay_grapheme_control_list $gci
set instruction move
@ -3647,7 +3713,7 @@ tcl::namespace::eval overtype {
set cursor_row 1
}
set idx [expr {$cursor_column - 1}]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
incr idx_over
priv::render_to_unapplied $overlay_grapheme_control_list $gci
set instruction move
@ -3656,6 +3722,7 @@ tcl::namespace::eval overtype {
}
G {
#CHA - Cursor Horizontal Absolute (move to absolute column no)
#see also HPA - Horizontal Position Absolute (same functionality)
if {$param eq ""} {
set targetcol 1
} else {
@ -3680,6 +3747,29 @@ tcl::namespace::eval overtype {
set cursor_column $targetcol
#puts stderr "renderline absolute col move ESC G (TEST)"
}
` {
#https://vt100.net/docs/vt510-rm/HPA.html
#docs don't mention that it defaults to one if $parm omitted - but it seems to do in practice
if {$param eq ""} {
set targetcol 1
} else {
set targetcol $param
if {![string is integer -strict $targetcol]} {
puts stderr "renderline HPA (Horizontal Position Absolute) error. Unrecognised parameter '$param'"
}
set targetcol [expr {$param}]
set max [llength $outcols]
if {$overflow_idx == -1} {
incr max
}
if {$targetcol > $max} {
puts stderr "renderline HPA (Horizontal Position Absolute) error. Param '$param' > max: $max"
set targetcol $max
}
}
set idx [expr {($targetcol -1) + $opt_colstart -1}]
set cursor_column $targetcol
}
H - f {
#CSI n;m H - CUP - Cursor Position
@ -3727,7 +3817,7 @@ tcl::namespace::eval overtype {
set cursor_row $target_row
set cursor_column $target_column
set idx [expr {$cursor_column -1}]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
incr idx_over
priv::render_to_unapplied $overlay_grapheme_control_list $gci
set instruction move
@ -3758,7 +3848,7 @@ tcl::namespace::eval overtype {
set cursor_row 1
set cursor_column 1
set idx [expr {$cursor_column -1}]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
incr idx_over
if {[llength $outcols]} {
priv::render_erasechar 0 [llength $outcols]
@ -4000,7 +4090,8 @@ tcl::namespace::eval overtype {
}
}
#append cursor_saved_attributes [join $sgr_stack ""]
append cursor_saved_attributes [punk::ansi::codetype::sgr_merge_list {*}$sgr_stack]
#append cursor_saved_attributes [punk::ansi::codetype::sgr_merge_list {*}$sgr_stack]
append cursor_saved_attributes [punk::ansi::codetype::sgr_merge $sgr_stack]
#as there is apparently only one cursor storage element we don't need to throw back to the calling loop for a save.
@ -4024,7 +4115,7 @@ tcl::namespace::eval overtype {
# set replay_codes_overlay $cursor_saved_attributes ;#empty - or last save if it happend in this input chunk
#} else {
#jj
#set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
#set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay ""
#}
@ -4398,7 +4489,7 @@ tcl::namespace::eval overtype {
#vt102-docs: "Moves cursor up one line in same column. If cursor is at top margin, screen performs a scroll-down"
puts stderr "overtype::renderline ESC M not fully implemented"
set replay_codes_overlay [punk::ansi::codetype::sgr_merge_list {*}[lindex $overlay_grapheme_control_stacks $gci]]
set replay_codes_overlay [punk::ansi::codetype::sgr_merge [lindex $overlay_grapheme_control_stacks $gci]]
#move up
incr cursor_row -1
if {$cursor_row < 1} {
@ -4743,6 +4834,9 @@ tcl::namespace::eval overtype {
#puts stderr "first_tail_null_posn: $first_tail_null_posn"
#puts stderr "colview: [ansistring VIEW $outcols]"
#NOTE understacks has been updated with data from the overlay - so it should reflect the final state of the stacks for each grapheme in outcols
foreach ch $outcols {
#puts "---- [ansistring VIEW $ch]"
@ -4766,15 +4860,58 @@ tcl::namespace::eval overtype {
if {$i < [llength $understacks]} {
#set cstack [tcl::dict::get $understacks $i]
set cstack [lindex $understacks $i]
if {$cstack ne $prevstack} {
if {[llength $prevstack] && ![llength $cstack]} {
#This reset is important e.g testfile fruit.ans - we get overhang on rhs without it. But why is cstack empty?
append sgrleader \033\[m
#use stack_eq for depth 1 comparison without generating string rep.
if {![stack_eq $cstack $prevstack]} {
#possible SGR attribute change.
if {[llength $prevstack]} {
if {![llength $cstack]} {
#why is cstack empty?
#a) no ansi in underlay and we are at a position 2 after an overlay insertion.
# (position 1 after overlay insertion should already have had a reset inserted)
#b) no ansi in overlay and we are at an overlay insertion point.
#--------------
#review
#todo? consider testing next-char's understack when applying each overlay char in the main loop.
#if empty or has no leading reset - we need to add a leading reset at that point.
#--------------
#--------
#following statement is FALSE - (historical info). Doesn't seem to apply.
#This reset is important e.g testfile fruit.ans - we get overhang on rhs without it.
#append sgrleader \033\[m
#--------
##test
#set view_prev ""
#foreach ps $prevstack {
# append view_prev [ansistring VIEW -lf 1 -vt 1 -nul 1 $ps]
#}
#puts stderr "col $i, ch: $ch - cstack empty vs prevstack $view_prev"
} else {
#without this we get extra redundant codes in some places.
#e.g a continuous string of underlay that originally had \x1b\[31m red text,
#but then when an overlay char is inserted near the start, the following underlay char (insertion index +1) codestack had a reset added.
#All subsequent underlay chars in the same run of plaintext don't have the reset and so appear 'different' but are actually part of the same run.
#check if actually different. ie if current stack actually changes anything from previous stack when merged together.
set prevmerge [punk::ansi::codetype::sgr_merge $prevstack]
set currmerge [punk::ansi::codetype::sgr_merge $cstack]
set together [punk::ansi::codetype::sgr_merge [list $prevmerge $currmerge]]
if {$together ne $prevmerge} {
#stacks are different enough that we need to output something
#if {{[punk::ansi::codetype::has_sgr_leading_reset $currmerge]}} {
#}
append sgrleader $currmerge
}
}
} else {
append sgrleader [punk::ansi::codetype::sgr_merge_list {*}$cstack]
if {[llength $cstack]} {
append sgrleader [punk::ansi::codetype::sgr_merge $cstack]
}
}
set prevstack $cstack
}
set prevstack $cstack
} else {
set prevstack [list]
}
@ -4797,7 +4934,8 @@ tcl::namespace::eval overtype {
#if {[llength $prevstack] && ![llength $cstack]} {
# append sgrleader \033\[m
#}
append sgrleader [punk::ansi::codetype::sgr_merge_list {*}$cstack]
#append sgrleader [punk::ansi::codetype::sgr_merge_list {*}$cstack]
append sgrleader [punk::ansi::codetype::sgr_merge $cstack]
append overflow_right $sgrleader
append overflow_right $ch
} else {
@ -4853,14 +4991,50 @@ tcl::namespace::eval overtype {
set replay_codes ""
if {[llength $understacks] > 0} {
if {$overflow_idx == -1} {
#set tail_idx [tcl::dict::size $understacks]
set tail_idx [llength $understacks]
} else {
set tail_idx [llength $undercols]
}
if {$tail_idx-1 < [llength $understacks]} {
if {$tail_idx == [llength $undercols]} {
#we got to the end of the original underlay
#- so we want the full stack at the end of the original underlay ie including trailing codes which are not associated with any grapheme in the underlay
#but would be in effect for any text after the underlay.
#---------------------
#REVIEW - determine if last col was overwritten by overlay?
#how best to determine if last underlay column was overwritten by overlay?
#we could track in the main loop whether each underlay column was overwritten by overlay
#This seems like the best mechanism, because the overlay ANSI can include movement codes, so the underlay can be overwritten in any order.
#We should consider that just because the last grapheme was overwritten, that doesn't necessarily mean we should disregard the trailing codes
#perhaps trailing underlay codes are never overwritten unless the overlay extends beyond the end of the underlay - in which case we can just check if overlay extends beyond end of underlay to determine whether to include trailing underlay codes in replay or not.
#if overlay extends beyond end of underlay - we use the overlay stack at the end of the underlay as the replay codes, which won't include any trailing underlay codes.
#---------------------
if {[lindex $undermap end] eq ""} {
#there were trailing codes in the underlay with no grapheme - we want to include those in the replay as they would affect any text after the underlay
#we need to backtrack from the end of the underlay to find the last grapheme with codes, and merge those codes with any trailing codes in the underlay with no grapheme
set tailcodes [list] ;#build in reverse order.
foreach {pt code} [lreverse $undermap] {
if {$pt ne ""} {
break
}
lappend tailcodes $code
}
set tailcodes [lreverse $tailcodes]
#set tailcodes [lindex $undermap end-1]
set laststack [lindex $understacks $tail_idx-1]
lappend laststack {*}$tailcodes
set replay_codes [punk::ansi::codetype::sgr_merge $laststack] ;#stack at end of underlay including trailing codes
} else {
#last part of underlay was plain text with no trailing codes - we can just use the stack at the last grapheme of the underlay
set replay_codes [punk::ansi::codetype::sgr_merge [lindex $understacks $tail_idx-1]] ;#stack at end of underlay
}
} elseif {$tail_idx-1 < [llength $understacks]} {
#set replay_codes [join [lindex $understacks $tail_idx-1] ""] ;#tail replay codes
set replay_codes [punk::ansi::codetype::sgr_merge_list {*}[lindex $understacks $tail_idx-1]] ;#tail replay codes
#set replay_codes [punk::ansi::codetype::sgr_merge_list {*}[lindex $understacks $tail_idx-1]] ;#tail replay codes
set replay_codes [punk::ansi::codetype::sgr_merge [lindex $understacks $tail_idx-1]] ;#tail replay codes
}
if {$tail_idx-1 < [llength $understacks_gx]} {
set gx0 [lindex $understacks_gx $tail_idx-1]
@ -4876,10 +5050,33 @@ tcl::namespace::eval overtype {
#pdict $understacks
if {[punk::ansi::ta::detect_sgr $outstring]} {
append outstring [punk::ansi::a] ;#without this - we would get for example, trailing backgrounds after rightmost column
#JULZ
#The caller is responsible for adding a reset at the end of returned lines depending on how they want to use it - so we don't add one here.
#<deprecated>
#append outstring [punk::ansi::a] ;#without this - we would get for example, trailing backgrounds after rightmost column
#</deprecated>
#we only want to append the replay codes if they are different to those already in effect at the end of the rendered line.
if {$overflow_idx == -1} {
set tail_idx [llength $understacks]
} else {
set tail_idx [llength $undercols]
}
set laststack [lindex $understacks $tail_idx-1]
set laststackmerge [punk::ansi::codetype::sgr_merge $laststack]
if {$replay_codes ne $laststackmerge} {
append outstring $replay_codes
}
#review
#close off any open gx?
#probably should - and overflow_right reopen?
#probably not, this is akin to adding a reset to close off open SGR codes, which we specifically don't do.
#caller will need to close off any open gx at the end of the line if they want to, and provide appropriate replay codes for the next line if they want to maintain gx state across lines.
#we just need to make sure we provide all necessary info in the result dictionary.
#todo - tests and examples.
#and overflow_right reopen?
}
if {$opt_returnextra} {
@ -4902,29 +5099,29 @@ tcl::namespace::eval overtype {
set result [tcl::dict::create\
result $outstring\
visualwidth [punk::ansi::printing_length $outstring]\
instruction $instruction\
stringlen [string length $outstring]\
overflow_right_column $overflow_right_column\
overflow_right $overflow_right\
unapplied $unapplied\
unapplied_list $unapplied_list\
unapplied_ansisplit $unapplied_ansisplit\
insert_mode $insert_mode\
autowrap_mode $autowrap_mode\
crm_mode $crm_mode\
reverse_mode $reverse_mode\
insert_lines_above $insert_lines_above\
insert_lines_below $insert_lines_below\
cursor_saved_position $cursor_saved_position\
visualwidth [punk::ansi::printing_length $outstring]\
instruction $instruction\
stringlen [string length $outstring]\
overflow_right_column $overflow_right_column\
overflow_right $overflow_right\
unapplied $unapplied\
unapplied_list $unapplied_list\
unapplied_ansisplit $unapplied_ansisplit\
insert_mode $insert_mode\
autowrap_mode $autowrap_mode\
crm_mode $crm_mode\
reverse_mode $reverse_mode\
insert_lines_above $insert_lines_above\
insert_lines_below $insert_lines_below\
cursor_saved_position $cursor_saved_position\
cursor_saved_attributes $cursor_saved_attributes\
cursor_column $cursor_column\
cursor_row $cursor_row\
expand_right $opt_expand_right\
replay_codes $replay_codes\
replay_codes_underlay $replay_codes_underlay\
replay_codes_overlay $replay_codes_overlay\
pm_list $pm_list\
cursor_column $cursor_column\
cursor_row $cursor_row\
expand_right $opt_expand_right\
replay_codes $replay_codes\
replay_codes_underlay $replay_codes_underlay\
replay_codes_overlay $replay_codes_overlay\
pm_list $pm_list\
]
if {$opt_returnextra == 1} {
#puts stderr "renderline: $result"
@ -5073,6 +5270,11 @@ tcl::namespace::eval overtype::priv {
#caching the answer saves some regex expense - possibly a few uS to lookup vs under 1uS
#todo - test if still worthwhile after a large cache is built up. (limit cache size?)
proc is_sgr {code} {
set code_endswith_m [expr {[tcl::string::index $code end] eq "m"}] ;#skip SGR regexp testing for cases that don't end with m - as they can't be SGR
if {!$code_endswith_m} {
#don't even cache.
return 0
}
variable cache_is_sgr
if {[tcl::dict::exists $cache_is_sgr $code]} {
return [tcl::dict::get $cache_is_sgr $code]
@ -5081,6 +5283,7 @@ tcl::namespace::eval overtype::priv {
tcl::dict::set cache_is_sgr $code $answer
return $answer
}
proc render_to_unapplied {overlay_grapheme_control_list gci} {
upvar idx_over idx_over
@ -5104,7 +5307,8 @@ tcl::namespace::eval overtype::priv {
set unapplied_ansisplit [list ""]
#append unapplied [join [lindex $overstacks $idx_over] ""]
#append unapplied [punk::ansi::codetype::sgr_merge_list {*}[lindex $overstacks $idx_over]]
set sgr_merged [punk::ansi::codetype::sgr_merge_list {*}[lindex $og_stacks $gci]]
#set sgr_merged [punk::ansi::codetype::sgr_merge_list {*}[lindex $og_stacks $gci]]
set sgr_merged [punk::ansi::codetype::sgr_merge [lindex $og_stacks $gci]]
if {$sgr_merged ne ""} {
lappend unapplied_list $sgr_merged
lappend unapplied_ansisplit $sgr_merged ""
@ -5167,7 +5371,8 @@ tcl::namespace::eval overtype::priv {
set unapplied_list [list]
set unapplied_ansisplit [list ""] ;#remove empty entry at end if nothing added
set sgr_merged [punk::ansi::codetype::sgr_merge_list {*}[lindex $og_stacks $gci]]
#set sgr_merged [punk::ansi::codetype::sgr_merge_list {*}[lindex $og_stacks $gci]]
set sgr_merged [punk::ansi::codetype::sgr_merge [lindex $og_stacks $gci]]
if {$sgr_merged ne ""} {
lappend unapplied_list $sgr_merged
lappend unapplied_ansisplit $sgr_merged ""
@ -5217,9 +5422,13 @@ tcl::namespace::eval overtype::priv {
upvar understacks_gx gxstacks
set nxt [llength $o]
if {$i < $nxt} {
set o [lreplace $o $i $i]
set ustacks [lreplace $ustacks $i $i]
set gxstacks [lreplace $gxstacks $i $i]
#set o [lreplace $o $i $i]
ledit o $i $i
#set ustacks [lreplace $ustacks $i $i]
ledit ustacks $i $i
#review - do we need to ensure that stack at new $i has a reset code at the start?
#set gxstacks [lreplace $gxstacks $i $i]
ledit gxstacks $i $i
} elseif {$i == 0 || $i == $nxt} {
#nothing to do
} else {
@ -5329,6 +5538,27 @@ tcl::namespace::eval overtype::priv {
}
if {$i < [llength $ustacks]} {
lset ustacks $i $sgrstack
#check if next ustacks entry has a reset.
#It will need one if it doesn't already have one because our inserted char should not affect the pre-existing ansi state of the underlay.
#we have just replaced an entry into the ustacks at position i but we are still at the same position - so the next entry is still at position i+1
if {[llength $sgrstack] && $i+1 < [llength $ustacks]} {
set next_ustack [lindex $ustacks $i+1]
#could be a reset or just empty - either way we need to add a reset if it's not already there
#(empty if underlay had no ansi)
#temporarily emit something to stderr
if {![llength $next_ustack]} {
#puts -nonewline stderr " next_ustack (empty) at position [expr {$i+1}] after replacing position $i with '$c' and sgrstack '[join $sgrstack ""]'\n"
lset ustacks $i+1 [list "\x1b\[m"]
} else {
#review - next_ustack is a list - has_sgr_leadingreset will not work as expected if called on whole next_ustack as a list.
#As the stack will need merging anyway - we can just prepend a reset without checking.
#REVIEW.
#puts -nonewline stderr "check next_ustack '$next_ustack' for reset at position [expr {$i+1}] after replacing position $i with '$c' and sgrstack '[join $sgrstack ""]'\n"
#set next_ustack [linsert $next_ustack 0 [a+ reset]]
ledit next_ustack -1 -1 "\x1b\[m"
lset ustacks $i+1 $next_ustack
}
}
lset gxstacks $i $gx0stack
} else {
lappend ustacks $sgrstack
@ -5339,7 +5569,8 @@ tcl::namespace::eval overtype::priv {
if {$i < $nxt} {
#set o [linsert $o $i $c]
#JMN insert via ledit
ledit o $i $i-1 $c
#ledit o $i $i-1 $c
ledit o $i -1 $c
} else {
lappend o $c
}
@ -5347,8 +5578,10 @@ tcl::namespace::eval overtype::priv {
#set ustacks [linsert $ustacks $i $sgrstack]
#set gxstacks [linsert $gxstacks $i $gx0stack]
#insert via ledit
ledit ustacks $i $i-1 $sgrstack
ledit gxstacks $i $i-1 $gx0stack
#ledit ustacks $i $i-1 $sgrstack
ledit ustacks $i -1 $sgrstack
#ledit gxstacks $i $i-1 $gx0stack
ledit gxstacks $i -1 $gx0stack
} else {
lappend ustacks $sgrstack
lappend gxstacks $gx0stack

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

@ -1743,7 +1743,7 @@ namespace eval punk {
append script \n {set assigned [ansistring length $leveldata]}
set level_script_complete 1
}
%str {
%str - %string {
set active_key_type "string"
if {$get_not} {
error "!%str - not string-get is not supported"
@ -1752,6 +1752,9 @@ namespace eval punk {
append script \n {# set active_key_type "" index_operation: string-get}
append script \n {set assigned $leveldata}
set level_script_complete 1
#todo - %lpad- %lpadstr- %join- etc as in punk::lib::showdict
#review - merge code shared with showdict for these operations
}
%sp {
#experimental
@ -1822,6 +1825,8 @@ namespace eval punk {
set level_script_complete 1
}
%ansiview {
#review - implemented differently in showdict.
#(showdict uses ansistring VIEW -lf 1 <str>)
set active_key_type "string"
if {$get_not} {
error "!%# not string-ansiview is not supported"
@ -2446,7 +2451,41 @@ namespace eval punk {
set index <idx>
}]
}
%split-* {
#split on one or more chars - review
#set hidekey 1
#lassign [split $key -] _ splitchars
#set thisval [split $dval $splitchars]
set active_key_type "string"
set splitchars [string range $index 7 end]
append script \n [string map [list <splitchars> $splitchars] {
# set active_key_type "string" index_operation: split-string
#e.g supports %split-"\\n"= "l1\n\nl3" -> {l1 "" l3}
set splitchars "<splitchars>"
set assigned [split $leveldata $splitchars]
}]
set level_script_complete 1
#todo %splitat- %splitn- ??
}
%lpad-* {
#moved from punk::lib::showdict patterns.
#set hidekey 1
#lassign [split $key -] _ extra
#set width [expr {[textblock::width $dval] + $extra}]
#set thisval [textblock::pad $dval -which left -width $width]
set active_key_type "string"
set extra [string range $index 6 end]
append script \n [string map [list <extra> $extra] {
# set active_key_type "string" index_operation: lpad-string
set extra "<extra>"
set width [expr {[textblock::width $leveldata] + $extra}]
set assigned [textblock::pad $leveldata -which left -width $width]
}]
set level_script_complete 1
}
%* {
#see above re %lpad- etc and synchronizing with showdict
set active_key_type "string"
set do_bounds_check 0
set index [string range $index 1 end]
@ -2827,11 +2866,21 @@ namespace eval punk {
} else {
if {$is_range} {
lappend INDEX_OPERATIONS list-range
#todo - if we know it's a contiguous range, we could use lrange here instead of lindex
#we would also need to detect if it's a reverse range such as @5..1 and handle that correctly
#- lrange doesn't support reverse ranges, but we could resolve the indexset to a list of indices
#and then use lindex with that list of indices to get the correct result.
#we don't always know at this point if the range is in reverse or not because we don't know the size of the list until
#runtime - so we will handle both cases in the same way for now.
#e.g for index 5..end-6 - this could be forward or reverse depending on the length of the list.
set assign_script {
set assigned [lmap i [punk::lib::indexset_resolve [llength $leveldata] <idx>] {lindex $leveldata $i}]
}
} else {
lappend INDEX_OPERATIONS listindex
}
set assign_script {
set assigned [lmap i [punk::lib::indexset_resolve [llength $leveldata] <idx>] {lindex $leveldata $i}]
set assign_script {
set assigned [lindex $leveldata [punk::lib::indexset_resolve [llength $leveldata] <idx>]]
}
}
}
@ -2881,6 +2930,8 @@ namespace eval punk {
}
set script [string map [list <idx> $index] $script]
} elseif {[string first "end" $index] >=0} {
#review - obsoleted by indexset syntax. prune branch?
puts stderr "index with end detected - review if this branch still reachable - prune? $index"
if {[regexp {^end([-+]{1,2}[0-9]+)$} $index _match endspec]} {
if {$get_not} {
@ -2923,6 +2974,8 @@ namespace eval punk {
}
} elseif {[regexp {^([0-9]+|end|end[-+]{1,2}[0-9]+)-([0-9]+|end|end[-+]{1,2}([0-9]+))$} $index _ start end]} {
#review - obsoleted by indexset syntax. prune branch?
puts stderr "index with range and end detected - review if this branch still reachable - prune? $index"
if {$get_not} {
lappend INDEX_OPERATIONS list-range-not
set assign_script [string map [list <s> $start <e> $end ] {
@ -3012,6 +3065,10 @@ namespace eval punk {
error $listmsg "destructure $selector" [list pipesyntax destructure selector $selector]
}
} elseif {[string first - $index] > 0} {
puts stderr "index with - detected - review if this branch still reachable - prune? $index"
#review - we changed to detect indexset above.
#syntax @m-n should be deprecated in favour of @m..n
#todo - check if this branch still reachable - prune?
#e.g @1-3 gets here
#JMN
if {$get_not} {
@ -3089,19 +3146,61 @@ namespace eval punk {
}
}
} elseif {$active_key_type eq "string"} {
if {[string match *-* $index]} {
lappend INDEX_OPERATIONS string-range
set re_idxdashidx {^([-+]{0,1}\d+|end[-+]{1}\d+|end)-([-+]{0,1}\d+|end[-+]{1}\d+|end)$}
#todo - support more complex indices: 0-end-1 etc
#changed to indexset notation m..n allowing eg 2..end-1 etc.
#if {[string match *-* $index]} {}
if {[punk::lib::is_indexset $index]} {
#review - we are assuming a single element indexset here - ie no comma separated sets.
#todo - support $get_not
#todo - consider bounds_check for string indices.
# - Tcl doesn't do bounds checking for string index, but we need to consider in the context of pattern-matching
# whether we want to support syntaxes for with and without bounds checking on string indices.
set is_range [expr {[string first ".." $index] >= 0}]
if {$is_range} {
lappend INDEX_OPERATIONS string-range
#review - not efficient for contiguous monotonically increasing ranges
#because we are retrievinng each character individually and concatenating
#- but it is more flexible because it also supports reverse ranges and could support non-contiguous ranges such as @0,2,4..6
set assign_script {
set assigned [join [lmap i [punk::lib::indexset_resolve [string length $leveldata] <idx>] {string index $leveldata $i}] ""]
}
} else {
lappend INDEX_OPERATIONS string-index
set assign_script {
set assigned [string index $leveldata [punk::lib::indexset_resolve [string length $leveldata] <idx>]]
}
}
#set assign_script {
# set assigned [lmap i [punk::lib::indexset_resolve [llength $leveldata] <idx>] {lindex $leveldata $i}]
#}
lassign [split $index -] a b
#todo - consider where/if we can support 'ansistring INDEX' for ANSI strings.
#if so - it shouldn't overload the % operator we currently use for string access.
append script \n [tstr -return string -allowcommands {
# set active_key_type "string"
set assigned [string range $leveldata ${$a} ${$b}]
if {$leveldata eq ""} {
set assigned ""
} else {
${$assign_script}
}
}]
set script [string map [list <idx> $index] $script]
#set re_idxdashidx {^([-+]{0,1}\d+|end[-+]{1}\d+|end)-([-+]{0,1}\d+|end[-+]{1}\d+|end)$}
##todo - support more complex indices: 0-end-1 etc
#lassign [split $index -] a b
#append script \n [tstr -return string -allowcommands {
# # set active_key_type "string"
# set assigned [string range $leveldata ${$a} ${$b}]
#}]
} else {
if {$index eq "*"} {
#equivalent to indexset ".."
lappend INDEX_OPERATIONS string-all
append script \n [tstr -return string -allowcommands {
# set active_key_type "string"
@ -4294,6 +4393,7 @@ namespace eval punk {
}
#todo check end-x bounds?
}
#todo - change to ledit
if {$isint} {
append script [string map [list <listvar> $listvar <idx> $index <exp> $exp <val> $data] {
set <listvar> [linsert [lindex [list $<listvar> [unset <listvar>]] 0] <idx> <exp><val>]
@ -4350,7 +4450,8 @@ namespace eval punk {
#last element has no -, so we are inserting at the final position - not replacing
append script [string map [list <listvar> $listvar <containerkeys> [lrange $parts 0 end-1] <lastkey> $last <exp> $exp <val> $data] {
set target [lindex $<listvar> <containerkeys>]
set target [linsert $target <lastkey> <exp><val>]
#set target [linsert $target <lastkey> <exp><val>]
ledit target <lastkey> -1 <exp><val>
lset <listvar> <containerkeys> $target
}]
}
@ -8564,7 +8665,7 @@ namespace eval punk {
lappend chunks [list stdout $text]
}
console - term - terminal {
set term_env_vars {TERM TERM_PROGRAM TERM_PROGRAM_VERSION}
set term_env_vars {TERM TERM_PROGRAM TERM_PROGRAM_VERSION COLORTERM}
set term_dict [dict create]
foreach e $term_env_vars {
if {[info exists ::env($e)]} {
@ -8577,6 +8678,7 @@ namespace eval punk {
append text [punk::lib::showdict $term_dict] \n
lappend chunks [list stdout $text]
set text ""
set indent [string repeat " " [string length "WARNING: "]]
if {[catch {package require punk::console} result]} {
set text "Unable to load punk::console package - cannot test\n$result"
@ -8591,7 +8693,6 @@ namespace eval punk {
}
lappend chunks [list stdout $text]
set indent [string repeat " " [string length "WARNING: "]]
lappend cstring_tests [dict create\
type "PM "\
msg "UN"\
@ -8686,10 +8787,45 @@ namespace eval punk {
}
}
}
set posn [punk::console::get_cursor_pos] ;#warmup call - and test if works
if {$posn eq ""} {
append warningblock \n "WARNING: terminal doesn't respond to cursor position query - may cause display bugs in some cases."
} else {
set timeresult [timerate {set cpos [punk::console::get_cursor_pos]}]
lassign [split $cpos {;}] row col
if {![string is integer -strict $row] || ![string is integer -strict $col]} {
append warningblock \n "WARNING: terminal returns non-integer values for cursor position query - may cause display bugs in some cases. got row:'$row' col:'$col'"
} else {
set micros [lindex $timeresult 0]
if {$micros > 2000} {
append warningblock \n "WARNING: terminal cursor position query is very slow ($micros microseconds - expect < 2000us )"
append warningblock \n $indent "- may cause display lag/bugs in some cases."
} else {
if {$micros > 1000} {
set text "\n[a+ yellow]Terminal cursor position query test passed."
append text \n $indent "Response time: ${micros} microseconds (OK, good would be <= 1000us).[a]"
} else {
set text "[a+ green]Terminal cursor position query test passed."
append text \n $indent "Response time: ${micros} microseconds (GOOD).[a]"
}
lappend chunks [list stdout $text]
}
}
}
if {![string length $warningblock]} {
set text "[a+ green]No terminal warnings[a]\n"
lappend chunks [list stdout $text]
} else {
set mode [punk::console::mode]
if {$mode eq "line"} {
append warningblock \n "Terminal appears to be in line mode. Consider switching to raw mode and re-testing (command: punk::console::mode raw)."
}
}
puts stdout [punk::ansi::move_back 200] ;#hack for some horizontal position bugs where the above tests can leave the cursor in the wrong place for the next output.
#200 is arbitrary large number to move back enough to get to start of line.
}
}
topics - help {
@ -8815,10 +8951,11 @@ namespace eval punk {
#interp alias {} c {} clear ;#external executable 'clear' may not always be available
#todo - review
interp alias {} clear {} ::punk::reset
interp alias {} c {} ::punk::reset
#interp alias {} clear {} ::punk::reset
#interp alias {} c {} ::punk::reset
interp alias {} reset {} ::punk::reset
proc reset {} {
if {[llength [info commands ::punk::repl::reset_terminal]]} {
#punk::repl::reset_terminal notifies prompt system of reset
@ -8828,6 +8965,91 @@ namespace eval punk {
}
}
namespace eval argdoc {
punk::args::define {
@id -id ::punk::ansi8
@cmd -name punk::ansi8\
-summary\
"Tell terminal to enable 8-bit ANSI codes."\
-help\
"Enable 8-bit ANSI codes in the terminal.
May not be supported by all terminals.
Some terminals may already have 8-bit ANSI enabled, but some may require an explicit command to enable it.
7-bit ANSI codes are generally preferred - and will still work on terminals with 8-bit ANSI support.
(This is nothing to do with 8-bit colors - it is about the underlying bytes used for ANSI control sequences).
The ANSI sequence sent to the terminal to enable 8-bit codes is: ESC <sp> 7
To disable 8-bit ANSI support - a reset of the terminal may be required.
"
@opts
@values -min 0 -max 0
}
}
proc ansi8 {} {
punk::console::S8C1R
}
namespace eval argdoc {
punk::args::define {
@id -id ::punk::clear
@cmd -name punk::clear\
-summary\
"Clear the terminal screen (and scrollback buffer by default)."\
-help\
"Clear the terminal screen.
By default this will also clear scrollback if supported by the terminal.
With -x option it will preserve scrollback but clear the screen.
"
@opts
-x -optional 1 -type none -mash 1 -help\
"Preserve scrollback (if supported by terminal) but clear screen."
-s -optional 1 -type none -mash 1 -help\
"Stay at the current cursor position instead of moving to top-left after clearing."
@values -min 0 -max 0
}
}
proc clear {args} {
set argd [punk::args::parse $args withid ::punk::clear]
lassign [dict values $argd] leaders opts values received
set opt_x [dict exists $received -x]
set opt_s [dict exists $received -s]
# -x preserves scrollback but clears screen
if {$opt_s} {
#set pre_move_cmd [punk::ansi::move_up 1]
#review - terminal support for save/restore.
#we can just move up one line before clearing to preserve the line we're on,
#but this won't work if we're already at the last line.
#save/restore would be better if widely supported.
#review - get_size already calls get_cursor pos - maybe we can optimize by not calling get_cursor_pos separately?
#review - consider turning off cursor updating while doing this to avoid flicker?
set cpos [punk::console::get_cursor_pos]
set row [lindex $cpos 0]
set size [punk::console::get_size]
set lastrow [dict get $size rows]
if {$row >= $lastrow} {
set pre_move_cmd [punk::ansi::cursor_save_dec]
} else {
set pre_move_cmd [punk::ansi::move_up 1][punk::ansi::cursor_save_dec]
}
set move_cmd [punk::ansi::cursor_restore_dec]
#set pre_move_cmd [punk::ansi::move_up 1]
#set move_cmd ""
} else {
set pre_move_cmd ""
set move_cmd [punk::ansi::move 1 1]
}
if {$opt_x} {
puts -nonewline stdout $pre_move_cmd[punk::ansi::clear]$move_cmd
} else {
puts -nonewline stdout $pre_move_cmd[punk::ansi::clear_all]$move_cmd
}
}
#c aliased to clear -xs
#cc aliases to clear -x
#fileutil::cat except with checking for windows illegal path names (when on windows platform)

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

@ -125,6 +125,10 @@ tcl::namespace::eval punk::aliascore {
grepstr ::punk::ansi::grepstr\
colour ::punk::console::colour\
color ::punk::console::colour\
ansi8 ::punk::ansi8\
clear ::punk::clear\
c {::punk::clear -xs}\
cc {::punk::clear -x}\
ansi ::punk::console::ansi\
a? ::punk::console::code_a?\
A? {::punk::console::code_a? forcecolor}\

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

File diff suppressed because it is too large Load Diff

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

@ -103,7 +103,7 @@ tcl::namespace::eval ::punk::ansi::colourmap {
name -type string|stringstartswith(#)
}]
proc get_rgb_using_tk {name} {
package require tk
package require Tk ;#package require tk (lowercase) doesn't always work
#assuming 'winfo depth .' is always 32 ?
set RGB [winfo rgb . $name]
set rgb [lmap n $RGB {expr {$n / 256}}]

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save