From bde3199cca444f00f71d43355056d4a38848a854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Pant=C5=AF=C4=8Dek?= Date: Tue, 21 Mar 2023 22:35:55 +0100 Subject: [PATCH] Add ansi-string-length. --- README.md | 7 +++++++ ansi.scm | 28 ++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/README.md b/README.md index 99496ac..475732e 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,13 @@ black text. Generic highlight of given text. Defaults to bold blue text. + (ansi-string-length str) + +* ```str``` string that may contain ANSI CSI SGR sequences + +Returns the string length in characters without any ANSI CSI SGR +sequences contained. + ### Command Line parsing Generic syntax-based implementation of command-line options parsing diff --git a/ansi.scm b/ansi.scm index 636f3cf..8a9f989 100644 --- a/ansi.scm +++ b/ansi.scm @@ -82,6 +82,31 @@ (define a:muted (ansi #:black #:bold)) (define a:highlight (ansi #:blue #:bold)) + ;; Returns visual string length in characters skipping any ANSI CSI + ;; SGR sequences. + ;; + ;; Internal states: + ;; 0 - regular string + ;; 1 - seen escape + ;; 2 - CSI started + (define (ansi-string-length str) + (let loop ((lst (string->list str)) + (state 0) + (len 0)) + (if (null? lst) + len + (let ((ch (car lst))) + (case state + ((0) (if (eq? ch #\escape) + (loop (cdr lst) 1 len) + (loop (cdr lst) 0 (add1 len)))) + ((1) (if (eq? ch #\[) + (loop (cdr lst) 2 len) + (loop (cdr lst) 0 len))) + ((2) (if (eq? ch #\m) + (loop (cdr lst) 0 len) + (loop (cdr lst) 2 len)))))))) + ;; Performs ANSI module self-tests. (define (ansi-tests!) (run-tests @@ -89,6 +114,9 @@ (test-equal? ansi (ansi #:red) "\x1b[31m") (test-equal? ansi (ansi #:nonsense) "") (test-equal? ansi (ansi #:default) "\x1b[0m") + (test-eq? ansi-string-length (ansi-string-length "test") 4) + (test-eq? ansi-string-length (ansi-string-length "\x1b[1mtest") 4) + (test-eq? ansi-string-length (ansi-string-length "\x1b[30mtest\x1b[0m") 4) )) )