1#!/usr/bin/env python 2# A tool to parse the FormatStyle struct from Format.h and update the 3# documentation in ../ClangFormatStyleOptions.rst automatically. 4# Run from the directory in which this file is located to update the docs. 5 6import collections 7import re 8import urllib2 9 10FORMAT_STYLE_FILE = '../../include/clang/Format/Format.h' 11DOC_FILE = '../ClangFormatStyleOptions.rst' 12 13 14def substitute(text, tag, contents): 15 replacement = '\n.. START_%s\n\n%s\n\n.. END_%s\n' % (tag, contents, tag) 16 pattern = r'\n\.\. START_%s\n.*\n\.\. END_%s\n' % (tag, tag) 17 return re.sub(pattern, '%s', text, flags=re.S) % replacement 18 19def doxygen2rst(text): 20 text = re.sub(r'([^/\*])\*', r'\1\\*', text) 21 text = re.sub(r'<tt>\s*(.*?)\s*<\/tt>', r'``\1``', text) 22 text = re.sub(r'\\c ([^ ,;\.]+)', r'``\1``', text) 23 text = re.sub(r'\\\w+ ', '', text) 24 return text 25 26def indent(text, columns): 27 indent = ' ' * columns 28 s = re.sub(r'\n([^\n])', '\n' + indent + '\\1', text, flags=re.S) 29 if s.startswith('\n'): 30 return s 31 return indent + s 32 33class Option: 34 def __init__(self, name, type, comment): 35 self.name = name 36 self.type = type 37 self.comment = comment.strip() 38 self.enum = None 39 self.nested_struct = None 40 41 def __str__(self): 42 s = '**%s** (``%s``)\n%s' % (self.name, self.type, 43 doxygen2rst(indent(self.comment, 2))) 44 if self.enum: 45 s += indent('\n\nPossible values:\n\n%s\n' % self.enum, 2) 46 if self.nested_struct: 47 s += indent('\n\nNested configuration flags:\n\n%s\n' %self.nested_struct, 48 2) 49 return s 50 51class NestedStruct: 52 def __init__(self, name, comment): 53 self.name = name 54 self.comment = comment.strip() 55 self.values = [] 56 57 def __str__(self): 58 return '\n'.join(map(str, self.values)) 59 60class NestedField: 61 def __init__(self, name, comment): 62 self.name = name 63 self.comment = comment.strip() 64 65 def __str__(self): 66 return '* ``%s`` %s' % (self.name, doxygen2rst(self.comment)) 67 68class Enum: 69 def __init__(self, name, comment): 70 self.name = name 71 self.comment = comment.strip() 72 self.values = [] 73 74 def __str__(self): 75 return '\n'.join(map(str, self.values)) 76 77class EnumValue: 78 def __init__(self, name, comment): 79 self.name = name 80 self.comment = comment.strip() 81 82 def __str__(self): 83 return '* ``%s`` (in configuration: ``%s``)\n%s' % ( 84 self.name, 85 re.sub('.*_', '', self.name), 86 doxygen2rst(indent(self.comment, 2))) 87 88def clean_comment_line(line): 89 if line == '/// \\code': 90 return '\n.. code-block:: c++\n\n' 91 if line == '/// \\endcode': 92 return '' 93 return line[4:] + '\n' 94 95def read_options(header): 96 class State: 97 BeforeStruct, Finished, InStruct, InNestedStruct, InNestedFieldComent, \ 98 InFieldComment, InEnum, InEnumMemberComment = range(8) 99 state = State.BeforeStruct 100 101 options = [] 102 enums = {} 103 nested_structs = {} 104 comment = '' 105 enum = None 106 nested_struct = None 107 108 for line in header: 109 line = line.strip() 110 if state == State.BeforeStruct: 111 if line == 'struct FormatStyle {': 112 state = State.InStruct 113 elif state == State.InStruct: 114 if line.startswith('///'): 115 state = State.InFieldComment 116 comment = clean_comment_line(line) 117 elif line == '};': 118 state = State.Finished 119 break 120 elif state == State.InFieldComment: 121 if line.startswith('///'): 122 comment += clean_comment_line(line) 123 elif line.startswith('enum'): 124 state = State.InEnum 125 name = re.sub(r'enum\s+(\w+)\s*\{', '\\1', line) 126 enum = Enum(name, comment) 127 elif line.startswith('struct'): 128 state = State.InNestedStruct 129 name = re.sub(r'struct\s+(\w+)\s*\{', '\\1', line) 130 nested_struct = NestedStruct(name, comment) 131 elif line.endswith(';'): 132 state = State.InStruct 133 field_type, field_name = re.match(r'([<>:\w(,\s)]+)\s+(\w+);', 134 line).groups() 135 option = Option(str(field_name), str(field_type), comment) 136 options.append(option) 137 else: 138 raise Exception('Invalid format, expected comment, field or enum') 139 elif state == State.InNestedStruct: 140 if line.startswith('///'): 141 state = State.InNestedFieldComent 142 comment = clean_comment_line(line) 143 elif line == '};': 144 state = State.InStruct 145 nested_structs[nested_struct.name] = nested_struct 146 elif state == State.InNestedFieldComent: 147 if line.startswith('///'): 148 comment += clean_comment_line(line) 149 else: 150 state = State.InNestedStruct 151 nested_struct.values.append(NestedField(line.replace(';', ''), comment)) 152 elif state == State.InEnum: 153 if line.startswith('///'): 154 state = State.InEnumMemberComment 155 comment = clean_comment_line(line) 156 elif line == '};': 157 state = State.InStruct 158 enums[enum.name] = enum 159 else: 160 raise Exception('Invalid format, expected enum field comment or };') 161 elif state == State.InEnumMemberComment: 162 if line.startswith('///'): 163 comment += clean_comment_line(line) 164 else: 165 state = State.InEnum 166 enum.values.append(EnumValue(line.replace(',', ''), comment)) 167 if state != State.Finished: 168 raise Exception('Not finished by the end of file') 169 170 for option in options: 171 if not option.type in ['bool', 'unsigned', 'int', 'std::string', 172 'std::vector<std::string>', 173 'std::vector<IncludeCategory>']: 174 if enums.has_key(option.type): 175 option.enum = enums[option.type] 176 elif nested_structs.has_key(option.type): 177 option.nested_struct = nested_structs[option.type]; 178 else: 179 raise Exception('Unknown type: %s' % option.type) 180 return options 181 182options = read_options(open(FORMAT_STYLE_FILE)) 183 184options = sorted(options, key=lambda x: x.name) 185options_text = '\n\n'.join(map(str, options)) 186 187contents = open(DOC_FILE).read() 188 189contents = substitute(contents, 'FORMAT_STYLE_OPTIONS', options_text) 190 191with open(DOC_FILE, 'wb') as output: 192 output.write(contents) 193 194