• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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