blob: 2e1e8c12297b294b8339996e06fee06e2e12ed47 [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')
Krasimir Georgievcf699ad2018-07-25 10:21:47 +000013INCLUDE_STYLE_FILE = os.path.join(CLANG_DIR, 'include/clang/Tooling/Inclusions/IncludeStyle.h')
Alexander Kornienko370bfb42016-02-23 16:11:43 +000014DOC_FILE = os.path.join(CLANG_DIR, 'docs/ClangFormatStyleOptions.rst')
Alexander Kornienkod278e0e2013-09-04 15:09:13 +000015
16
17def substitute(text, tag, contents):
18 replacement = '\n.. START_%s\n\n%s\n\n.. END_%s\n' % (tag, contents, tag)
19 pattern = r'\n\.\. START_%s\n.*\n\.\. END_%s\n' % (tag, tag)
20 return re.sub(pattern, '%s', text, flags=re.S) % replacement
21
22def doxygen2rst(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
Krasimir Georgiev90b4ce32017-06-23 11:29:40 +000028def indent(text, columns, indent_first_line=True):
Alexander Kornienkod278e0e2013-09-04 15:09:13 +000029 indent = ' ' * columns
30 s = re.sub(r'\n([^\n])', '\n' + indent + '\\1', text, flags=re.S)
Krasimir Georgiev90b4ce32017-06-23 11:29:40 +000031 if not indent_first_line or s.startswith('\n'):
Alexander Kornienkod278e0e2013-09-04 15:09:13 +000032 return s
33 return indent + s
34
Serge Guelton09616bd2018-12-03 12:12:48 +000035class Option(object):
Alexander Kornienkod278e0e2013-09-04 15:09:13 +000036 def __init__(self, name, type, comment):
37 self.name = name
38 self.type = type
39 self.comment = comment.strip()
40 self.enum = None
Daniel Jasperc1bc38e2015-09-29 14:57:55 +000041 self.nested_struct = None
Alexander Kornienkod278e0e2013-09-04 15:09:13 +000042
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)
Daniel Jasperc1bc38e2015-09-29 14:57:55 +000048 if self.nested_struct:
49 s += indent('\n\nNested configuration flags:\n\n%s\n' %self.nested_struct,
50 2)
Alexander Kornienkod278e0e2013-09-04 15:09:13 +000051 return s
52
Serge Guelton09616bd2018-12-03 12:12:48 +000053class NestedStruct(object):
Daniel Jasperc1bc38e2015-09-29 14:57:55 +000054 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
Serge Guelton09616bd2018-12-03 12:12:48 +000062class NestedField(object):
Daniel Jasperc1bc38e2015-09-29 14:57:55 +000063 def __init__(self, name, comment):
64 self.name = name
65 self.comment = comment.strip()
66
67 def __str__(self):
Krasimir Georgiev90b4ce32017-06-23 11:29:40 +000068 return '\n* ``%s`` %s' % (
69 self.name,
70 doxygen2rst(indent(self.comment, 2, indent_first_line=False)))
Daniel Jasperc1bc38e2015-09-29 14:57:55 +000071
Serge Guelton09616bd2018-12-03 12:12:48 +000072class Enum(object):
Alexander Kornienkod278e0e2013-09-04 15:09:13 +000073 def __init__(self, name, comment):
74 self.name = name
75 self.comment = comment.strip()
76 self.values = []
77
78 def __str__(self):
79 return '\n'.join(map(str, self.values))
80
Serge Guelton09616bd2018-12-03 12:12:48 +000081class EnumValue(object):
Alexander Kornienkod278e0e2013-09-04 15:09:13 +000082 def __init__(self, name, comment):
83 self.name = name
Alexander Kornienko32718b62016-02-23 16:11:55 +000084 self.comment = comment
Alexander Kornienkod278e0e2013-09-04 15:09:13 +000085
86 def __str__(self):
87 return '* ``%s`` (in configuration: ``%s``)\n%s' % (
88 self.name,
89 re.sub('.*_', '', self.name),
90 doxygen2rst(indent(self.comment, 2)))
91
92def clean_comment_line(line):
Alexander Kornienko32718b62016-02-23 16:11:55 +000093 match = re.match(r'^/// \\code(\{.(\w+)\})?$', line)
94 if match:
95 lang = match.groups()[1]
96 if not lang:
97 lang = 'c++'
98 return '\n.. code-block:: %s\n\n' % lang
Daniel Jasper8ce1b8d2015-10-06 11:54:18 +000099 if line == '/// \\endcode':
100 return ''
101 return line[4:] + '\n'
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000102
103def read_options(header):
Serge Guelton09616bd2018-12-03 12:12:48 +0000104 class State(object):
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000105 BeforeStruct, Finished, InStruct, InNestedStruct, InNestedFieldComent, \
106 InFieldComment, InEnum, InEnumMemberComment = range(8)
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000107 state = State.BeforeStruct
108
109 options = []
110 enums = {}
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000111 nested_structs = {}
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000112 comment = ''
113 enum = None
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000114 nested_struct = None
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000115
116 for line in header:
117 line = line.strip()
118 if state == State.BeforeStruct:
Krasimir Georgievcf699ad2018-07-25 10:21:47 +0000119 if line == 'struct FormatStyle {' or line == 'struct IncludeStyle {':
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000120 state = State.InStruct
121 elif state == State.InStruct:
122 if line.startswith('///'):
123 state = State.InFieldComment
124 comment = clean_comment_line(line)
125 elif line == '};':
126 state = State.Finished
127 break
128 elif state == State.InFieldComment:
129 if line.startswith('///'):
130 comment += clean_comment_line(line)
131 elif line.startswith('enum'):
132 state = State.InEnum
133 name = re.sub(r'enum\s+(\w+)\s*\{', '\\1', line)
134 enum = Enum(name, comment)
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000135 elif line.startswith('struct'):
136 state = State.InNestedStruct
137 name = re.sub(r'struct\s+(\w+)\s*\{', '\\1', line)
138 nested_struct = NestedStruct(name, comment)
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000139 elif line.endswith(';'):
140 state = State.InStruct
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000141 field_type, field_name = re.match(r'([<>:\w(,\s)]+)\s+(\w+);',
142 line).groups()
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000143 option = Option(str(field_name), str(field_type), comment)
144 options.append(option)
145 else:
146 raise Exception('Invalid format, expected comment, field or enum')
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000147 elif state == State.InNestedStruct:
148 if line.startswith('///'):
149 state = State.InNestedFieldComent
150 comment = clean_comment_line(line)
151 elif line == '};':
152 state = State.InStruct
153 nested_structs[nested_struct.name] = nested_struct
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000154 elif state == State.InNestedFieldComent:
155 if line.startswith('///'):
156 comment += clean_comment_line(line)
157 else:
158 state = State.InNestedStruct
159 nested_struct.values.append(NestedField(line.replace(';', ''), comment))
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000160 elif state == State.InEnum:
161 if line.startswith('///'):
162 state = State.InEnumMemberComment
163 comment = clean_comment_line(line)
164 elif line == '};':
165 state = State.InStruct
166 enums[enum.name] = enum
167 else:
168 raise Exception('Invalid format, expected enum field comment or };')
169 elif state == State.InEnumMemberComment:
170 if line.startswith('///'):
171 comment += clean_comment_line(line)
172 else:
173 state = State.InEnum
174 enum.values.append(EnumValue(line.replace(',', ''), comment))
175 if state != State.Finished:
176 raise Exception('Not finished by the end of file')
177
178 for option in options:
Daniel Jasperb5524822014-04-09 14:05:49 +0000179 if not option.type in ['bool', 'unsigned', 'int', 'std::string',
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000180 'std::vector<std::string>',
Krasimir Georgiev818da9b2017-11-09 15:41:23 +0000181 'std::vector<IncludeCategory>',
182 'std::vector<RawStringFormat>']:
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000183 if enums.has_key(option.type):
184 option.enum = enums[option.type]
Daniel Jasperc1bc38e2015-09-29 14:57:55 +0000185 elif nested_structs.has_key(option.type):
Krasimir Georgiev90b4ce32017-06-23 11:29:40 +0000186 option.nested_struct = nested_structs[option.type]
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000187 else:
188 raise Exception('Unknown type: %s' % option.type)
189 return options
190
191options = read_options(open(FORMAT_STYLE_FILE))
Krasimir Georgievcf699ad2018-07-25 10:21:47 +0000192options += read_options(open(INCLUDE_STYLE_FILE))
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000193
194options = sorted(options, key=lambda x: x.name)
195options_text = '\n\n'.join(map(str, options))
196
197contents = open(DOC_FILE).read()
198
199contents = substitute(contents, 'FORMAT_STYLE_OPTIONS', options_text)
200
Benjamin Kramer611d33a2015-11-20 07:46:19 +0000201with open(DOC_FILE, 'wb') as output:
Alexander Kornienkod278e0e2013-09-04 15:09:13 +0000202 output.write(contents)