def _label_docsrc_lines(self, string):
def _complete_source(line, state_indent, line_iter):<EOL><INDENT>"""<STR_LIT>"""<EOL>norm_line = line[state_indent:]  <EOL>prefix = norm_line[:<NUM_LIT:4>]<EOL>suffix = norm_line[<NUM_LIT:4>:]<EOL>assert prefix.strip() in {'<STR_LIT>', '<STR_LIT>'}, '<STR_LIT:{}>'.format(prefix)<EOL>yield line<EOL>source_parts = [suffix]<EOL>HACK_TRIPLE_QUOTE_FIX = True<EOL>try:<EOL><INDENT>while not static.is_balanced_statement(source_parts, only_tokens=True):<EOL><INDENT>line_idx, next_line = next(line_iter)<EOL>norm_line = next_line[state_indent:]<EOL>prefix = norm_line[:<NUM_LIT:4>]<EOL>suffix = norm_line[<NUM_LIT:4>:]<EOL>if prefix.strip() not in {'<STR_LIT>', '<STR_LIT>', '<STR_LIT>'}:  <EOL><INDENT>error = True<EOL>if HACK_TRIPLE_QUOTE_FIX:<EOL><INDENT>if any("<STR_LIT>" in s or '<STR_LIT>' in s for s in source_parts):<EOL><INDENT>next_line = next_line[:state_indent] + '<STR_LIT>' + norm_line<EOL>norm_line = '<STR_LIT>' + norm_line<EOL>prefix = '<STR_LIT>'<EOL>suffix = norm_line<EOL>error = False<EOL><DEDENT><DEDENT>if error:<EOL><INDENT>if DEBUG:<EOL><INDENT>print('<STR_LIT>')<EOL>print('<STR_LIT>'.format(source_parts))<EOL>print('<STR_LIT>'.format(prefix))<EOL>print('<STR_LIT>'.format(norm_line))<EOL>print('<STR_LIT>')<EOL><DEDENT>raise SyntaxError(<EOL>'<STR_LIT>'.format(<EOL>line_idx, next_line))<EOL><DEDENT><DEDENT>source_parts.append(suffix)<EOL>yield next_line<EOL><DEDENT><DEDENT>except StopIteration:<EOL><INDENT>if DEBUG:<EOL><INDENT>import ubelt as ub<EOL>print('<STR_LIT>')<EOL>import traceback<EOL>tb_text = traceback.format_exc()<EOL>tb_text = ub.highlight_code(tb_text)<EOL>tb_text = ub.indent(tb_text)<EOL>print(tb_text)<EOL>print('<STR_LIT>'.format(line_iter))<EOL>print('<STR_LIT>'.format(state_indent))<EOL>print('<STR_LIT>'.format(line))<EOL>print('<STR_LIT>')<EOL>print('<STR_LIT>'.format(ub.repr2(source_parts, nl=<NUM_LIT:2>)))<EOL>print(ub.codeblock(<EOL>r'''<STR_LIT>'''))<EOL>print('<STR_LIT>')<EOL><DEDENT>raise IncompleteParseError(<EOL>'<STR_LIT>'<EOL>'<STR_LIT>')<EOL><DEDENT>else:<EOL><INDENT>if DEBUG > <NUM_LIT:1>:<EOL><INDENT>import ubelt as ub<EOL>print('<STR_LIT>')<EOL>print('<STR_LIT>'.format(line_iter))<EOL>print('<STR_LIT>'.format(ub.repr2(source_parts, nl=<NUM_LIT:2>)))<EOL>print('<STR_LIT>')<EOL><DEDENT><DEDENT><DEDENT>labeled_lines = []<EOL>state_indent = <NUM_LIT:0><EOL>TEXT = '<STR_LIT:text>'<EOL>DSRC = '<STR_LIT>'<EOL>WANT = '<STR_LIT>'<EOL>prev_state = TEXT<EOL>curr_state = None<EOL>line_iter = enumerate(string.splitlines())<EOL>def hasprefix(line, prefixes):<EOL><INDENT>if not isinstance(prefixes, tuple):<EOL><INDENT>prefixes = [prefixes]<EOL><DEDENT>return any(line == p or line.startswith(p + '<STR_LIT:U+0020>') for p in prefixes)<EOL><DEDENT>for line_idx, line in line_iter:<EOL><INDENT>match = INDENT_RE.search(line)<EOL>line_indent = <NUM_LIT:0> if match is None else (match.end() - match.start())<EOL>norm_line = line[state_indent:]  <EOL>strip_line = line.strip()<EOL>if prev_state == TEXT:<EOL><INDENT>if hasprefix(strip_line, '<STR_LIT>'):<EOL><INDENT>curr_state = DSRC<EOL><DEDENT>else:<EOL><INDENT>curr_state = TEXT<EOL><DEDENT><DEDENT>elif prev_state == WANT:<EOL><INDENT>if len(strip_line) == <NUM_LIT:0>:<EOL><INDENT>curr_state = TEXT<EOL><DEDENT>elif hasprefix(line.strip(), '<STR_LIT>'):<EOL><INDENT>curr_state = DSRC<EOL><DEDENT>elif line_indent < state_indent:<EOL><INDENT>curr_state = TEXT<EOL><DEDENT>else:<EOL><INDENT>curr_state = WANT<EOL><DEDENT><DEDENT>elif prev_state == DSRC:  <EOL><INDENT>if len(strip_line) == <NUM_LIT:0> or line_indent < state_indent:<EOL><INDENT>curr_state = TEXT<EOL><DEDENT>elif hasprefix(norm_line, ('<STR_LIT>', '<STR_LIT>')):<EOL><INDENT>if strip_line == '<STR_LIT>':<EOL><INDENT>curr_state = WANT<EOL><DEDENT>else:<EOL><INDENT>curr_state = DSRC<EOL><DEDENT><DEDENT>else:<EOL><INDENT>curr_state = WANT<EOL><DEDENT><DEDENT>else:  <EOL><INDENT>raise AssertionError('<STR_LIT>'.format(<EOL>prev_state))<EOL><DEDENT>if prev_state != curr_state:<EOL><INDENT>if curr_state == TEXT:<EOL><INDENT>state_indent = <NUM_LIT:0><EOL><DEDENT>if curr_state == DSRC:<EOL><INDENT>state_indent = line_indent<EOL>norm_line = line[state_indent:]<EOL><DEDENT><DEDENT>if curr_state == DSRC:<EOL><INDENT>try:<EOL><INDENT>for part in _complete_source(line, state_indent, line_iter):<EOL><INDENT>labeled_lines.append((DSRC, part))<EOL><DEDENT><DEDENT>except IncompleteParseError as orig_ex:<EOL><INDENT>raise<EOL><DEDENT>except SyntaxError as orig_ex:<EOL><INDENT>if DEBUG:<EOL><INDENT>print('<STR_LIT>')<EOL>print('<STR_LIT>'.format(line_iter))<EOL>print('<STR_LIT>'.format(state_indent))<EOL>print('<STR_LIT>'.format(line))<EOL>print('<STR_LIT>')<EOL>print('<STR_LIT>')<EOL>for line in labeled_lines:<EOL><INDENT>print(line)<EOL><DEDENT>print('<STR_LIT>')<EOL>print('<STR_LIT>')<EOL><DEDENT>raise<EOL><DEDENT><DEDENT>elif curr_state == WANT:<EOL><INDENT>labeled_lines.append((WANT, line))<EOL><DEDENT>elif curr_state == TEXT:<EOL><INDENT>labeled_lines.append((TEXT, line))<EOL><DEDENT>prev_state = curr_state<EOL><DEDENT>if DEBUG > <NUM_LIT:1>:<EOL><INDENT>import ubelt as ub<EOL>print('<STR_LIT>')<EOL>print('<STR_LIT>'.format(ub.repr2(labeled_lines, nl=<NUM_LIT:1>)))<EOL>print('<STR_LIT>')<EOL><DEDENT>return labeled_lines<EOL>
Example:
    >>> from xdoctest.parser import *
    >>> # Having multiline strings in doctests can be nice
    >>> string = utils.codeblock(
            '''
            text
            >>> items = ['also', 'nice', 'to', 'not', 'worry',
            >>>          'about', '...', 'vs', '>>>']
            ... print('but its still allowed')
            but its still allowed

            more text
            ''')
    >>> self = DoctestParser()
    >>> labeled = self._label_docsrc_lines(string)
    >>> expected = [
    >>>     ('text', 'text'),
    >>>     ('dsrc', ">>> items = ['also', 'nice', 'to', 'not', 'worry',"),
    >>>     ('dsrc', ">>>          'about', '...', 'vs', '>>>']"),
    >>>     ('dsrc', "... print('but its still allowed')"),
    >>>     ('want', 'but its still allowed'),
    >>>     ('text', ''),
    >>>     ('text', 'more text')
    >>> ]
    >>> assert labeled == expected