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 |
||||
#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 |
||||
|
||||
namespace eval ::testspace { |
||||
namespace import ::tcltest::* |
||||
variable common { |
||||
set result "" |
||||
} |
||||
|
||||
|
||||
|
||||
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 |
||||
] |
||||
|
||||
} |
||||
tcltest::cleanupTests ;#needed to produce test summary. |
||||
package require tcltest |
||||
|
||||
package require punk::lib |
||||
package require punk::ansi |
||||
|
||||
#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 |
||||
#grapheme indexing, code-prefix merging and column mapping are pinned 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. |
||||
#The fixture uses a CJK double-wide grapheme (丁) to pin grapheme-vs-column |
||||
#distinctions. |
||||
|
||||
namespace eval ::testspace { |
||||
namespace import ::tcltest::* |
||||
variable common { |
||||
set result "" |
||||
} |
||||
|
||||
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