From 6128fb4f5bf9594e34ddec4df38bdb17a66799a5 Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Fri, 15 May 2026 01:56:14 +1000 Subject: [PATCH] detect unicode support by char display width and use ascii if necessary in repl mode raw boxes --- src/bootsupport/modules/punk-0.1.tm | 26 ++++++++++----- src/bootsupport/modules/punk/repl-0.1.2.tm | 38 +++++++++++++++++----- src/modules/punk-0.1.tm | 23 ++++++++----- src/modules/punk/repl-999999.0a1.0.tm | 38 +++++++++++++++++----- 4 files changed, 93 insertions(+), 32 deletions(-) diff --git a/src/bootsupport/modules/punk-0.1.tm b/src/bootsupport/modules/punk-0.1.tm index 1823f13c..8994be71 100644 --- a/src/bootsupport/modules/punk-0.1.tm +++ b/src/bootsupport/modules/punk-0.1.tm @@ -8432,16 +8432,26 @@ namespace eval punk { set cols [dict get $sizedict columns] set rows [dict get $sizedict rows] - if {$cols <= 80} { - # Be conservative with frame types on narrow terminals for help. - # an 80x30 terminal is more likely to be an older style terminal and may not have unicode support. - # unicode on a non-unicode terminal is a bad experience - with the frame chars showing as garbage (e.g 3 chars per grapheme). - set frametype ascii - } else { - #unicode - set frametype light + + + #todo - provide a mechanism to configure the default frametype everywhere and describe it in this help. + + set frametype ascii ;#conservative default. + #if the test char width fails - it's likely we're on a very old terminal that doesn't support unicode at all. + if {![catch {punk::console::test_char_width \u00e9} testcharwidth]} { + if {$cols <= 80} { + # Be conservative with frame types on narrow terminals for help. + # an 80x30 terminal is more likely to be an older style terminal and may not have unicode support. + # unicode on a non-unicode terminal is a bad experience - with the frame chars showing as garbage (e.g 3 chars per grapheme). + set frametype ascii + } else { + if {$testcharwidth == 1} { + set frametype light ;#unicode box-drawing chars. + } + } } + # ------------------------------------------------------- set logoblock "" if {[catch { diff --git a/src/bootsupport/modules/punk/repl-0.1.2.tm b/src/bootsupport/modules/punk/repl-0.1.2.tm index c8f7fc3a..8b4b0f0a 100644 --- a/src/bootsupport/modules/punk/repl-0.1.2.tm +++ b/src/bootsupport/modules/punk/repl-0.1.2.tm @@ -21,6 +21,7 @@ unset stdin_info # ----------------------------------- + #------------------------------------------------------------------------------------- if {[package provide punk::libunknown] eq ""} { #maintenance - also in src/vfs/_config/punk_main.tcl @@ -162,6 +163,13 @@ namespace eval punk::repl { #todo - key on shell/subshell tsv::set repl runchunks-0 [list] ;#last_run_display + variable frametype + set frametype ascii; #conservative default + if {![catch {punk::console::test_char_width \u00e9} testcharwidth]} { + if {$testcharwidth == 1} { + set frametype light + } + } variable debug_repl 0 variable signal_control_c 0 @@ -991,6 +999,8 @@ namespace eval punk::repl::class { dict set o_chunk_info 0 [dict create micros [clock microseconds] type rendered] } method add_chunk {chunk} { + upvar ::punk::repl::frametype frametype + #we still split on lf - but each physical line may contain horizontal or vertical movements so we need to feed each line in and possibly get an overflow_right and unapplied and cursor-movent return info lappend o_chunk_list $chunk ;#may contain newlines,horizontal/vertical movements etc - all ok dict set o_chunk_info [expr {[llength $o_chunk_list] -1}] [dict create micros [clock microseconds] type raw] @@ -1065,7 +1075,7 @@ namespace eval punk::repl::class { append debug \n [showdict $dinfo] append debug \n "input:[ansistring VIEW -lf 1 -vt 1 $new0] before row:$o_cursor_row after row: $result_row before col:$o_cursor_col after col:$result_col" package require textblock - set debug [textblock::frame -checkargs 0 -buildcache 0 $debug] + set debug [textblock::frame -type $frametype -checkargs 0 -buildcache 0 $debug] if {![punk::console::vt52]} { catch {punk::console::move_emitblock_return $debug_first_row 1 $debug} } else { @@ -1148,7 +1158,7 @@ namespace eval punk::repl::class { set debug "add_chunk$i" append debug \n $mergedinfo append debug \n "input:[ansistring VIEW -lf 1 -vt 1 $p]" - set debug [textblock::frame -checkargs 0 -buildcache 0 $debug] + set debug [textblock::frame -type $frametype -checkargs 0 -buildcache 0 $debug] #catch {punk::console::move_emitblock_return [expr {$debug_first_row + ($i * 6)}] 1 $debug} set result [dict get $mergedinfo result] @@ -1816,6 +1826,8 @@ proc punk::repl::console_debugview {editbuf consolewidth args} { } package require textblock variable debug_repl + variable frametype + if {$debug_repl <= 0} { return [dict create width 0 height 0 topleft {}] } @@ -1850,11 +1862,11 @@ proc punk::repl::console_debugview {editbuf consolewidth args} { } set debug_height [expr {[llength $lines]+2}] ;#framed height } errM]} { - set info [textblock::frame -checkargs 0 -buildcache 0 -title "[a red]error$RST" $errM] + set info [textblock::frame -type $frametype -checkargs 0 -buildcache 0 -title "[a red]error$RST" $errM] set debug_height [textblock::height $info] } else { #treat as ephemeral (unreusable) frames due to varying width & height - therefore set -buildcache 0 - set info [textblock::frame -checkargs 0 -buildcache 0 -ansiborder [a+ bold green] -title "[a cyan]debugview_raw$RST" $info] + set info [textblock::frame -type $frametype -checkargs 0 -buildcache 0 -ansiborder [a+ bold green] -title "[a cyan]debugview_raw$RST" $info] } set debug_width [textblock::widthtopline $info] @@ -1881,6 +1893,8 @@ proc punk::repl::console_editbufview {editbuf consolewidth args} { package require textblock upvar ::repl::editbuf_list editbuf_list + variable frametype + set defaults {-row 10 -rightmargin 0} set opts [dict merge $defaults $args] set opt_row [dict get $opts -row] @@ -1894,14 +1908,14 @@ proc punk::repl::console_editbufview {editbuf consolewidth args} { set info [punk::lib::list_as_lines $lines] } } editbuf_error]} { - set info [textblock::frame -checkargs 0 -buildcache 0 -title "[a red]error[a]" "$editbuf_error\n$::errorInfo"] + set info [textblock::frame -type $frametype -checkargs 0 -buildcache 0 -title "[a red]error[a]" "$editbuf_error\n$::errorInfo"] } else { set title "[a cyan]editbuf [expr {[llength $editbuf_list]-1}] lines [$editbuf linecount][a]" append title "[a+ yellow bold] col:[format %3s [$editbuf cursor_column]] row:[$editbuf cursor_row][a]" set row1 " lastchar:[ansistring VIEW -lf 1 [$editbuf last_char]] lastgrapheme:[ansistring VIEW -lf 1 [$editbuf last_grapheme]]" set row2 " lastansi:[ansistring VIEW -lf 1 [$editbuf last_ansi]]" set info [a+ green bold]$row1\n$row2[a]\n$info - set info [textblock::frame -checkargs 0 -buildcache 0 -ansiborder [a+ green bold] -title $title $info] + set info [textblock::frame -type $frametype -checkargs 0 -buildcache 0 -ansiborder [a+ green bold] -title $title $info] } set editbuf_width [textblock::widthtopline $info] set spacepatch [textblock::block $editbuf_width 2 " "] @@ -1915,8 +1929,10 @@ proc punk::repl::console_editbufview {editbuf consolewidth args} { return [dict create width $editbuf_width] } + proc punk::repl::console_controlnotification {message consolewidth consoleheight args} { package require textblock + variable frametype set defaults {-bottommargin 0 -rightmargin 0} set opts [dict merge $defaults $args] set opt_bottommargin [dict get $opts -bottommargin] @@ -1924,8 +1940,14 @@ proc punk::repl::console_controlnotification {message consolewidth consoleheight set messagelines [split $message \n] set message [lindex $messagelines 0] ;#only allow single line set info "[a+ bold red]$message[a]" - set hlt [dict get [textblock::framedef light] hlt] - set box [textblock::frame -checkargs 0 -boxmap [list tlc $hlt trc $hlt] -title $message -height 1] + + if {$frametype eq "ascii"} { + set box [textblock::frame -type ascii -checkargs 0 -boxmap [list tlc + trc +] -title $message -height 1] + } else { + set hlt [dict get [textblock::framedef light] hlt] + set box [textblock::frame -checkargs 0 -boxmap [list tlc $hlt trc $hlt] -title $message -height 1] + } + set notification_width [textblock::widthtopline $info] set box_offset [expr {$consolewidth - $notification_width - $opt_rightmargin}] set row [expr {$consoleheight - $opt_bottommargin}] diff --git a/src/modules/punk-0.1.tm b/src/modules/punk-0.1.tm index b4cd370e..8994be71 100644 --- a/src/modules/punk-0.1.tm +++ b/src/modules/punk-0.1.tm @@ -8435,16 +8435,23 @@ namespace eval punk { #todo - provide a mechanism to configure the default frametype everywhere and describe it in this help. - if {$cols <= 80} { - # Be conservative with frame types on narrow terminals for help. - # an 80x30 terminal is more likely to be an older style terminal and may not have unicode support. - # unicode on a non-unicode terminal is a bad experience - with the frame chars showing as garbage (e.g 3 chars per grapheme). - set frametype ascii - } else { - #unicode - set frametype light + + set frametype ascii ;#conservative default. + #if the test char width fails - it's likely we're on a very old terminal that doesn't support unicode at all. + if {![catch {punk::console::test_char_width \u00e9} testcharwidth]} { + if {$cols <= 80} { + # Be conservative with frame types on narrow terminals for help. + # an 80x30 terminal is more likely to be an older style terminal and may not have unicode support. + # unicode on a non-unicode terminal is a bad experience - with the frame chars showing as garbage (e.g 3 chars per grapheme). + set frametype ascii + } else { + if {$testcharwidth == 1} { + set frametype light ;#unicode box-drawing chars. + } + } } + # ------------------------------------------------------- set logoblock "" if {[catch { diff --git a/src/modules/punk/repl-999999.0a1.0.tm b/src/modules/punk/repl-999999.0a1.0.tm index 1c9896d0..aa4a43e2 100644 --- a/src/modules/punk/repl-999999.0a1.0.tm +++ b/src/modules/punk/repl-999999.0a1.0.tm @@ -21,6 +21,7 @@ unset stdin_info # ----------------------------------- + #------------------------------------------------------------------------------------- if {[package provide punk::libunknown] eq ""} { #maintenance - also in src/vfs/_config/punk_main.tcl @@ -162,6 +163,13 @@ namespace eval punk::repl { #todo - key on shell/subshell tsv::set repl runchunks-0 [list] ;#last_run_display + variable frametype + set frametype ascii; #conservative default + if {![catch {punk::console::test_char_width \u00e9} testcharwidth]} { + if {$testcharwidth == 1} { + set frametype light + } + } variable debug_repl 0 variable signal_control_c 0 @@ -991,6 +999,8 @@ namespace eval punk::repl::class { dict set o_chunk_info 0 [dict create micros [clock microseconds] type rendered] } method add_chunk {chunk} { + upvar ::punk::repl::frametype frametype + #we still split on lf - but each physical line may contain horizontal or vertical movements so we need to feed each line in and possibly get an overflow_right and unapplied and cursor-movent return info lappend o_chunk_list $chunk ;#may contain newlines,horizontal/vertical movements etc - all ok dict set o_chunk_info [expr {[llength $o_chunk_list] -1}] [dict create micros [clock microseconds] type raw] @@ -1065,7 +1075,7 @@ namespace eval punk::repl::class { append debug \n [showdict $dinfo] append debug \n "input:[ansistring VIEW -lf 1 -vt 1 $new0] before row:$o_cursor_row after row: $result_row before col:$o_cursor_col after col:$result_col" package require textblock - set debug [textblock::frame -checkargs 0 -buildcache 0 $debug] + set debug [textblock::frame -type $frametype -checkargs 0 -buildcache 0 $debug] if {![punk::console::vt52]} { catch {punk::console::move_emitblock_return $debug_first_row 1 $debug} } else { @@ -1148,7 +1158,7 @@ namespace eval punk::repl::class { set debug "add_chunk$i" append debug \n $mergedinfo append debug \n "input:[ansistring VIEW -lf 1 -vt 1 $p]" - set debug [textblock::frame -checkargs 0 -buildcache 0 $debug] + set debug [textblock::frame -type $frametype -checkargs 0 -buildcache 0 $debug] #catch {punk::console::move_emitblock_return [expr {$debug_first_row + ($i * 6)}] 1 $debug} set result [dict get $mergedinfo result] @@ -1816,6 +1826,8 @@ proc punk::repl::console_debugview {editbuf consolewidth args} { } package require textblock variable debug_repl + variable frametype + if {$debug_repl <= 0} { return [dict create width 0 height 0 topleft {}] } @@ -1850,11 +1862,11 @@ proc punk::repl::console_debugview {editbuf consolewidth args} { } set debug_height [expr {[llength $lines]+2}] ;#framed height } errM]} { - set info [textblock::frame -checkargs 0 -buildcache 0 -title "[a red]error$RST" $errM] + set info [textblock::frame -type $frametype -checkargs 0 -buildcache 0 -title "[a red]error$RST" $errM] set debug_height [textblock::height $info] } else { #treat as ephemeral (unreusable) frames due to varying width & height - therefore set -buildcache 0 - set info [textblock::frame -checkargs 0 -buildcache 0 -ansiborder [a+ bold green] -title "[a cyan]debugview_raw$RST" $info] + set info [textblock::frame -type $frametype -checkargs 0 -buildcache 0 -ansiborder [a+ bold green] -title "[a cyan]debugview_raw$RST" $info] } set debug_width [textblock::widthtopline $info] @@ -1881,6 +1893,8 @@ proc punk::repl::console_editbufview {editbuf consolewidth args} { package require textblock upvar ::repl::editbuf_list editbuf_list + variable frametype + set defaults {-row 10 -rightmargin 0} set opts [dict merge $defaults $args] set opt_row [dict get $opts -row] @@ -1894,14 +1908,14 @@ proc punk::repl::console_editbufview {editbuf consolewidth args} { set info [punk::lib::list_as_lines $lines] } } editbuf_error]} { - set info [textblock::frame -checkargs 0 -buildcache 0 -title "[a red]error[a]" "$editbuf_error\n$::errorInfo"] + set info [textblock::frame -type $frametype -checkargs 0 -buildcache 0 -title "[a red]error[a]" "$editbuf_error\n$::errorInfo"] } else { set title "[a cyan]editbuf [expr {[llength $editbuf_list]-1}] lines [$editbuf linecount][a]" append title "[a+ yellow bold] col:[format %3s [$editbuf cursor_column]] row:[$editbuf cursor_row][a]" set row1 " lastchar:[ansistring VIEW -lf 1 [$editbuf last_char]] lastgrapheme:[ansistring VIEW -lf 1 [$editbuf last_grapheme]]" set row2 " lastansi:[ansistring VIEW -lf 1 [$editbuf last_ansi]]" set info [a+ green bold]$row1\n$row2[a]\n$info - set info [textblock::frame -checkargs 0 -buildcache 0 -ansiborder [a+ green bold] -title $title $info] + set info [textblock::frame -type $frametype -checkargs 0 -buildcache 0 -ansiborder [a+ green bold] -title $title $info] } set editbuf_width [textblock::widthtopline $info] set spacepatch [textblock::block $editbuf_width 2 " "] @@ -1915,8 +1929,10 @@ proc punk::repl::console_editbufview {editbuf consolewidth args} { return [dict create width $editbuf_width] } + proc punk::repl::console_controlnotification {message consolewidth consoleheight args} { package require textblock + variable frametype set defaults {-bottommargin 0 -rightmargin 0} set opts [dict merge $defaults $args] set opt_bottommargin [dict get $opts -bottommargin] @@ -1924,8 +1940,14 @@ proc punk::repl::console_controlnotification {message consolewidth consoleheight set messagelines [split $message \n] set message [lindex $messagelines 0] ;#only allow single line set info "[a+ bold red]$message[a]" - set hlt [dict get [textblock::framedef light] hlt] - set box [textblock::frame -checkargs 0 -boxmap [list tlc $hlt trc $hlt] -title $message -height 1] + + if {$frametype eq "ascii"} { + set box [textblock::frame -type ascii -checkargs 0 -boxmap [list tlc + trc +] -title $message -height 1] + } else { + set hlt [dict get [textblock::framedef light] hlt] + set box [textblock::frame -checkargs 0 -boxmap [list tlc $hlt trc $hlt] -title $message -height 1] + } + set notification_width [textblock::widthtopline $info] set box_offset [expr {$consolewidth - $notification_width - $opt_rightmargin}] set row [expr {$consoleheight - $opt_bottommargin}]