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