Length of the escape sequence beginning at start, or 0.
Escape sequences occupy no columns, so measuring and truncating both
have to step over them rather than count them. Without this,
styled("ARRIVALS", fg=ANSI_RED) measures seventeen wide rather than
eight, and truncating a styled row could cut an escape sequence in
half – which does not merely lose the colour, it leaves the terminal
reading the rest of the row as parameters.
Recognises the CSI form, ESC [ then parameter and intermediate
bytes then a final byte in the range @ to ~, which is what
everything in this module emits. An unterminated sequence swallows
the remaining text, since there is no position in it that could
safely be cut. Any other ESC pair counts as two bytes.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | text |
Text being scanned. |
||
| integer(kind=default_int), | intent(in) | :: | start |
Index to examine. |
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| integer(kind=default_int), | private | :: | byte | ||||
| integer(kind=default_int), | private | :: | j | ||||
| integer(kind=default_int), | private | :: | last |
pure function escape_length(text, start) result(n) !! Length of the escape sequence beginning at `start`, or 0. !! !! Escape sequences occupy no columns, so measuring and truncating both !! have to step over them rather than count them. Without this, !! `styled("ARRIVALS", fg=ANSI_RED)` measures seventeen wide rather than !! eight, and truncating a styled row could cut an escape sequence in !! half -- which does not merely lose the colour, it leaves the terminal !! reading the rest of the row as parameters. !! !! Recognises the CSI form, `ESC [` then parameter and intermediate !! bytes then a final byte in the range `@` to `~`, which is what !! everything in this module emits. An unterminated sequence swallows !! the remaining text, since there is no position in it that could !! safely be cut. Any other `ESC` pair counts as two bytes. character(len=*), intent(in) :: text !! Text being scanned. integer(default_int), intent(in) :: start !! Index to examine. integer(default_int) :: n integer(default_int) :: j, last, byte n = 0_default_int last = len(text, kind=default_int) if (start > last) return if (text(start:start) /= ESC) return if (start == last) then n = 1_default_int ! a lone trailing ESC return end if if (text(start + 1_default_int:start + 1_default_int) /= "[") then n = 2_default_int ! ESC followed by something else return end if j = start + 2_default_int do while (j <= last) byte = iachar(text(j:j), default_int) if (byte >= 64_default_int .and. byte <= 126_default_int) then n = j - start + 1_default_int ! final byte, sequence complete return end if if (byte < 32_default_int .or. byte > 63_default_int) then ! A byte that is neither parameter, intermediate nor final aborts ! the sequence where it stands -- which is what a terminal does ! with it. Everything from here on is ordinary text again. n = j - start return end if j = j + 1_default_int end do n = last - start + 1_default_int ! ran off the end; nowhere safe to cut end function escape_length