📄 states.py
字号:
``None`` is returned for invalid enumerator text). The enumerator format has already been determined by the regular expression match. If `expected_sequence` is given, that sequence is tried first. If not, we check for Roman numeral 1. This way, single-character Roman numerals (which are also alphabetical) can be matched. If no sequence has been matched, all sequences are checked in order. """ groupdict = match.groupdict() sequence = '' for format in self.enum.formats: if groupdict[format]: # was this the format matched? break # yes; keep `format` else: # shouldn't happen raise ParserError('enumerator format not matched') text = groupdict[format][self.enum.formatinfo[format].start :self.enum.formatinfo[format].end] if text == '#': sequence = '#' elif expected_sequence: try: if self.enum.sequenceregexps[expected_sequence].match(text): sequence = expected_sequence except KeyError: # shouldn't happen raise ParserError('unknown enumerator sequence: %s' % sequence) elif text == 'i': sequence = 'lowerroman' elif text == 'I': sequence = 'upperroman' if not sequence: for sequence in self.enum.sequences: if self.enum.sequenceregexps[sequence].match(text): break else: # shouldn't happen raise ParserError('enumerator sequence not matched') if sequence == '#': ordinal = 1 else: try: ordinal = self.enum.converters[sequence](text) except roman.InvalidRomanNumeralError: ordinal = None return format, sequence, text, ordinal def is_enumerated_list_item(self, ordinal, sequence, format): """ Check validity based on the ordinal value and the second line. Return true iff the ordinal is valid and the second line is blank, indented, or starts with the next enumerator or an auto-enumerator. """ if ordinal is None: return None try: next_line = self.state_machine.next_line() except EOFError: # end of input lines self.state_machine.previous_line() return 1 else: self.state_machine.previous_line() if not next_line[:1].strip(): # blank or indented return 1 result = self.make_enumerator(ordinal + 1, sequence, format) if result: next_enumerator, auto_enumerator = result try: if ( next_line.startswith(next_enumerator) or next_line.startswith(auto_enumerator) ): return 1 except TypeError: pass return None def make_enumerator(self, ordinal, sequence, format): """ Construct and return the next enumerated list item marker, and an auto-enumerator ("#" instead of the regular enumerator). Return ``None`` for invalid (out of range) ordinals. """ #" if sequence == '#': enumerator = '#' elif sequence == 'arabic': enumerator = str(ordinal) else: if sequence.endswith('alpha'): if ordinal > 26: return None enumerator = chr(ordinal + ord('a') - 1) elif sequence.endswith('roman'): try: enumerator = roman.toRoman(ordinal) except roman.RomanError: return None else: # shouldn't happen raise ParserError('unknown enumerator sequence: "%s"' % sequence) if sequence.startswith('lower'): enumerator = enumerator.lower() elif sequence.startswith('upper'): enumerator = enumerator.upper() else: # shouldn't happen raise ParserError('unknown enumerator sequence: "%s"' % sequence) formatinfo = self.enum.formatinfo[format] next_enumerator = (formatinfo.prefix + enumerator + formatinfo.suffix + ' ') auto_enumerator = formatinfo.prefix + '#' + formatinfo.suffix + ' ' return next_enumerator, auto_enumerator def field_marker(self, match, context, next_state): """Field list item.""" field_list = nodes.field_list() self.parent += field_list field, blank_finish = self.field(match) field_list += field offset = self.state_machine.line_offset + 1 # next line newline_offset, blank_finish = self.nested_list_parse( self.state_machine.input_lines[offset:], input_offset=self.state_machine.abs_line_offset() + 1, node=field_list, initial_state='FieldList', blank_finish=blank_finish) self.goto_line(newline_offset) if not blank_finish: self.parent += self.unindent_warning('Field list') return [], next_state, [] def field(self, match): name = self.parse_field_marker(match) lineno = self.state_machine.abs_line_number() indented, indent, line_offset, blank_finish = \ self.state_machine.get_first_known_indented(match.end()) field_node = nodes.field() field_node.line = lineno name_nodes, name_messages = self.inline_text(name, lineno) field_node += nodes.field_name(name, '', *name_nodes) field_body = nodes.field_body('\n'.join(indented), *name_messages) field_node += field_body if indented: self.parse_field_body(indented, line_offset, field_body) return field_node, blank_finish def parse_field_marker(self, match): """Extract & return field name from a field marker match.""" field = match.group()[1:] # strip off leading ':' field = field[:field.rfind(':')] # strip off trailing ':' etc. return field def parse_field_body(self, indented, offset, node): self.nested_parse(indented, input_offset=offset, node=node) def option_marker(self, match, context, next_state): """Option list item.""" optionlist = nodes.option_list() try: listitem, blank_finish = self.option_list_item(match) except MarkupError, (message, lineno): # This shouldn't happen; pattern won't match. msg = self.reporter.error( 'Invalid option list marker: %s' % message, line=lineno) self.parent += msg indented, indent, line_offset, blank_finish = \ self.state_machine.get_first_known_indented(match.end()) blockquote, messages = self.block_quote(indented, line_offset) self.parent += blockquote self.parent += messages if not blank_finish: self.parent += self.unindent_warning('Option list') return [], next_state, [] self.parent += optionlist optionlist += listitem offset = self.state_machine.line_offset + 1 # next line newline_offset, blank_finish = self.nested_list_parse( self.state_machine.input_lines[offset:], input_offset=self.state_machine.abs_line_offset() + 1, node=optionlist, initial_state='OptionList', blank_finish=blank_finish) self.goto_line(newline_offset) if not blank_finish: self.parent += self.unindent_warning('Option list') return [], next_state, [] def option_list_item(self, match): offset = self.state_machine.abs_line_offset() options = self.parse_option_marker(match) indented, indent, line_offset, blank_finish = \ self.state_machine.get_first_known_indented(match.end()) if not indented: # not an option list item self.goto_line(offset) raise statemachine.TransitionCorrection('text') option_group = nodes.option_group('', *options) description = nodes.description('\n'.join(indented)) option_list_item = nodes.option_list_item('', option_group, description) if indented: self.nested_parse(indented, input_offset=line_offset, node=description) return option_list_item, blank_finish def parse_option_marker(self, match): """ Return a list of `node.option` and `node.option_argument` objects, parsed from an option marker match. :Exception: `MarkupError` for invalid option markers. """ optlist = [] optionstrings = match.group().rstrip().split(', ') for optionstring in optionstrings: tokens = optionstring.split() delimiter = ' ' firstopt = tokens[0].split('=') if len(firstopt) > 1: # "--opt=value" form tokens[:1] = firstopt delimiter = '=' elif (len(tokens[0]) > 2 and ((tokens[0].startswith('-') and not tokens[0].startswith('--')) or tokens[0].startswith('+'))): # "-ovalue" form tokens[:1] = [tokens[0][:2], tokens[0][2:]] delimiter = '' if len(tokens) > 1 and (tokens[1].startswith('<') and tokens[-1].endswith('>')): # "-o <value1 value2>" form; join all values into one token tokens[1:] = [' '.join(tokens[1:])] if 0 < len(tokens) <= 2: option = nodes.option(optionstring) option += nodes.option_string(tokens[0], tokens[0]) if len(tokens) > 1: option += nodes.option_argument(tokens[1], tokens[1], delimiter=delimiter) optlist.append(option) else: raise MarkupError( 'wrong number of option tokens (=%s), should be 1 or 2: ' '"%s"' % (len(tokens), optionstring), self.state_machine.abs_line_number() + 1) return optlist def doctest(self, match, context, next_state): data = '\n'.join(self.state_machine.get_text_block()) self.parent += nodes.doctest_block(data, data) return [], next_state, [] def line_block(self, match, context, next_state): """First line of a line block.""" block = nodes.line_block() self.parent += block lineno = self.state_machine.abs_line_number() line, messages, blank_finish = self.line_block_line(match, lineno) block += line self.parent += messages if not blank_finish: offset = self.state_machine.line_offset + 1 # next line new_line_offset, blank_finish = self.nested_list_parse( self.state_machine.input_lines[offset:], input_offset=self.state_machine.abs_line_offset() + 1, node=block, initial_state='LineBlock', blank_finish=0) self.goto_line(new_line_offset) if not blank_finish: self.parent += self.reporter.warning( 'Line block ends without a blank line.', line=(self.state_machine.abs_line_number() + 1)) if len(block): if block[0].indent is None: block[0].indent = 0 self.nest_line_block_lines(block) return [], next_state, [] def line_block_line(self, match, lineno): """Return one line element of a line_block.""" indented, indent, line_offset, blank_finish = \ self.state_machine.get_first_known_indented(match.end(), until_blank=1) text = u'\n'.join(indented) text_nodes, messages = self.inline_text(text, lineno) line = nodes.line(text, '', *text_nodes) if match.string.rstrip() != '|': # not empty line.indent = len(match.group(1)) - 1 return line, messages, blank_finish def nest_line_block_lines(self, block): for index in range(1, len(block)): if block[index].indent is None: block[index].indent = block[index - 1].indent self.nest_line_block_segment(block) def nest_line_block_segment(self, block): indents = [item.indent for item in block] least = min(indents) new_items = [] new_block = nodes.line_block() for item in block: if item.indent > least: new_block.append(item) else: if len(new_block): self.nest_line_block_segment(new_block) new_items.append(new_block) new_block = nodes.line_block() new_items.append(item) if len(new_bloc
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -