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