Browse Source
- 34 documentation-only PUNKARGS blocks added: punk::ansi::codetype (is_sgr_reset, has_sgr_leadingreset, is_cursor_move_in_line, has_all_effective, get_effective_types, is_gx/is_gx_open/is_gx_close, sgr_merge), punk::ansi::sequence_type (is_Fe7/is_Fe/is_Fe8, is_Fp, is_Fs, is_nF, is_3Fp, is_code7/is_code8/is_code, classify), punk::ansi::ta (detect_in_list, detectcode, detectcode_in_list, detect_g0, detect_open, detect_st_open, detect_csi, detect_sgr, split_at_codes, split_codes, split_codes_single, get_codes_single) and punk::ansi::ansistring (VIEW, COUNT, length, trimleft/trimright/trim, INDEX, INDEXCHAR, RANGE, INSERT, INDEXABSOLUTE, INDEXCOLUMNS, COLUMNINDEX). - ::punk::ansi::sequence_type added to the punk::args::register NAMESPACES list - the escape-form classifiers live there (a separate namespace from codetype) and were previously undiscoverable by the doc system. - New characterization suites: ta.test (12 tests - detect vs detectcode lone-CSI distinction, split_codes/split_codes_single/split_at_codes shapes and join round-trip, length/extract), codetype.test (11 tests - reset/leading-reset semantics, effective-state queries incl. bold-via-intensity, sgr_merge, sequence_type classify taxonomy); ansistring.test extended 1 -> 12 tests (INDEX/INDEXCODE/INDEXCHAR/RANGE/INSERT grapheme indexing with merged SGR-prefix tracking incl. end+1 state-after-string, INDEXCOLUMNS/COLUMNINDEX double-wide column mapping via a CJK fixture, INDEXABSOLUTE resolution, styled-whitespace trim, VIEW control-picture substitution). - Tests use literal escape strings (not a+) so results are independent of the punk::console colour state. Origin notes added to the usage-marking suites' local SGR helpers pointing at these canonical mechanisms and their coverage. - src/tests/modules/AGENTS.md child DOX index: entries added for the punk/ansi, punk/args and punk/ns suites (GAP-pin/goal mapping documented). - Verified: 44/44 ansi suite and the full ansi+args+ns sweep (232 passed, 1 pre-existing skip) on Tcl 9.0.3 and 8.7; make.tcl modules clean. Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.commaster
6 changed files with 1228 additions and 25 deletions
@ -1,3 +1,4 @@ |
|||||||
0.1.1 |
0.1.2 |
||||||
#First line must be a semantic version number |
#First line must be a semantic version number |
||||||
#all other lines are ignored. |
#all other lines are ignored. |
||||||
|
#0.1.2 - documentation-only: PUNKARGS argdoc blocks added for punk::ansi::codetype (is_sgr_reset, has_sgr_leadingreset, is_cursor_move_in_line, has_all_effective, get_effective_types, is_gx/is_gx_open/is_gx_close, sgr_merge), punk::ansi::sequence_type (is_Fe7/is_Fe/is_Fe8, is_Fp, is_Fs, is_nF, is_3Fp, is_code7/is_code8/is_code, classify), punk::ansi::ta (detect_in_list, detectcode, detectcode_in_list, detect_g0, detect_open, detect_st_open, detect_csi, detect_sgr, split_at_codes, split_codes, split_codes_single, get_codes_single) and punk::ansi::ansistring (VIEW, COUNT, length, trimleft/trimright/trim, INDEX, INDEXCHAR, RANGE, INSERT, INDEXABSOLUTE, INDEXCOLUMNS, COLUMNINDEX); ::punk::ansi::sequence_type added to punk::args::register NAMESPACES so its argdocs are discoverable |
||||||
|
|||||||
@ -1,23 +1,186 @@ |
|||||||
package require tcltest |
package require tcltest |
||||||
|
|
||||||
namespace eval ::testspace { |
package require punk::lib |
||||||
namespace import ::tcltest::* |
package require punk::ansi |
||||||
variable common { |
|
||||||
set result "" |
#ansistring index/code-tracking characterization extended 2026-07-10 alongside the |
||||||
} |
#PUNKARGS documentation pass over ansistring/ta/codetype (punk::ansi 0.1.2). |
||||||
|
#ansistring INDEX/INDEXCODE/RANGE are the canonical "ANSI in effect at a character |
||||||
|
#position" mechanisms in punk (the test-local SGR helpers in the punk::args/punk::ns |
||||||
|
#usage-marking suites are deliberately-independent miniatures of this) - so the |
||||||
test ansistring_insert_end_relative {test ansistring INSERT matches string insert behaviour for end-relative indices}\ |
#grapheme indexing, code-prefix merging and column mapping are pinned here. |
||||||
-setup $common -body { |
#ANSI codes are written as literal escape strings (not generated via a+) so the tests |
||||||
lappend result [ansistring INSERT re end d] |
#are independent of the punk::console colour on/off state. |
||||||
lappend result [ansistring INSERT re end-1 d] |
#The fixture uses a CJK double-wide grapheme (丁) to pin grapheme-vs-column |
||||||
}\ |
#distinctions. |
||||||
-cleanup { |
|
||||||
}\ |
namespace eval ::testspace { |
||||||
-result [list\ |
namespace import ::tcltest::* |
||||||
red rde |
variable common { |
||||||
] |
set result "" |
||||||
|
} |
||||||
} |
|
||||||
tcltest::cleanupTests ;#needed to produce test summary. |
variable RED "\x1b\[31m" |
||||||
|
variable RESET "\x1b\[0m" |
||||||
|
#a RED b <wide> BOLD c RESET d (graphemes: a b <wide> c d = 5) |
||||||
|
variable S "a\x1b\[31mb丁\x1b\[1mc\x1b\[0md" |
||||||
|
|
||||||
|
test ansistring_insert_end_relative {test ansistring INSERT matches string insert behaviour for end-relative indices}\ |
||||||
|
-setup $common -body { |
||||||
|
lappend result [ansistring INSERT re end d] |
||||||
|
lappend result [ansistring INSERT re end-1 d] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list\ |
||||||
|
red rde |
||||||
|
] |
||||||
|
|
||||||
|
test ansistring_count_and_length {COUNT counts graphemes, length counts stripped characters}\ |
||||||
|
-setup $common -body { |
||||||
|
variable S |
||||||
|
lappend result [ansistring COUNT $S] |
||||||
|
lappend result [ansistring length $S] |
||||||
|
lappend result [ansistring COUNT ""] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list 5 5 0] |
||||||
|
|
||||||
|
test ansistring_index_code_prefix {INDEX returns the grapheme prefixed with the merged SGR codes in effect}\ |
||||||
|
-setup $common -body { |
||||||
|
variable S |
||||||
|
#no code in effect at position 0 |
||||||
|
lappend result [ansistring INDEX $S 0] |
||||||
|
#red in effect at position 1 |
||||||
|
lappend result [ansistring INDEX $S 1] |
||||||
|
#red then bold merged into a single code at position 3 |
||||||
|
lappend result [ansistring INDEX $S 3] |
||||||
|
#the reset before 'd' restarts the tracked stack - reset code applied as prefix |
||||||
|
lappend result [ansistring INDEX $S end] |
||||||
|
#out of bounds - empty string as for tcl string index |
||||||
|
lappend result [ansistring INDEX $S 99] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list\ |
||||||
|
a\ |
||||||
|
"\x1b\[31mb"\ |
||||||
|
"\x1b\[1;31mc"\ |
||||||
|
"\x1b\[0md"\ |
||||||
|
{}\ |
||||||
|
] |
||||||
|
|
||||||
|
test ansistring_indexchar_stripped {INDEXCHAR returns just the grapheme}\ |
||||||
|
-setup $common -body { |
||||||
|
variable S |
||||||
|
lappend result [ansistring INDEXCHAR $S 1] |
||||||
|
lappend result [ansistring INDEXCHAR $S 2] |
||||||
|
lappend result [ansistring INDEXCHAR $S 99] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list b 丁 {}] |
||||||
|
|
||||||
|
test ansistring_indexcode_in_effect {INDEXCODE returns the merged code in effect at a position; end+1 gives the state after the string}\ |
||||||
|
-setup $common -body { |
||||||
|
variable S |
||||||
|
lappend result [ansistring INDEXCODE $S 0] |
||||||
|
lappend result [ansistring INDEXCODE $S 1] |
||||||
|
lappend result [ansistring INDEXCODE $S 3] |
||||||
|
lappend result [ansistring INDEXCODE $S end+1] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list\ |
||||||
|
{}\ |
||||||
|
"\x1b\[31m"\ |
||||||
|
"\x1b\[1;31m"\ |
||||||
|
"\x1b\[0m"\ |
||||||
|
] |
||||||
|
|
||||||
|
test ansistring_range_preserves_codes {RANGE returns the grapheme range with codes in effect preserved}\ |
||||||
|
-setup $common -body { |
||||||
|
variable S |
||||||
|
set r [ansistring RANGE $S 1 3] |
||||||
|
lappend result [punk::ansi::ansistrip $r] |
||||||
|
lappend result $r |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list\ |
||||||
|
"b丁c"\ |
||||||
|
"\x1b\[31mb丁\x1b\[1;31mc"\ |
||||||
|
] |
||||||
|
|
||||||
|
test ansistring_insert_inside_styled_run {INSERT places the string at the grapheme position within the styled run}\ |
||||||
|
-setup $common -body { |
||||||
|
variable S |
||||||
|
set r [ansistring INSERT $S 2 X] |
||||||
|
lappend result [punk::ansi::ansistrip $r] |
||||||
|
lappend result $r |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list\ |
||||||
|
"abX丁cd"\ |
||||||
|
"a\x1b\[31mbX丁\x1b\[1mc\x1b\[0md"\ |
||||||
|
] |
||||||
|
|
||||||
|
test ansistring_indexabsolute {INDEXABSOLUTE resolves index expressions against the content length - empty string when out of bounds}\ |
||||||
|
-setup $common -body { |
||||||
|
lappend result [ansistring INDEXABSOLUTE "abcde" 2 end end-1 7 -1 1+2] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list [list 2 4 3 {} {} 3]] |
||||||
|
|
||||||
|
test ansistring_indexcolumns_doublewide {INDEXCOLUMNS returns 1-based column extents - a pair spanning two columns for a double-wide grapheme}\ |
||||||
|
-setup $common -body { |
||||||
|
lappend result [ansistring INDEXCOLUMNS "a丁b" 0] |
||||||
|
lappend result [ansistring INDEXCOLUMNS "a丁b" 1] |
||||||
|
lappend result [ansistring INDEXCOLUMNS "a丁b" 2] |
||||||
|
lappend result [ansistring INDEXCOLUMNS "a丁b" 9] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list {1 1} {2 3} {4 4} {}] |
||||||
|
|
||||||
|
test ansistring_columnindex_doublewide {COLUMNINDEX maps either column of a double-wide grapheme to its index}\ |
||||||
|
-setup $common -body { |
||||||
|
lappend result [ansistring COLUMNINDEX "a丁b" 1] |
||||||
|
lappend result [ansistring COLUMNINDEX "a丁b" 2] |
||||||
|
lappend result [ansistring COLUMNINDEX "a丁b" 3] |
||||||
|
lappend result [ansistring COLUMNINDEX "a丁b" 4] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list 0 1 1 2] |
||||||
|
|
||||||
|
test ansistring_trim_preserves_codes {trim/trimleft/trimright remove whitespace (including styled whitespace) while preserving codes}\ |
||||||
|
-setup $common -body { |
||||||
|
variable RED |
||||||
|
variable RESET |
||||||
|
lappend result [ansistring trim " ${RED} x ${RESET} "] |
||||||
|
lappend result [ansistring trimleft " ${RED} x "] |
||||||
|
lappend result [ansistring trimright " x ${RESET} "] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list\ |
||||||
|
"\x1b\[31mx\x1b\[0m"\ |
||||||
|
"\x1b\[31mx "\ |
||||||
|
" x\x1b\[0m"\ |
||||||
|
] |
||||||
|
|
||||||
|
test ansistring_view_visuals {VIEW substitutes ESC and (by default) space with control-picture equivalents; -sp 0 leaves spaces}\ |
||||||
|
-setup $common -body { |
||||||
|
lappend result [ansistring VIEW "a b\x1b\[31m"] |
||||||
|
lappend result [ansistring VIEW -sp 0 "a b"] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list "a␠b␛\[31m" "a b"] |
||||||
|
|
||||||
|
} |
||||||
|
tcltest::cleanupTests ;#needed to produce test summary. |
||||||
|
|||||||
@ -0,0 +1,163 @@ |
|||||||
|
package require tcltest |
||||||
|
|
||||||
|
package require punk::ansi |
||||||
|
|
||||||
|
#Characterization of punk::ansi::codetype (single-code tests, effective-state queries, |
||||||
|
#SGR merging) and punk::ansi::sequence_type (escape-form classification) - added |
||||||
|
#2026-07-10 alongside the PUNKARGS documentation pass (punk::ansi 0.1.2). |
||||||
|
#These are the primitives behind the ANSI-in-effect tracking used by ansistring |
||||||
|
#INDEX/INDEXCODE and the usage-marking machinery, so their semantics are pinned directly. |
||||||
|
#ANSI codes are written as literal escape strings (not generated via a+) so the tests are |
||||||
|
#independent of the punk::console colour on/off state. |
||||||
|
|
||||||
|
namespace eval ::testspace { |
||||||
|
namespace import ::tcltest::* |
||||||
|
variable common { |
||||||
|
set result "" |
||||||
|
} |
||||||
|
|
||||||
|
variable RED "\x1b\[31m" |
||||||
|
variable BOLD "\x1b\[1m" |
||||||
|
variable RESET "\x1b\[0m" |
||||||
|
|
||||||
|
#--- single-code tests -------------------------------------------------------------------- |
||||||
|
|
||||||
|
test codetype_is_sgr {is_sgr: trailing SGR detected; non-SGR CSI is not}\ |
||||||
|
-setup $common -body { |
||||||
|
variable RED |
||||||
|
lappend result [punk::ansi::codetype::is_sgr $RED] |
||||||
|
lappend result [punk::ansi::codetype::is_sgr "\x1b\[2A"] |
||||||
|
lappend result [punk::ansi::codetype::is_sgr "plain"] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list 1 0 0] |
||||||
|
|
||||||
|
test codetype_is_sgr_reset {is_sgr_reset: pure trailing reset only - embedded or non-trailing resets not detected}\ |
||||||
|
-setup $common -body { |
||||||
|
variable RESET |
||||||
|
lappend result [punk::ansi::codetype::is_sgr_reset $RESET] |
||||||
|
lappend result [punk::ansi::codetype::is_sgr_reset "\x1b\[m"] |
||||||
|
#reset amongst other parameters is not a *pure* reset |
||||||
|
lappend result [punk::ansi::codetype::is_sgr_reset "\x1b\[31;0m"] |
||||||
|
#reset not at the very end is not detected |
||||||
|
lappend result [punk::ansi::codetype::is_sgr_reset "${RESET}x"] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list 1 1 0 0] |
||||||
|
|
||||||
|
test codetype_has_sgr_leadingreset {has_sgr_leadingreset: first parameter of the leading SGR must be a reset}\ |
||||||
|
-setup $common -body { |
||||||
|
lappend result [punk::ansi::codetype::has_sgr_leadingreset "\x1b\[0;31m"] |
||||||
|
lappend result [punk::ansi::codetype::has_sgr_leadingreset "\x1b\[31;0m"] |
||||||
|
lappend result [punk::ansi::codetype::has_sgr_leadingreset "\x1b\[m"] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list 1 0 1] |
||||||
|
|
||||||
|
test codetype_is_cursor_move_in_line {is_cursor_move_in_line: within-line moves (C/D/G) accepted, vertical moves not}\ |
||||||
|
-setup $common -body { |
||||||
|
lappend result [punk::ansi::codetype::is_cursor_move_in_line "\x1b\[5C"] |
||||||
|
lappend result [punk::ansi::codetype::is_cursor_move_in_line "\x1b\[3G"] |
||||||
|
lappend result [punk::ansi::codetype::is_cursor_move_in_line "\x1b\[2A"] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list 1 1 0] |
||||||
|
|
||||||
|
test codetype_is_gx_family {is_gx / is_gx_open / is_gx_close for G0/G1 charset sequences}\ |
||||||
|
-setup $common -body { |
||||||
|
lappend result [punk::ansi::codetype::is_gx "\x1b(0abc\x1b(B"] |
||||||
|
lappend result [punk::ansi::codetype::is_gx "\x1b(0abc"] |
||||||
|
lappend result [punk::ansi::codetype::is_gx_open "\x1b(0"] |
||||||
|
lappend result [punk::ansi::codetype::is_gx_close "\x1b(B"] |
||||||
|
lappend result [punk::ansi::codetype::is_gx_open "\x1b(B"] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list 1 0 1 1 0] |
||||||
|
|
||||||
|
#--- effective-state queries --------------------------------------------------------------- |
||||||
|
|
||||||
|
test codetype_has_any_effective {has_any_effective: state must survive to the end of the string}\ |
||||||
|
-setup $common -body { |
||||||
|
variable RED |
||||||
|
variable RESET |
||||||
|
lappend result [punk::ansi::codetype::has_any_effective "${RED}text" fg] |
||||||
|
#a trailing reset makes the red ineffective |
||||||
|
lappend result [punk::ansi::codetype::has_any_effective "${RED}text${RESET}" fg] |
||||||
|
#no bg was ever set |
||||||
|
lappend result [punk::ansi::codetype::has_any_effective "${RED}text" bg] |
||||||
|
#any-semantics: one of the requested states suffices |
||||||
|
lappend result [punk::ansi::codetype::has_any_effective "${RED}text" bg fg] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list 1 0 0 1] |
||||||
|
|
||||||
|
test codetype_has_all_effective {has_all_effective: every requested state must be in effect; bold matches intensity 1}\ |
||||||
|
-setup $common -body { |
||||||
|
variable RED |
||||||
|
variable BOLD |
||||||
|
lappend result [punk::ansi::codetype::has_all_effective "${RED}${BOLD}x" fg bold] |
||||||
|
lappend result [punk::ansi::codetype::has_all_effective "${RED}x" fg bold] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list 1 0] |
||||||
|
|
||||||
|
test codetype_get_effective_types {get_effective_types lists surviving states; a trailing reset clears them (the reset code itself still reports 'sgr')}\ |
||||||
|
-setup $common -body { |
||||||
|
variable RED |
||||||
|
variable BOLD |
||||||
|
variable RESET |
||||||
|
lappend result [punk::ansi::codetype::get_effective_types "${RED}${BOLD}x"] |
||||||
|
lappend result [punk::ansi::codetype::get_effective_types "${RED}x${RESET}"] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list {sgr intensity fg bold} sgr] |
||||||
|
|
||||||
|
test codetype_sgr_merge {sgr_merge combines separate SGR codes into a single code}\ |
||||||
|
-setup $common -body { |
||||||
|
variable RED |
||||||
|
variable BOLD |
||||||
|
lappend result [punk::ansi::codetype::sgr_merge [list $RED $BOLD]] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list "\x1b\[1;31m"] |
||||||
|
|
||||||
|
#--- sequence_type classification ----------------------------------------------------------- |
||||||
|
|
||||||
|
test sequence_type_classify {classify recognises Fp/Fe/Fs/nF/3Fp forms and unknown}\ |
||||||
|
-setup $common -body { |
||||||
|
variable RED |
||||||
|
lappend result [punk::ansi::sequence_type::classify $RED] |
||||||
|
lappend result [punk::ansi::sequence_type::classify "\x1b7"] |
||||||
|
lappend result [punk::ansi::sequence_type::classify "\x1bc"] |
||||||
|
lappend result [punk::ansi::sequence_type::classify "\x1b(0"] |
||||||
|
lappend result [punk::ansi::sequence_type::classify "\x1b#4"] |
||||||
|
lappend result [punk::ansi::sequence_type::classify "abc"] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list Fe Fp Fs 0Fp 3Fp unknown] |
||||||
|
|
||||||
|
test sequence_type_is_forms {is_Fp / is_Fs / is_nF / is_code form tests}\ |
||||||
|
-setup $common -body { |
||||||
|
variable RED |
||||||
|
lappend result [punk::ansi::sequence_type::is_Fp "\x1b7"] |
||||||
|
lappend result [punk::ansi::sequence_type::is_Fs "\x1bc"] |
||||||
|
lappend result [punk::ansi::sequence_type::is_nF "\x1b(0"] |
||||||
|
lappend result [punk::ansi::sequence_type::is_Fe7 $RED] |
||||||
|
lappend result [punk::ansi::sequence_type::is_code $RED] |
||||||
|
lappend result [punk::ansi::sequence_type::is_code "abc"] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list 1 1 1 1 1 0] |
||||||
|
} |
||||||
|
tcltest::cleanupTests ;#needed to produce test summary line. |
||||||
@ -0,0 +1,185 @@ |
|||||||
|
package require tcltest |
||||||
|
|
||||||
|
package require punk::ansi |
||||||
|
|
||||||
|
#Characterization of punk::ansi::ta (text-ansi) detection and splitting - added 2026-07-10 |
||||||
|
#alongside the PUNKARGS documentation pass over ta/codetype/sequence_type/ansistring |
||||||
|
#(punk::ansi 0.1.2). These functions underpin most ANSI processing in punk (including the |
||||||
|
#character-position code tracking that ansistring INDEX/INDEXCODE and opunk::Str INDEXCODE |
||||||
|
#build on), so their splitting invariants are pinned directly here. |
||||||
|
#ANSI codes are written as literal escape strings (not generated via a+) so the tests are |
||||||
|
#independent of the punk::console colour on/off state. |
||||||
|
|
||||||
|
namespace eval ::testspace { |
||||||
|
namespace import ::tcltest::* |
||||||
|
variable common { |
||||||
|
set result "" |
||||||
|
} |
||||||
|
|
||||||
|
variable RED "\x1b\[31m" |
||||||
|
variable BOLD "\x1b\[1m" |
||||||
|
variable RESET "\x1b\[0m" |
||||||
|
|
||||||
|
#--- detection ---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
test ta_detect_basics {detect: complete codes detected, plain text and truncated sequences are not}\ |
||||||
|
-setup $common -body { |
||||||
|
variable RED |
||||||
|
lappend result [punk::ansi::ta::detect "a${RED}b"] |
||||||
|
lappend result [punk::ansi::ta::detect "plain text"] |
||||||
|
#a lone trailing ESC is not a complete code |
||||||
|
lappend result [punk::ansi::ta::detect "abc\x1b"] |
||||||
|
#a lone CSI opening is not a *complete* code either |
||||||
|
lappend result [punk::ansi::ta::detect "abc\x1b\["] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list 1 0 0 0] |
||||||
|
|
||||||
|
test ta_detectcode_lone_openings {detectcode: detects codes without requiring paired/complete sequences - a lone CSI opening counts}\ |
||||||
|
-setup $common -body { |
||||||
|
variable RED |
||||||
|
lappend result [punk::ansi::ta::detectcode "a${RED}b"] |
||||||
|
lappend result [punk::ansi::ta::detectcode "plain text"] |
||||||
|
#the difference from detect: a lone CSI opening is detected |
||||||
|
lappend result [punk::ansi::ta::detectcode "abc\x1b\["] |
||||||
|
#but a lone ESC alone is not |
||||||
|
lappend result [punk::ansi::ta::detectcode "abc\x1b"] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list 1 0 1 0] |
||||||
|
|
||||||
|
test ta_detect_in_list {detect_in_list/detectcode_in_list: per-element detection}\ |
||||||
|
-setup $common -body { |
||||||
|
variable RED |
||||||
|
lappend result [punk::ansi::ta::detect_in_list [list plain "a${RED}b" more]] |
||||||
|
lappend result [punk::ansi::ta::detect_in_list [list plain more]] |
||||||
|
lappend result [punk::ansi::ta::detectcode_in_list [list plain "abc\x1b\["]] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list 1 0 1] |
||||||
|
|
||||||
|
test ta_detect_csi_vs_sgr {detect_csi detects any CSI sequence; detect_sgr only those ending in m}\ |
||||||
|
-setup $common -body { |
||||||
|
variable RED |
||||||
|
#cursor-up is a CSI but not an SGR |
||||||
|
lappend result [punk::ansi::ta::detect_csi "\x1b\[2A"] |
||||||
|
lappend result [punk::ansi::ta::detect_sgr "\x1b\[2A"] |
||||||
|
lappend result [punk::ansi::ta::detect_csi "a${RED}b"] |
||||||
|
lappend result [punk::ansi::ta::detect_sgr "a${RED}b"] |
||||||
|
#an SGR reset is also an SGR |
||||||
|
lappend result [punk::ansi::ta::detect_sgr "\x1b\[0m"] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list 1 0 1 1 1] |
||||||
|
|
||||||
|
test ta_detect_g0 {detect_g0 requires a complete G0 open..close group}\ |
||||||
|
-setup $common -body { |
||||||
|
lappend result [punk::ansi::ta::detect_g0 "\x1b(0qqq\x1b(B"] |
||||||
|
lappend result [punk::ansi::ta::detect_g0 "\x1b(0qqq"] |
||||||
|
lappend result [punk::ansi::ta::detect_g0 "plain"] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list 1 0 0] |
||||||
|
|
||||||
|
#--- splitting ---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
test ta_split_codes_shape {split_codes: plaintext on even indices, codes on odd; first/last always plaintext; consecutive codes stay together}\ |
||||||
|
-setup $common -body { |
||||||
|
variable RED |
||||||
|
variable BOLD |
||||||
|
variable RESET |
||||||
|
lappend result [punk::ansi::ta::split_codes ""] |
||||||
|
lappend result [punk::ansi::ta::split_codes "a"] |
||||||
|
#string ending in a code gets a trailing empty plaintext element |
||||||
|
lappend result [punk::ansi::ta::split_codes "a${RED}"] |
||||||
|
lappend result [punk::ansi::ta::split_codes "${RED}a${RESET}b"] |
||||||
|
#consecutive codes remain in ONE code element |
||||||
|
lappend result [punk::ansi::ta::split_codes "${RED}${BOLD}b"] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list\ |
||||||
|
{}\ |
||||||
|
[list a]\ |
||||||
|
[list a "\x1b\[31m" ""]\ |
||||||
|
[list "" "\x1b\[31m" a "\x1b\[0m" b]\ |
||||||
|
[list "" "\x1b\[31m\x1b\[1m" b]\ |
||||||
|
] |
||||||
|
|
||||||
|
test ta_split_codes_single_shape {split_codes_single: each code in its own element with empty plaintext between consecutive codes}\ |
||||||
|
-setup $common -body { |
||||||
|
variable RED |
||||||
|
variable BOLD |
||||||
|
lappend result [punk::ansi::ta::split_codes_single "${RED}${BOLD}b"] |
||||||
|
lappend result [punk::ansi::ta::split_codes_single ""] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list\ |
||||||
|
[list "" "\x1b\[31m" "" "\x1b\[1m" b]\ |
||||||
|
{}\ |
||||||
|
] |
||||||
|
|
||||||
|
test ta_split_at_codes_plaintext_only {split_at_codes returns only the plaintext portions}\ |
||||||
|
-setup $common -body { |
||||||
|
variable RED |
||||||
|
variable RESET |
||||||
|
lappend result [punk::ansi::ta::split_at_codes "a${RED}b${RESET}c"] |
||||||
|
lappend result [punk::ansi::ta::split_at_codes ""] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list [list a b c] {}] |
||||||
|
|
||||||
|
test ta_get_codes_single {get_codes_single returns just the codes, one per element}\ |
||||||
|
-setup $common -body { |
||||||
|
variable RED |
||||||
|
variable BOLD |
||||||
|
lappend result [punk::ansi::ta::get_codes_single "a${RED}b${BOLD}c"] |
||||||
|
lappend result [punk::ansi::ta::get_codes_single "plain"] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list [list "\x1b\[31m" "\x1b\[1m"] {}] |
||||||
|
|
||||||
|
test ta_split_roundtrip {joining split_codes / split_codes_single output reconstructs the original string}\ |
||||||
|
-setup $common -body { |
||||||
|
variable RED |
||||||
|
variable BOLD |
||||||
|
variable RESET |
||||||
|
set s "start${RED}${BOLD}mid${RESET}end" |
||||||
|
lappend result [expr {[join [punk::ansi::ta::split_codes $s] ""] eq $s}] |
||||||
|
lappend result [expr {[join [punk::ansi::ta::split_codes_single $s] ""] eq $s}] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list 1 1] |
||||||
|
|
||||||
|
#--- length / extract --------------------------------------------------------------------- |
||||||
|
|
||||||
|
test ta_length_excludes_codes {length counts characters excluding ANSI codes}\ |
||||||
|
-setup $common -body { |
||||||
|
variable RED |
||||||
|
lappend result [punk::ansi::ta::length "a${RED}b"] |
||||||
|
lappend result [punk::ansi::ta::length ""] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list 2 0] |
||||||
|
|
||||||
|
test ta_extract_codes_only {extract returns only the ANSI codes (opposite of strip)}\ |
||||||
|
-setup $common -body { |
||||||
|
variable RED |
||||||
|
variable RESET |
||||||
|
lappend result [punk::ansi::ta::extract "a${RED}b${RESET}"] |
||||||
|
}\ |
||||||
|
-cleanup { |
||||||
|
}\ |
||||||
|
-result [list "\x1b\[31m\x1b\[0m"] |
||||||
|
} |
||||||
|
tcltest::cleanupTests ;#needed to produce test summary line. |
||||||
Loading…
Reference in new issue