class_docstr: For Crystal source code.
  
  .. versionadded:: 2.2
class_docstr_tok: ["For","Crystal","source","code",".","..","versionadded",":",":","2.2"]
code: def heredoc_callback(self, match, ctx):
      # okay, this is the hardest part of parsing Crystal...
      # match: 1 = <<-?, 2 = quote? 3 = name 4 = quote? 5 = rest of line
  
      start = match.start(1)
      yield start, Operator, match.group(1)  # <<-?
      yield match.start(2), String.Heredoc, match.group(
          2
      )  # quote ", ', `
      yield match.start(3), String.Delimiter, match.group(
          3
      )  # heredoc name
      yield match.start(4), String.Heredoc, match.group(
          4
      )  # quote again
  
      heredocstack = ctx.__dict__.setdefault("heredocstack", [])
      outermost = not bool(heredocstack)
      heredocstack.append((match.group(1) == "<<-", match.group(3)))
  
      ctx.pos = match.start(5)
      ctx.end = match.end(5)
      # this may find other heredocs, so limit the recursion depth
      if len(heredocstack) < 100:
          yield from self.get_tokens_unprocessed(context=ctx)
      else:
          yield ctx.pos, String.Heredoc, match.group(5)
      ctx.pos = match.end()
  
      if outermost:
          # this is the outer heredoc again, now we can process them all
          for tolerant, hdname in heredocstack:
              lines = []
              for match in line_re.finditer(ctx.text, ctx.pos):
                  if tolerant:
                      check = match.group().strip()
                  else:
                      check = match.group().rstrip()
                  if check == hdname:
                      for amatch in lines:
                          yield amatch.start(), String.Heredoc, amatch.group()
                      yield match.start(), String.Delimiter, match.group()
                      ctx.pos = match.end()
                      break
                  else:
                      lines.append(match)
              else:
                  # end of heredoc not found -- error!
                  for amatch in lines:
                      yield amatch.start(), Error, amatch.group()
          ctx.end = len(ctx.text)
          del heredocstack[:]
  
imports: import re
  from pygments.lexer import ExtendedRegexLexer, include, bygroups, default, words, line_re
  from pygments.token import Comment, Operator, Keyword, Name, String, Number, Punctuation, Error, Whitespace
label_desc: Use image node_id 1 for calling the CrystalLexer obj's underlying member method code with example usage: obj.heredoc_callback(match, ctx) without return types