docs: def _long_string_handler(c, ctx, is_field_name=False):
      """Handles triple-quoted strings. Remains active until a value other than a long string is encountered."""
      assert c == _SINGLE_QUOTE
      is_clob = ctx.ion_type is IonType.CLOB
      max_char = _MAX_CLOB_CHAR if is_clob else _MAX_TEXT_CHAR
      assert not (is_clob and is_field_name)
      if not is_clob and not is_field_name:
          ctx.set_ion_type(IonType.STRING)
      assert not ctx.value
      ctx.set_unicode(quoted_text=True)
      val = ctx.value
      if is_field_name:
          assert not val
          ctx.set_pending_symbol()
          val = ctx.pending_symbol
      quotes = 0
      in_data = True
      c, self = yield
      here = ctx.immediate_transition(self)
      trans = here
      while True:
          if c == _SINGLE_QUOTE and not _is_escaped(c):
              quotes += 1
              if quotes == 3:
                  in_data = not in_data
                  ctx.set_quoted_text(in_data)
                  quotes = 0
          else:
              if in_data:
                  _validate_long_string_text(c, ctx, max_char)
                  # Any quotes found in the meantime are part of the data
                  val.extend(_SINGLE_QUOTES[quotes])
                  if not _is_escaped_newline(c):
                      val.append(c)
                  quotes = 0
              else:
                  if quotes > 0:
                      assert quotes < 3
                      if is_field_name or is_clob:
                          # There are at least two values here, which is illegal for field names or within clobs.
                          _illegal_character(c, ctx, 'Malformed triple-quoted text: %s' % (val,))
                      else:
                          # This string value is followed by a quoted symbol.
                          if ctx.container.is_delimited:
                              _illegal_character(c, ctx, 'Delimiter %s not found after value.'
                                                 % (_chr(ctx.container.delimiter[0]),))
                          trans = ctx.event_transition(IonEvent, IonEventType.SCALAR, ctx.ion_type, ctx.value.as_text())
                          if quotes == 1:
                              if BufferQueue.is_eof(c):
                                  _illegal_character(c, ctx, "Unexpected EOF.")
                              # c was read as a single byte. Re-read it as a code point.
                              ctx.queue.unread(c)
                              ctx.set_quoted_text(True)
                              c, _ = yield ctx.immediate_transition(self)
                              trans = _CompositeTransition(
                                  trans,
                                  ctx,
                                  partial(_quoted_symbol_handler, c, is_field_name=False),
                              )
                          else:  # quotes == 2
                              trans = _CompositeTransition(trans, ctx, None, ctx.set_empty_symbol())
                  elif c not in _WHITESPACE:
                      if is_clob:
                          trans = ctx.immediate_transition(_clob_end_handler(c, ctx))
                      elif c == _SLASH:
                          if ctx.container.ion_type is IonType.SEXP:
                              pending = ctx.event_transition(IonEvent, IonEventType.SCALAR,
                                                             ctx.ion_type, ctx.value.as_text())
                              trans = ctx.immediate_transition(_sexp_slash_handler(c, ctx, self, pending))
                          else:
                              trans = ctx.immediate_transition(_comment_handler(c, ctx, self))
                      elif is_field_name:
                          if c != _COLON:
                              _illegal_character(c, ctx, 'Illegal character after field name %s.' % (val,))
                          trans = ctx.immediate_transition(ctx.whence)
                      else:
                          trans = ctx.event_transition(IonEvent, IonEventType.SCALAR, ctx.ion_type, ctx.value.as_text())
          c, _ = yield trans
          ctx.set_self_delimiting(False)  # If comments separated long string components, this would have been set.
          trans = here