Browse Source

raw mode repl fixes, ansi fixes

master
Julian Noble 10 months ago
parent
commit
dd2bfa76c5
  1. 5
      src/modules/punk-0.1.tm
  2. 69
      src/modules/punk/ansi-999999.0a1.0.tm
  3. 23
      src/modules/punk/char-999999.0a1.0.tm
  4. 125
      src/modules/punk/console-999999.0a1.0.tm
  5. 19
      src/modules/punk/lib-999999.0a1.0.tm
  6. 15
      src/modules/punk/mix/commandset/loadedlib-999999.0a1.0.tm
  7. 4
      src/modules/punk/mix/util-999999.0a1.0.tm
  8. 133
      src/modules/punk/repl-0.1.tm
  9. 3
      src/modules/punk/repo-999999.0a1.0.tm

5
src/modules/punk-0.1.tm

@ -6972,11 +6972,12 @@ namespace eval punk {
proc mode {raw_or_line} { proc mode {raw_or_line} {
set raw_or_line [string tolower $raw_or_line] set raw_or_line [string tolower $raw_or_line]
if {$raw_or_line eq "raw"} { if {$raw_or_line eq "raw"} {
punk::console::enableVirtualTerminal
punk::console::enableRaw punk::console::enableRaw
punk::console::enableVirtualTerminal
} elseif {$raw_or_line eq "line"} { } elseif {$raw_or_line eq "line"} {
#review -order. disableRaw has memory from enableRaw.. but but for line mode we want vt disabled - so call it after disableRaw (?)
punk::console::disableRaw punk::console::disableRaw
#vt disable? punk::console::disableVirtualTerminal
} else { } else {
error "punk::mode expected 'raw' or 'line' error "punk::mode expected 'raw' or 'line'
} }

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

@ -598,12 +598,13 @@ namespace eval punk::ansi {
proc move_column {col} { proc move_column {col} {
#*** !doctools #*** !doctools
#[call [fun move_column] [arg col]] #[call [fun move_column] [arg col]]
return \x1b\[${col}g return \x1b\[${col}G
} }
proc move_row {row} { proc move_row {row} {
#*** !doctools #*** !doctools
#[call [fun move_row] [arg row]] #[call [fun move_row] [arg row]]
return \x1b\[${row}G #[para]VPA - Vertical Line Position Absolute
return \x1b\[${row}d
} }
# -- --- --- --- --- # -- --- --- --- ---
@ -686,6 +687,22 @@ namespace eval punk::ansi {
return \033\[6n return \033\[6n
} }
proc request_cursor_information {} {
#*** !doctools
#[call [fun request_cursor_information]]
#[para]DECRQPSR (DEC Request Presentation State Report) for DECCCIR Cursor Information report
#[para]When written to the terminal, this sequence causes the terminal to emit cursor information to stdin
#[para]A stdin readloop will need to be in place to read this information
return \x1b\[1\$w
}
proc request_tabstops {} {
#*** !doctools
#[call [fun request_tabstops]]
#[para]DECRQPSR (DEC Request Presentation State Report) for DECTABSR Tab stop report
#[para]When written to the terminal, this sequence causes the terminal to emit tabstop information to stdin
return \x1b\[2\$w
}
#alternative to string terminator is \007 - #alternative to string terminator is \007 -
proc titleset {windowtitle} { proc titleset {windowtitle} {
@ -1254,7 +1271,7 @@ namespace eval punk::ansi::ansistring {
APC [list \x9f \ue03f]\ APC [list \x9f \ue03f]\
] ]
#it turns out we need pretty much everything for debugging #it turns out we need pretty much everything for debugging
set visuals [dict create\ set visuals_c0 [dict create\
NUL [list \x00 \u2400]\ NUL [list \x00 \u2400]\
SOH [list \x01 \u2401]\ SOH [list \x01 \u2401]\
STX [list \x02 \u2402]\ STX [list \x02 \u2402]\
@ -1282,41 +1299,71 @@ namespace eval punk::ansi::ansistring {
RS [list \x1e \u241e]\ RS [list \x1e \u241e]\
US [list \x1f \u241f]\ US [list \x1f \u241f]\
DEL [list \x7f \u2421]\ DEL [list \x7f \u2421]\
]
set visuals_c1 [dict create\
BPH [list \x82 \ue022]\
NBH [list \x83 \ue023]\
IND [list \x84 \ue024]\
NEL [list \x85 \ue025]\
SSA [list \x86 \ue026]\
ESA [list \x87 \ue027]\
HTS [list \x88 \ue028]\
HTJ [list \x89 \ue029]\
VTS [list \x8a \ue02a]\
PLD [list \x8b \ue02a]\
PLU [list \x8c \ue02c]\
RI [list \x8d \ue02d]\
SS2 [list \x8e \ue02e]\
SS3 [list \x8f \ue02f]\
DCS [list \x90 \ue030]\
PU1 [list \x91 \ue031]\
PU2 [list \x92 \ue032]\
STS [list \x93 \ue033]\
CCH [list \x94 \ue034]\
MW [list \x95 \ue035]\
SPA [list \x96 \ue036]\
EPA [list \x97 \ue037]\
SOS [list \x98 \ue038]\ SOS [list \x98 \ue038]\
SCI [list \x9a \ue03a]\
CSI [list \x9b \ue03b]\ CSI [list \x9b \ue03b]\
ST [list \x9c \ue03c]\ ST [list \x9c \ue03c]\
OSC [list \x9d \ue03d]\
PM [list \x9e \ue03e]\ PM [list \x9e \ue03e]\
APC [list \x9f \ue03f]\ APC [list \x9f \ue03f]\
] ]
set visuals_opt [dict create]
if {$opt_esc} { if {$opt_esc} {
dict set visuals VT [list \x1b \u241b] dict set visuals_opt ESC [list \x1b \u241b]
} }
if {$opt_cr} { if {$opt_cr} {
dict set visuals CR [list \x0d \u240d] dict set visuals_opt CR [list \x0d \u240d]
} }
if {$opt_lf} { if {$opt_lf} {
dict set visuals LF [list \x0a \u240a] dict set visuals_opt LF [list \x0a \u240a]
} }
if {$opt_vt} { if {$opt_vt} {
dict set visuals VT [list \x0b \u240b] dict set visuals_opt VT [list \x0b \u240b]
} }
if {$opt_ht} { if {$opt_ht} {
dict set visuals HT [list \x09 \u2409] dict set visuals_opt HT [list \x09 \u2409]
} }
if {$opt_bs} { if {$opt_bs} {
dict set visuals BS [list \x08 \u2408] dict set visuals_opt BS [list \x08 \u2408]
} }
if {$opt_sp} { if {$opt_sp} {
dict set visuals SP [list \x20 \u2420] dict set visuals_opt SP [list \x20 \u2420]
} }
set visuals [dict merge $visuals_opt $visuals_c0 $visuals_c1]
set charmap [list] set charmap [list]
dict for {nm chars} $visuals { dict for {nm chars} $visuals {
lappend charmap {*}$chars lappend charmap {*}$chars
} }
return [string map $charmap $string] return [string map $charmap $string]
#ISO2047 - 7bit - limited set, limited support
#test of ISO2047 - 7bit - limited set, limited support, somewhat obscure glyphs
#return [string map [list \033 \U2296 \007 \U237E] $string] #return [string map [list \033 \U2296 \007 \U237E] $string]
} }

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

@ -1796,12 +1796,21 @@ namespace eval punk::char {
#review - what about \r \t \b ? #review - what about \r \t \b ?
proc string_width {text} { proc string_width {text} {
#review is detecting \033 enough? what about 8-bit escapes? #review is detecting \033 enough? what about 8-bit escapes?
if {[string first \n $text] >= 0} { if {[string first \n $text] >= 0} {
error "string_width accepts only a single line" error "string_width accepts only a single line"
} }
if {[string first \033 $text] >= 0} {
error "string_width doesn't accept ansi escape sequences. Use punk::ansi::stripansi first"
} #we can c0 control characters after or while processing ansi escapes.
#we need to map remaining control characters to zero-width (under assumption we're not using a font/codepage that displays them - review!)
#anyway - we must allow things like raw ESC,DEL, NUL etc to pass through without error
#if {[string first \033 $text] >= 0} {
# error "string_width doesn't accept ansi escape sequences. Use punk::ansi::stripansi first"
#}
set re_ascii_c0 {[\U0000-\U001F]}
set text [regsub -all $re_ascii_c0 $text ""]
#todo - check double-width chars in unicode blocks.. try to do reasonably quicky #todo - check double-width chars in unicode blocks.. try to do reasonably quicky
#short-circuit basic cases #short-circuit basic cases
if {![regexp {[\uFF-\U10FFFF]} $text]} { if {![regexp {[\uFF-\U10FFFF]} $text]} {
@ -1858,13 +1867,13 @@ namespace eval punk::char {
#This shouldn't be called on text containing ansi codes! #This shouldn't be called on text containing ansi codes!
proc strip_nonprinting_ascii {str} { proc strip_nonprinting_ascii {str} {
#review - some single-byte 'control' chars have visual representations e.g ETX as heart #review - some single-byte 'control' chars have visual representations e.g ETX as heart depending on font/codepage
#It is currently used for screen display width calculations #It is currently used for screen display width calculations
#equivalent for various unicode combining chars etc? #equivalent for various unicode combining chars etc?
set map [list\ set map [list\
\007 ""\ \x00 ""\
[format %c 0] ""\ \x07 ""\
[format %c 0x7f] ""\ \x7f ""\
] ]
return [string map $map $str] return [string map $map $str]
} }

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

@ -80,6 +80,12 @@ namespace eval punk::console {
internal::abort_if_loop internal::abort_if_loop
tailcall enableVirtualTerminal tailcall enableVirtualTerminal
} }
proc disableVirtualTerminal {} {
#loopavoidancetoken (don't remove)
internal::define_windows_procs
internal::abort_if_loop
tailcall disableVirtualTerminal
}
} else { } else {
proc enableAnsi {} { proc enableAnsi {} {
#todo? #todo?
@ -232,6 +238,19 @@ namespace eval punk::console {
twapi::SetConsoleMode $h_in $newmode_in twapi::SetConsoleMode $h_in $newmode_in
return [list stdout [list from $oldmode_out to $newmode_out] stdin [list from $oldmode_in to $newmode_in]] return [list stdout [list from $oldmode_out to $newmode_out] stdin [list from $oldmode_in to $newmode_in]]
} }
proc [namespace parent]::disableVirtualTerminal {} {
set h_out [twapi::get_console_handle stdout]
set oldmode_out [twapi::GetConsoleMode $h_out]
set newmode_out [expr {$oldmode_out & ~4}]
twapi::SetConsoleMode $h_out $newmode_out
set h_in [twapi::get_console_handle stdin]
set oldmode_in [twapi::GetConsoleMode $h_in]
set newmode_in [expr {$oldmode_in & ~0x200}]
twapi::SetConsoleMode $h_in $newmode_in
return [list stdout [list from $oldmode_out to $newmode_out] stdin [list from $oldmode_in to $newmode_in]]
}
proc [namespace parent]::enableProcessedInput {} { proc [namespace parent]::enableProcessedInput {} {
set h_in [twapi::get_console_handle stdin] set h_in [twapi::get_console_handle stdin]
@ -250,6 +269,27 @@ namespace eval punk::console {
proc [namespace parent]::enableRaw {{channel stdin}} { proc [namespace parent]::enableRaw {{channel stdin}} {
variable is_raw
#review - change to modify_console_input_mode
#set console_handle [twapi::GetStdHandle -10]
set console_handle [twapi::get_console_handle stdin]
#returns dictionary
#e.g -processedinput 1 -lineinput 1 -echoinput 1 -windowinput 0 -mouseinput 0 -insertmode 1 -quickeditmode 1 -extendedmode 1 -autoposition 0
set oldmode [twapi::get_console_input_mode]
twapi::modify_console_input_mode $console_handle -lineinput 0 -echoinput 0
# Turn off the echo and line-editing bits
#set newmode [dict merge $oldmode [dict create -lineinput 0 -echoinput 0]]
set newmode [twapi::get_console_input_mode]
set is_raw 1
#don't disable handler - it will detect is_raw
### twapi::set_console_control_handler {}
return [list stdin [list from $oldmode to $newmode]]
}
#sometimes gives invalid handle.. (after stdin reopened?)
proc [namespace parent]::enableRaw1 {{channel stdin}} {
variable is_raw variable is_raw
#review - change to modify_console_input_mode #review - change to modify_console_input_mode
set console_handle [twapi::GetStdHandle -10] set console_handle [twapi::GetStdHandle -10]
@ -262,6 +302,19 @@ namespace eval punk::console {
return [list stdin [list from $oldmode to $newmode]] return [list stdin [list from $oldmode to $newmode]]
} }
proc [namespace parent]::disableRaw {{channel stdin}} { proc [namespace parent]::disableRaw {{channel stdin}} {
variable is_raw
set console_handle [twapi::get_console_handle stdin]
set oldmode [twapi::get_console_input_mode]
# Turn on the echo and line-editing bits
twapi::modify_console_input_mode $console_handle -lineinput 1 -echoinput 1
set newmode [twapi::get_console_input_mode]
set is_raw 0
return [list stdin [list from $oldmode to $newmode]]
}
proc [namespace parent]::disableRaw1 {{channel stdin}} {
variable is_raw variable is_raw
set console_handle [twapi::GetStdHandle -10] set console_handle [twapi::GetStdHandle -10]
set oldmode [twapi::GetConsoleMode $console_handle] set oldmode [twapi::GetConsoleMode $console_handle]
@ -289,7 +342,10 @@ namespace eval punk::console {
} }
} }
#review - 1 byte at a time seems inefficient.. #review - 1 byte at a time seems inefficient... but we don't have a way to peek or put back chars (?)
#todo - timeout - what if terminal doesn't put data on stdin?
#review - what if we slurp in data meant for main loop? Main loop probably needs to detect these responses and store them for lookup *instead* of this handler
#we may still need this handler if such a loop doesn't exist.
proc ansi_response_handler {chan accumulatorvar waitvar} { proc ansi_response_handler {chan accumulatorvar waitvar} {
set status [catch {read $chan 1} bytes] set status [catch {read $chan 1} bytes]
if { $status != 0 } { if { $status != 0 } {
@ -457,9 +513,14 @@ namespace eval punk::console {
set accumulator ::punk::console::chunk set accumulator ::punk::console::chunk
set waitvar ::punk::console::chunkdone set waitvar ::punk::console::chunkdone
set existing_handler [fileevent stdin readable] set existing_handler [fileevent stdin readable] ;#review!
set $waitvar "" set $waitvar ""
#todo - test and save rawstate so we don't disableRaw if terminal was already raw
set stdin_state [fconfigure stdin]
#todo - only use own handler if an existing stdin handler not present.. (or console is in line mode)
#todo - test and save rawstate so we don't disableRaw if console was already raw
if {!$::punk::console::is_raw} { if {!$::punk::console::is_raw} {
set was_raw 0 set was_raw 0
enableRaw enableRaw
@ -467,8 +528,7 @@ namespace eval punk::console {
set was_raw 1 set was_raw 1
} }
fconfigure stdin -blocking 0 fconfigure stdin -blocking 0
#review #
#fconfigure stdin -blocking 0 -inputmode raw
fileevent stdin readable [list ::punk::console::internal::ansi_response_handler stdin $accumulator $waitvar] fileevent stdin readable [list ::punk::console::internal::ansi_response_handler stdin $accumulator $waitvar]
# - stderr vs stdout # - stderr vs stdout
@ -479,31 +539,46 @@ namespace eval punk::console {
puts -nonewline stdout \033\[6n ;flush stdout puts -nonewline stdout \033\[6n ;flush stdout
after 0 {update idletasks} after 0 {update idletasks}
#response from terminal
#e.g \033\[46;1R #e.g \033\[46;1R
#todo - reset
#todo - make timeout configurable?
set cancel_timeout_id [after 1500 {set $waitvar timedout}]
set info "" set info ""
if {[set $waitvar] eq ""} { if {[set $waitvar] eq ""} {
vwait $waitvar vwait $waitvar
} }
if {$waitvar ne "timedout"} {
after cancel $cancel_timeout_id
} else {
return ""
}
if {$was_raw == 0} { if {$was_raw == 0} {
disableRaw disableRaw
} }
#fconfigure stdin -inputmode normal
if {[string length $existing_handler]} { if {[string length $existing_handler]} {
fileevent stdin readable $existing_handler fileevent stdin readable $existing_handler
} }
#response handler automatically removes it's own fileevent #response handler automatically removes it's own fileevent
#restore stdin state
fconfigure stdin -blocking [dict get $stdin_state -blocking]
set info [set $accumulator] set info [set $accumulator]
set start [string first \x1b $info] set start [string first \x1b $info]
if {$start > 0} { if {$start > 0} {
set other [string range $info 0 $start-1] set other [string range $info 0 $start-1]
#!!!!! TODO #!!!!! TODO
# Log this somehwere? Work out how to stop it happening? # Log this somewhere? Work out how to stop it happening?
#puts stderr "Warning - get_cursor_pos read extra data at start - '$other'" #puts stderr "Warning - get_cursor_pos read extra data at start - '$other'"
set info [string range $info $start end] set info [string range $info $start end]
} }
#set punk::console::chunk "" #set punk::console::chunk ""
set data [string range $info 2 end-1] set data [string range $info 2 end-1]
return $data return $data
@ -523,12 +598,21 @@ namespace eval punk::console {
if {[catch { if {[catch {
lassign [split [punk::console::get_cursor_pos] ";"] _row1 col1 lassign [split [punk::console::get_cursor_pos] ";"] _row1 col1
} errM]} { } errM]} {
puts stderr "Cannot test_char_width - may be no console? Error message from get_cursor_pos: $errM" puts stderr "Cannot test_char_width for '[punk::ansi::ansistring VIEW $char_or_string]' - may be no console? Error message from get_cursor_pos: $errM"
return
}
if {![string is integer -strict $col1]} {
puts stderr "Could not get response from get_cursor_pos"
return return
} }
puts -nonewline stdout $char_or_string puts -nonewline stdout $char_or_string
lassign [split [punk::console::get_cursor_pos] ";"] _row2 col2 lassign [split [punk::console::get_cursor_pos] ";"] _row2 col2
if {![string is integer -strict $col2]} {
puts stderr "Could not get response from get_cursor_pos"
return
}
if {!$emit} { if {!$emit} {
puts -nonewline stdout \033\[2K\033\[1G puts -nonewline stdout \033\[2K\033\[1G
} }
@ -654,19 +738,25 @@ namespace eval punk::console {
} }
move $orig_row $orig_col move $orig_row $orig_col
} }
proc save_cursor {} {
puts -nonewline stdout [punk::ansi::save_cursor]
}
proc restore_cursor {} {
puts -nonewline stdout [punk::ansi::restore_cursor]
}
proc scroll_up {n} { proc scroll_up {n} {
puts -nonewline stdout [punk::ansi::scroll_up] puts -nonewline stdout [punk::ansi::scroll_up]
} }
proc scroll_down {n} { proc scroll_down {n} {
puts -nonewline stdout [punk::ansi::scroll_down] puts -nonewline stdout [punk::ansi::scroll_down]
} }
#review - worth the extra microseconds to inline? might be
#review - worth the extra microseconds to inline? might be if used in for example prompt on every keypress.
#caller should build as much as possible using the punk::ansi versions to avoid extra puts calls
proc save_cursor {} {
#*** !doctools
#[call [fun save_cursor]]
puts -nonewline \x1b\[s
}
proc restore_cursor {} {
#*** !doctools
#[call [fun restore_cursor]]
puts -nonewline \x1b\[u
}
proc insert_spaces {count} { proc insert_spaces {count} {
puts -nonewline stdout \x1b\[${count}@ puts -nonewline stdout \x1b\[${count}@
} }
@ -712,7 +802,8 @@ namespace eval punk::console {
#puts -nonewline [punk::ansi::erase_eol]$blanks;move_emit_return this $col $text #puts -nonewline [punk::ansi::erase_eol]$blanks;move_emit_return this $col $text
#puts -nonewline [move_emit_return this $col [punk::ansi::insert_spaces 150]$text] #puts -nonewline [move_emit_return this $col [punk::ansi::insert_spaces 150]$text]
save_cursor save_cursor
move_emit_return this $col [punk::ansi::move_forward 50][punk::ansi::insert_spaces 150][punk::ansi::move_back 50][punk::ansi::move_forward $col]$text #move_emit_return this $col [punk::ansi::move_forward 50][punk::ansi::insert_spaces 150][punk::ansi::move_back 50][punk::ansi::move_forward $col]$text
puts -nonewline [punk::ansi::insert_spaces 150][punk::ansi::move_column $col]$text
restore_cursor restore_cursor
} }
proc move_emit_return {row col data args} { proc move_emit_return {row col data args} {

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

@ -488,9 +488,11 @@ namespace eval punk::lib {
proc askuser {question} { proc askuser {question} {
#*** !doctools #*** !doctools
#[call [fun askuser] [arg question]] #[call [fun askuser] [arg question]]
#[para]A very basic utility to read an answer from stdin #[para]A basic utility to read an answer from stdin
#[para]The prompt is written to the terminal and then it waits for a user to type something #[para]The prompt is written to the terminal and then it waits for a user to type something
#[para]stdin is temporarily configured to blocking and then put back in its original state in case it wasn't already so. #[para]stdin is temporarily configured to blocking and then put back in its original state in case it wasn't already so.
#[para]If the terminal is using punk::console and is in raw mode - the terminal will temporarily be put in line mode.
#[para](Generic terminal raw vs linemode detection not yet present)
#[para]The user must hit enter to submit the response #[para]The user must hit enter to submit the response
#[para]The return value is the string if any that was typed prior to hitting enter. #[para]The return value is the string if any that was typed prior to hitting enter.
#[para]The question argument can be manually colourised using the various punk::ansi funcitons #[para]The question argument can be manually colourised using the various punk::ansi funcitons
@ -505,9 +507,22 @@ namespace eval punk::lib {
puts stdout $question puts stdout $question
flush stdout flush stdout
set stdin_state [fconfigure stdin] set stdin_state [fconfigure stdin]
if {[catch {
package require punk::console
set console_raw [set ::punk::console::is_raw]
} err_console]} {
#assume normal line mode
set console_raw 0
}
try { try {
fconfigure stdin -blocking 1 fconfigure stdin -blocking 1
set answer [gets stdin] if {$console_raw} {
punk::console::disableRaw
set answer [gets stdin]
punk::console::enableRaw
} else {
set answer [gets stdin]
}
} finally { } finally {
fconfigure stdin -blocking [dict get $stdin_state -blocking] fconfigure stdin -blocking [dict get $stdin_state -blocking]
} }

15
src/modules/punk/mix/commandset/loadedlib-999999.0a1.0.tm

@ -18,6 +18,7 @@
## Requirements ## Requirements
##e.g package require frobz ##e.g package require frobz
package require punk::ns package require punk::ns
package require punk::lib
@ -463,11 +464,8 @@ namespace eval punk::mix::commandset::loadedlib {
puts stdout "---" puts stdout "---"
puts stdout "$loadinfo" puts stdout "$loadinfo"
puts stdout "---" puts stdout "---"
puts stdout "Proceed to create ${pkgtail}-${ver}.tm module? Y|N" set question "Proceed to create ${pkgtail}-${ver}.tm module? Y|N"
set stdin_state [fconfigure stdin] set answer [punk::lib::askuser $question] ;#takes account of previous stdin state and terminal raw vs line state
fconfigure stdin -blocking 1
set answer [string tolower [gets stdin]]
fconfigure stdin -blocking [dict get $stdin_state -blocking]
if {$answer ne "y"} { if {$answer ne "y"} {
puts stderr "mix libcopy.asmodule aborting due to user response '$answer' (required Y|y to proceed) use -askme 0 to avoid prompts." puts stderr "mix libcopy.asmodule aborting due to user response '$answer' (required Y|y to proceed) use -askme 0 to avoid prompts."
return return
@ -486,11 +484,8 @@ namespace eval punk::mix::commandset::loadedlib {
if {[file exists $target_path]} { if {[file exists $target_path]} {
puts stdout "WARNING - module already exists at $target_path" puts stdout "WARNING - module already exists at $target_path"
if {$opt_askme} { if {$opt_askme} {
puts stdout "Copy anyway? Y|N" set question "Copy anyway? Y|N"
set stdin_state [fconfigure stdin] set answer [punk::lib::askuser $question]
fconfigure stdin -blocking 1
set answer [string tolower [gets stdin]]
fconfigure stdin -blocking [dict get $stdin_state -blocking]
if {$answer ne "y"} { if {$answer ne "y"} {
puts stderr "mix libcopy.asmodule aborting due to user response '$answer' (required Y|y to proceed) use -askme 0 to avoid prompts." puts stderr "mix libcopy.asmodule aborting due to user response '$answer' (required Y|y to proceed) use -askme 0 to avoid prompts."
return return

4
src/modules/punk/mix/util-999999.0a1.0.tm

@ -182,6 +182,9 @@ namespace eval punk::mix::util {
} }
proc askuser {question} { proc askuser {question} {
if {![catch {package require punk::lib}]} {
return [punk::lib::askuser $question] ;#takes account of terminal mode raw vs line (if punk::console used)
}
puts stdout $question puts stdout $question
flush stdout flush stdout
set stdin_state [fconfigure stdin] set stdin_state [fconfigure stdin]
@ -191,6 +194,7 @@ namespace eval punk::mix::util {
return $answer return $answer
} }
#review - can be surprising if caller unaware it uses try
proc do_in_path {path script} { proc do_in_path {path script} {
#from ::kettle::path::in #from ::kettle::path::in
set here [pwd] set here [pwd]

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

@ -146,8 +146,10 @@ if {$::tcl_platform(platform) eq "windows"} {
incr ::repl::signal_control_c incr ::repl::signal_control_c
#rputs stderr "* console_control: $args" #rputs stderr "* console_control: $args"
if {$::punk::console::is_raw} { if {$::punk::console::is_raw} {
#how to let rawmode loop handle it? It doesn't seem to get through #how to let rawmode loop handle it? It doesn't seem to get through if we return 0
return 0 puts stderr "ctrl-c while in raw mode"
flush stderr
return 42
} }
#note - returning 0 means pass event to other handlers including OS default handler #note - returning 0 means pass event to other handlers including OS default handler
if {$::repl::signal_control_c <= 2} { if {$::repl::signal_control_c <= 2} {
@ -652,6 +654,7 @@ proc repl::doprompt {prompt {col {green bold}}} {
} }
#this sort of works - but steals some of our stdin data ? review #this sort of works - but steals some of our stdin data ? review
#
#lassign [punk::console::get_cursor_pos_list] column row #lassign [punk::console::get_cursor_pos_list] column row
#if {$row != 1} { #if {$row != 1} {
# set c "\n" # set c "\n"
@ -1101,11 +1104,11 @@ proc repl::repl_handler {inputchan prompt_config} {
while {[string length [set chunk [read $inputchan 1024]]] >= 0 && $lc < $linemax & $numreads < $maxreads} { while {[string length [set chunk [read $inputchan 1024]]] >= 0 && $lc < $linemax & $numreads < $maxreads} {
set chunklen [string length $chunk] set chunklen [string length $chunk]
if {$chunklen > 0} { if {$chunklen > 0} {
set info1 "chunk $chunklen bytes->[ansistring VIEW -lf 1 -vt 1 $chunk]" set info1 "read $chunklen bytes->[ansistring VIEW -lf 1 -vt 1 $chunk]"
#it's strange - but apparently terminals use a lone cr to represent enter #it's strange - but apparently terminals use a lone cr to represent enter
#You can insert an lf using ctrl-j - and of course stdin could have crlf or lf #You can insert an lf using ctrl-j - and of course stdin could have crlf or lf
#pasting from notepad++ with mixed line endings seems to paste everything ok #pasting from notepad++ with mixed line endings seems to paste everything ok
#we don't really know the source of input - and whether a read has potentially chopped a crl in half.. #we don't really know the source of input keyboard vs paste vs pipe - and whether a read has potentially chopped a crl in half..
#possibly no real way to determine that. We could wait a small time to see if there's more data coming.. and potentially impact performance. #possibly no real way to determine that. We could wait a small time to see if there's more data coming.. and potentially impact performance.
#Instead we'll try to make sense of it here. #Instead we'll try to make sense of it here.
@ -1124,19 +1127,42 @@ proc repl::repl_handler {inputchan prompt_config} {
#could be a sequence of cr's from holding enter key #could be a sequence of cr's from holding enter key
} }
#esc or ctrl-lb
if {$chunk eq "\x1b"} {
#return
set readingchunk ""
set stdinlines [list "\x1b"]
set commandstr ""
set chunk ""
screen_last_char_add \x1b stdin escape
break
}
if {$chunk eq "\x1b\[D"} {
rputs stderr "${debugprompt}arrow-left D"
#set commandstr ""
#punk::console::move_back 1
}
#if we get just ctrl-c in one chunk #if we get just ctrl-c in one chunk
#ctrl-c
if {$chunk eq "\x03"} { if {$chunk eq "\x03"} {
::repl::term::handler_console_control "ctrl-c_via_rawloop" #::repl::term::handler_console_control "ctrl-c_via_rawloop"
return return
} }
#for now - exit with small delay for tidyup #for now - exit with small delay for tidyup
#ctrl-z
if {$chunk eq "\x1a"} { if {$chunk eq "\x1a"} {
::repl::term::handler_console_control "ctrl-z_via_rawloop" #::repl::term::handler_console_control "ctrl-z_via_rawloop"
punk::mode line
after 1000 exit after 1000 exit
return return
} }
#try to brutally terminate process #ctrl-bslash
if {$chunk eq "\x1c"} { if {$chunk eq "\x1c"} {
#try to brutally terminate process
#attempt to leave terminal in a reasonable state
punk::mode line
after 200
exit 42 exit 42
} }
append readingchunk $chunk append readingchunk $chunk
@ -1184,8 +1210,50 @@ proc repl::repl_handler {inputchan prompt_config} {
set lastoutchar "" set lastoutchar ""
set lasterrchar "" set lasterrchar ""
set pad [string repeat " " [string length $line]]
set line [overtype::renderline $pad $line] #consider \x1b as text on console vs \x1b the character
#review - if we're getting these actual escape characters in line mode.. something is off - let's emit something instead of trying to interpret as a command and failing.
#This tends to happen when some sort of readline not avaialbe ie on unix or mintty in windows
#this only captures leading escape.. as an aid to diagnosis e.g <sp><right-arrow> won't be caught and the user will need to close the right bracket to complete the bogus command
#we may need to think about legitimate raw escapes in commands e.g from pipes or script files, vs via console?
#esc key or ctrl-lb followed by enter
if {$line eq "\x1b"} {
#abort current command
if {$linenum == 0} {
doprompt "E% " {yellow bold}
#screen_last_char_add " " empty empty
} else {
doprompt "\nE% " {yellow bold}
#screen_last_char_add "\n" empty empty ;#add \n to indicate noclearance required
}
incr linenum
continue
} else {
if {$line eq "\x1b\[C"} {
rputs stderr "${debugprompt}arrow-right C"
#set commandstr ""
}
if {$line eq "\x1b\[D"} {
#rputs stderr "${debugprompt}arrow-left D"
#set commandstr ""
#punk::console::move_back 1
}
if {$line eq "\x1b\[A"} {
rputs stderr "${debugprompt}arrow-up A"
}
if {$line eq "\x1b\[B"} {
rputs stderr "arrow-down B"
}
if {[string match "\x1b*" $line]} {
rputs stderr "${debugprompt}esc - '[punk::ansi::ansistring::VIEW $line]'"
#set commandstr [punk::ansi::stripansi $commandstr]
}
}
if {$commandstr ne ""} {
append commandstr \n
}
set stdinconf [fconfigure stdin] set stdinconf [fconfigure stdin]
if {$::tcl_platform(platform) eq "windows" && [dict get $stdinconf -encoding] ni [list unicode utf-16]} { if {$::tcl_platform(platform) eq "windows" && [dict get $stdinconf -encoding] ni [list unicode utf-16]} {
@ -1201,49 +1269,32 @@ proc repl::repl_handler {inputchan prompt_config} {
#puts "--stdin> [fconfigure stdin]" #puts "--stdin> [fconfigure stdin]"
append commandstr $line
#puts "1=============>[string length $commandstr] bytes , [string map [list \r -r- \n -n-] $commandstr] , info complete:[info complete $line]" append commandstr $line
puts "1=============>[string length $commandstr] bytes , [ansistring VIEW $commandstr] , info complete:[info complete $line]"
set commandstr [string range $commandstr 0 end-3] set commandstr [string range $commandstr 0 end-3]
set commandstr [encoding convertfrom utf-16be $commandstr] ;#This is weird - but it seemt to be big endian! set commandstr [encoding convertfrom utf-16be $commandstr] ;#This is weird - but it seems to be big endian?
set commandstr [string trimright $commandstr] set commandstr [string trimright $commandstr]
#puts "2=============>[string length $commandstr] bytes , [string map [list \r -r- \n -n-] $commandstr] , info complete:[info complete $line]" #puts "2=============>[string length $commandstr] bytes , [string map [list \r -r- \n -n-] $commandstr] , info complete:[info complete $line]"
append commandstr \n
} else { } else {
#append commandstr $line #append commandstr $line
#puts "0=============>[string length $commandstr] bytes , [string map [list \r -r- \n -n-] $commandstr] , info complete:[info complete $line]" #puts "0=============>[string length $commandstr] bytes , [string map [list \r -r- \n -n-] $commandstr] , info complete:[info complete $line]"
append commandstr $line\n append commandstr $line
} }
#puts "=============>[string length $commandstr] bytes , [string map [list \r -r- \n -n-] $commandstr] , info complete:[info complete $line]" #puts "=============>[string length $commandstr] bytes , [string map [list \r -r- \n -n-] $commandstr] , info complete:[info complete $line]"
set ::repl::last_repl_char "\n" ;#this is actually the eol from stdin set ::repl::last_repl_char "\n" ;#this is actually the eol from stdin
screen_last_char_add "\n" stdin $line screen_last_char_add "\n" stdin $line
#consider \x1b as text on console vs \x1b the character
#review - if we're getting these actual escape characters in line mode.. something is off - let's emit something instead of trying to interpret as a command and failing.
#This tends to happen when some sort of readline not avaialbe ie on unix or mintty in windows #append commandstr \n
#this only captures leading escape.. as an aid to diagnosis e.g <sp><right-arrow> won't be caught and the user will need to close the right bracket to complete the bogus command
#we may need to think about legitimate raw escapes in commands e.g from pipes or script files, vs via console?
if {$commandstr eq "\x1b\[C\n"} {
rputs stderr "${debugprompt}arrow-right C"
set commandstr ""
}
if {$commandstr eq "\x1b\[D\n"} {
#rputs stderr "${debugprompt}arrow-left D"
#set commandstr ""
punk::console::move_back 1
}
if {$commandstr eq "\x1b\[A\n"} {
rputs stderr "${debugprompt}arrow-up A"
set commandstr ""
}
if {$commandstr eq "\x1b\[B\n"} {
rputs stderr "arrow-down B"
}
if {[string match "\x1b*" $commandstr]} {
rputs stderr "${debugprompt}esc - '[punk::ansi::ansistring::VIEW $commandstr]'"
set commandstr [punk::ansi::stripansi $commandstr]
}
if {[info complete $commandstr]} { if {[info complete $commandstr]} {
#set commandstr [overtype::renderline -overflow 1 "" $commandstr]
set ::repl::output_stdout "" set ::repl::output_stdout ""
set ::repl::output_stderr "" set ::repl::output_stderr ""
set outstack [list] set outstack [list]
@ -1346,6 +1397,7 @@ proc repl::repl_handler {inputchan prompt_config} {
if {$weirdns} { if {$weirdns} {
uplevel 1 {punk::ns::nseval $punk::ns::ns_current $run_command_string} uplevel 1 {punk::ns::nseval $punk::ns::ns_current $run_command_string}
} else { } else {
#puts stderr "--> [ansistring VIEW -lf 1 -vt 1 $run_command_string] <--"
uplevel 1 {namespace inscope $punk::ns::ns_current $run_command_string} uplevel 1 {namespace inscope $punk::ns::ns_current $run_command_string}
} }
} raw_result] } raw_result]
@ -1651,6 +1703,9 @@ proc repl::repl_handler {inputchan prompt_config} {
} repl_error]} { } repl_error]} {
puts stderr "error in repl_handler: $repl_error" puts stderr "error in repl_handler: $repl_error"
puts stderr "-------------"
puts stderr "$::errorInfo"
puts stderr "-------------"
set stdinreader [fileevent $inputchan readable] set stdinreader [fileevent $inputchan readable]
if {![string length $stdinreader]} { if {![string length $stdinreader]} {
puts stderr "*> stdin reader inactive" puts stderr "*> stdin reader inactive"

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

@ -139,6 +139,9 @@ namespace eval punk::repo {
} }
proc askuser {question} { proc askuser {question} {
if {![catch {package require punk::lib}]} {
return [punk::lib::askuser $question] ;#takes account of punk::console raw vs line
}
puts stdout $question puts stdout $question
flush stdout flush stdout
set stdin_state [fconfigure stdin] set stdin_state [fconfigure stdin]

Loading…
Cancel
Save