blob: 1ca050e062b7496134709b159c0f5e81a74e54af [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
Alexander Kornienko370bfb42016-02-23 16:11:43 +00007import os
Alexander Kornienkod278e0e2013-09-04 15:09:13 +00008import re
9import urllib2
10
Alexander Kornienko370bfb42016-02-23 16:11:43 +000011CLANG_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')
Alexander Kornienkod278e0e2013-09-04 15:09:13 +000014
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'<tt>\s*(.*?)\s*<\/tt>', r'``\1``', text)
23 text = re.sub(r'\\c ([^ ,;\.]+)', r'``\1``', text)
24 text = re.sub(r'\\\w+ ', '', text)
25 return text
26
Krasimir Georgiev90b4ce32017-06-23 11:29:40 +000027def indent(text, columns, indent_first_line=True):
Alexander Kornienkod278e0e2013-09-04 15:09:13 +000028 indent = ' ' * columns
29 s = re.sub(r'\n([^\n])', '\n' + indent + '\\1', text, flags=re.S)
Krasimir Georgiev90b4ce32017-06-23 11:29:40 +000030 if not indent_first_line or s.startswith('\n'):
Alexander Kornienkod278e0e2013-09-04 15:09:13 +000031 return s
32 return indent + s
33
34class Option:
35 def __init__(self, name, type, comment):
36 self.name = name
37 self.type = type
38 self.comment = comment.strip()
39 self.enum = None
Daniel Jasperc1bc38e2015-09-29 14:57:55 +000040 self.nested_struct = None
Alexander Kornienkod278e0e2013-09-04 15:09:13 +000041
42 def __str__(self):
43 s = '**%s** (``%s``)\n%s' % (self.name, self.type,
44 doxygen2rst(indent(self.comment, 2)))
45 if self.enum:
46 s += indent('\n\nPossible values:\n\n%s\n' % self.enum, 2)
Daniel Jasperc1bc38e2015-09-29 14:57:55 +000047 if self.nested_struct:
48 s += indent('\n\nNested configuration flags:\n\n%s\n' %self.nested_struct,
49 2)
Alexander Kornienkod278e0e2013-09-04 15:09:13 +000050 return s
51
Daniel Jasperc1bc38e2015-09-29 14:57:55 +000052class NestedStruct:
53 def __init__(self, name, comment):
54 self.name = name
55 self.comment = comment.strip()
56 self.values = []
57
58 def __str__(self):
59 return '\n'.join(map(str, self.values))
60
61class NestedField:
62 def __init__(self, name, comment):
63 self.name = name
64 self.comment = comment.strip()
65
66 def __str__(self):
Krasimir Georgiev90b4ce32017-06-23 11:29:40 +000067 return '\n* ``%s`` %s' % (
68 self.name,
69 doxygen2rst(indent(self.comment, 2, indent_first_line=False)))
Daniel Jasperc1bc38e2015-09-29 14:57:55 +000070
Alexander Kornienkod278e0e2013-09-04 15:09:13 +000071class Enum:
72 def __init__(self, name, comment):
73 self.name = name
74 self.comment = comment.strip()
75 self.values = []
76
77 def __str__(self):
78 return '\n'.join(map(str, self.values))
79
80class EnumValue:
81 def __init__(self, name, comment):
82 self.name = name
Alexander Kornienko32718b62016-02-23 16:11:55 +000083 self.comment = comment
Alexander Kornienkod278e0e2013-09-04 15:09:13 +000084
85 def __str__(self):
86 return '* ``%s`` (in configuration: ``%s``)\n%s' % (
87 self.name,
88 re.sub('.*_', '', self.name),
89 doxygen2rst(indent(self.comment, 2)))
90
91def clean_comment_line(line):
Alexander Kornienko32718b62016-02-23 16:11:55 +000092 match = re.match(r'^/// \\code(\{.(\w+)\})?$', line)
93 if match:
94 lang = match.groups()[1]
95 if not lang:
96 lang = 'c++'
97 return '\n.. code-block:: %s\n\n' % lang
Daniel Jasper8ce1b8d2015-10-06 11:54:18 +000098 if line == '/// \\endcode':
99 return ''
100 return line[4:] + '\n'
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000101
102def read_options(header):
103 class State:
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000104 BeforeStruct, Finished, InStruct, InNestedStruct, InNestedFieldComent, \
105 InFieldComment, InEnum, InEnumMemberComment = range(8)
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000106 state = State.BeforeStruct
107
108 options = []
109 enums = {}
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000110 nested_structs = {}
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000111 comment = ''
112 enum = None
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000113 nested_struct = None
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000114
115 for line in header:
116 line = line.strip()
117 if state == State.BeforeStruct:
118 if line == 'struct FormatStyle {':
119 state = State.InStruct
120 elif state == State.InStruct:
121 if line.startswith('///'):
122 state = State.InFieldComment
123 comment = clean_comment_line(line)
124 elif line == '};':
125 state = State.Finished
126 break
127 elif state == State.InFieldComment:
128 if line.startswith('///'):
129 comment += clean_comment_line(line)
130 elif line.startswith('enum'):
131 state = State.InEnum
132 name = re.sub(r'enum\s+(\w+)\s*\{', '\\1', line)
133 enum = Enum(name, comment)
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000134 elif line.startswith('struct'):
135 state = State.InNestedStruct
136 name = re.sub(r'struct\s+(\w+)\s*\{', '\\1', line)
137 nested_struct = NestedStruct(name, comment)
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000138 elif line.endswith(';'):
139 state = State.InStruct
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000140 field_type, field_name = re.match(r'([<>:\w(,\s)]+)\s+(\w+);',
141 line).groups()
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000142 option = Option(str(field_name), str(field_type), comment)
143 options.append(option)
144 else:
145 raise Exception('Invalid format, expected comment, field or enum')
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000146 elif state == State.InNestedStruct:
147 if line.startswith('///'):
148 state = State.InNestedFieldComent
149 comment = clean_comment_line(line)
150 elif line == '};':
151 state = State.InStruct
152 nested_structs[nested_struct.name] = nested_struct
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000153 elif state == State.InNestedFieldComent:
154 if line.startswith('///'):
155 comment += clean_comment_line(line)
156 else:
157 state = State.InNestedStruct
158 nested_struct.values.append(NestedField(line.replace(';', ''), comment))
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000159 elif state == State.InEnum:
160 if line.startswith('///'):
161 state = State.InEnumMemberComment
162 comment = clean_comment_line(line)
163 elif line == '};':
164 state = State.InStruct
165 enums[enum.name] = enum
166 else:
167 raise Exception('Invalid format, expected enum field comment or };')
168 elif state == State.InEnumMemberComment:
169 if line.startswith('///'):
170 comment += clean_comment_line(line)
171 else:
172 state = State.InEnum
173 enum.values.append(EnumValue(line.replace(',', ''), comment))
174 if state != State.Finished:
175 raise Exception('Not finished by the end of file')
176
177 for option in options:
Daniel Jasperb5524822014-04-09 14:05:49 +0000178 if not option.type in ['bool', 'unsigned', 'int', 'std::string',
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000179 'std::vector<std::string>',
Krasimir Georgiev818da9b2017-11-09 15:41:23 +0000180 'std::vector<IncludeCategory>',
181 'std::vector<RawStringFormat>']:
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000182 if enums.has_key(option.type):
183 option.enum = enums[option.type]
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000184 elif nested_structs.has_key(option.type):
Krasimir Georgiev90b4ce32017-06-23 11:29:40 +0000185 option.nested_struct = nested_structs[option.type]
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000186 else:
187 raise Exception('Unknown type: %s' % option.type)
188 return options
189
190options = read_options(open(FORMAT_STYLE_FILE))
191
192options = sorted(options, key=lambda x: x.name)
193options_text = '\n\n'.join(map(str, options))
194
195contents = open(DOC_FILE).read()
196
197contents = substitute(contents, 'FORMAT_STYLE_OPTIONS', options_text)
198
Benjamin Kramer611d33a2015-11-20 07:46:19 +0000199with open(DOC_FILE, 'wb') as output:
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000200 output.write(contents)