escape_length Function

private 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.

Arguments

Type IntentOptional Attributes Name
character(len=*), intent(in) :: text

Text being scanned.

integer(kind=default_int), intent(in) :: start

Index to examine.

Return Value integer(kind=default_int)


Called by

proc~~escape_length~~CalledByGraph proc~escape_length escape_length proc~display_width display_width proc~display_width->proc~escape_length proc~truncate_to_width truncate_to_width proc~truncate_to_width->proc~escape_length proc~frame_set_line frame_t%frame_set_line proc~frame_set_line->proc~truncate_to_width

Variables

Type Visibility Attributes Name Initial
integer(kind=default_int), private :: byte
integer(kind=default_int), private :: j
integer(kind=default_int), private :: last

Source Code

   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