docs: def get_atx_heading(line: str,
                      keep_header_levels: int = 3,
                      parser: str = 'github',
                      no_links: bool = False):
      r"""Given a line extract the link label and its type.
  
      :parameter line: the line to be examined.
      :parameter keep_header_levels: the maximum level of headers to be
           considered as such when building the table of contents.
           Defaults to ``3``.
      :parameter parser: decides rules on how to generate the anchor text.
           Defaults to ``github``.
      :parameter no_links: disables the use of links.
      :type line: str
      :type keep_header_levels: int
      :type parser: str
      :type np_links: bool
      :returns: None if the line does not contain header elements according to
           the rules of the selected markdown parser, or a tuple containing the
           header type and the trimmed header text, according to the selected
           parser rules, otherwise.
      :rtype: typing.Optional[tuple]
      :raises: GithubEmptyLinkLabel or GithubOverflowCharsLinkLabel or a
           built-in exception.
      """
      assert keep_header_levels >= 1
  
      if len(line) == 0:
          return None
  
      if (parser == 'github' or parser == 'cmark' or parser == 'gitlab'
              or parser == 'commonmarker'):
  
          if line[0] == '\u005c':
              return None
  
          i = 0
          while i < len(line) and line[i] == ' ' and i <= md_parser['github'][
                  'header']['max_space_indentation']:
              i += 1
          if i > md_parser['github']['header']['max_space_indentation']:
              return None
  
          offset = i
          while i < len(line) and line[i] == '#' and i <= md_parser['github'][
                  'header']['max_levels'] + offset:
              i += 1
          if i - offset > md_parser['github']['header'][
                  'max_levels'] or i - offset > keep_header_levels or i - offset == 0:
              return None
          current_headers = i - offset
  
          # Include special cases for line endings which should not be
          # discarded as non-ATX headers.
          if i < len(line) and (line[i] != ' ' and line[i] != '\u000a'
                                and line[i] != '\u000d'):
              return None
  
          i += 1
          # Exclude leading whitespaces after the ATX header identifier.
          while i < len(line) and line[i] == ' ':
              i += 1
  
          # An algorithm to find the start and the end of the closing sequence.
          # The closing sequence includes all the significant part of the
          # string. This algorithm has a complexity of O(n) with n being the
          # length of the line.
          cs_start = i
          cs_end = cs_start
          line_prime = line[::-1]
          hash_char_rounds = 0
          go_on = True
          i = 0
          i_prev = i
          while i < len(line) - cs_start - 1 and go_on:
              if ((line_prime[i] != ' ' and line_prime[i] != '#')
                      or hash_char_rounds > 1):
                  if i > i_prev:
                      cs_end = len(line_prime) - i_prev
                  else:
                      cs_end = len(line_prime) - i
                  go_on = False
              while go_on and line_prime[i] == ' ':
                  i += 1
              i_prev = i
              while go_on and line_prime[i] == '#':
                  i += 1
              if i > i_prev:
                  hash_char_rounds += 1
  
          # Instead of changing the whole algorithm to check for line
          # endings, this seems cleaner.
          find_newline = line.find('\u000a')
          find_carriage_return = line.find('\u000d')
          if find_newline != -1:
              cs_end = min(cs_end, find_newline)
          if find_carriage_return != -1:
              cs_end = min(cs_end, find_carriage_return)
  
          final_line = line[cs_start:cs_end]
  
          if not no_links:
              if len(final_line) > 0 and final_line[-1] == '\u005c':
                  final_line += ' '
              if len(
                      final_line.strip('\u0020').strip('\u0009').strip('\u000a').
                      strip('\u000b').strip('\u000c').strip('\u000d')) == 0:
                  raise GithubEmptyLinkLabel
              if len(final_line
                     ) > md_parser['github']['link']['max_chars_label']:
                  raise GithubOverflowCharsLinkLabel
              # Escape square brackets if not already escaped.
              i = 0
              while i < len(final_line):
                  if (final_line[i] == '[' or final_line[i] == ']'):
                      j = i - 1
                      consecutive_escape_characters = 0
                      while j >= 0 and final_line[j] == '\u005c':
                          consecutive_escape_characters += 1
                          j -= 1
                      if ((consecutive_escape_characters > 0
                           and consecutive_escape_characters % 2 == 0)
                              or consecutive_escape_characters == 0):
                          tmp = '\u005c'
                      else:
                          tmp = str()
                      final_line = final_line[0:i] + tmp + final_line[i:len(
                          final_line)]
                      i += 1 + len(tmp)
                  else:
                      i += 1
  
      elif parser == 'redcarpet':
  
          if line[0] != '#':
              return None
  
          i = 0
          while (i < len(line)
                 and i < md_parser['redcarpet']['header']['max_levels']
                 and line[i] == '#'):
              i += 1
          current_headers = i
  
          if i < len(line) and line[i] != ' ':
              return None
  
          while i < len(line) and line[i] == ' ':
              i += 1
  
          end = i
          while end < len(line) and line[end] != '\n':
              end += 1
  
          while end > 0 and line[end - 1] == '#':
              end -= 1
  
          while end > 0 and line[end - 1] == ' ':
              end -= 1
  
          if end > i:
              final_line = line
              if not no_links and len(final_line) > 0 and final_line[-1] == '\\':
                  final_line += ' '
                  end += 1
              final_line = final_line[i:end]
          else:
              return None
  
      # TODO: escape or remove '[', ']', '(', ')' in inline links for redcarpet,
      # TODO: check link label rules for redcarpet.
  
      return current_headers, final_line