blob: b61d2017d05f4f187e1143a2338115f36ff5e875 [file] [log] [blame]
Alexander Kornienkod278e0e2013-09-04 15:09:13 +00001#!/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):
Daniel Jasper7f432662014-12-02 14:21:16 +000020 text = re.sub(r'([^/\*])\*', r'\1\\*', text)
Alexander Kornienkod278e0e2013-09-04 15:09:13 +000021 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
Daniel Jasperc1bc38e2015-09-29 14:57:55 +000039 self.nested_struct = None
Alexander Kornienkod278e0e2013-09-04 15:09:13 +000040
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)
Daniel Jasperc1bc38e2015-09-29 14:57:55 +000046 if self.nested_struct:
47 s += indent('\n\nNested configuration flags:\n\n%s\n' %self.nested_struct,
48 2)
Alexander Kornienkod278e0e2013-09-04 15:09:13 +000049 return s
50
Daniel Jasperc1bc38e2015-09-29 14:57:55 +000051class 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
Alexander Kornienkod278e0e2013-09-04 15:09:13 +000068class 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):
Daniel Jasper8ce1b8d2015-10-06 11:54:18 +000089 if line == '/// \\code':
Daniel Jasper8e1ecca2015-10-07 13:02:45 +000090 return '\n.. code-block:: c++\n\n'
Daniel Jasper8ce1b8d2015-10-06 11:54:18 +000091 if line == '/// \\endcode':
92 return ''
93 return line[4:] + '\n'
Alexander Kornienkod278e0e2013-09-04 15:09:13 +000094
95def read_options(header):
96 class State:
Daniel Jasperc1bc38e2015-09-29 14:57:55 +000097 BeforeStruct, Finished, InStruct, InNestedStruct, InNestedFieldComent, \
98 InFieldComment, InEnum, InEnumMemberComment = range(8)
Alexander Kornienkod278e0e2013-09-04 15:09:13 +000099 state = State.BeforeStruct
100
101 options = []
102 enums = {}
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000103 nested_structs = {}
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000104 comment = ''
105 enum = None
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000106 nested_struct = None
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000107
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)
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000127 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)
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000131 elif line.endswith(';'):
132 state = State.InStruct
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000133 field_type, field_name = re.match(r'([<>:\w(,\s)]+)\s+(\w+);',
134 line).groups()
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000135 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')
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000139 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
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000146 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))
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000152 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:
Daniel Jasperb5524822014-04-09 14:05:49 +0000171 if not option.type in ['bool', 'unsigned', 'int', 'std::string',
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000172 'std::vector<std::string>',
Daniel Jasper8ce1b8d2015-10-06 11:54:18 +0000173 'std::vector<IncludeCategory>']:
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000174 if enums.has_key(option.type):
175 option.enum = enums[option.type]
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000176 elif nested_structs.has_key(option.type):
177 option.nested_struct = nested_structs[option.type];
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000178 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
Benjamin Kramer611d33a2015-11-20 07:46:19 +0000191with open(DOC_FILE, 'wb') as output:
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000192 output.write(contents)
193