Files
clang-p2996/clang/docs/tools/dump_format_style.py
mydeveloperday 3125aa9959 [clang-format] ensure dump_format_style.py works with Python3 correctly
Summary:
Python2 has been removed from cygwin, this means anyone running the dump_format_style.py in a cygwin shell could pick up python3 instead

In Python3 all strings are unicode as the file is opened in binary mode we need to encode the contents string or we'll face the following error

```
Traceback (most recent call last):
  File "./dump_format_style.py", line 228, in <module>
    output.write(contents)
TypeError: a bytes-like object is required, not 'str'
```

Reviewed By: krasimir

Subscribers: cfe-commits

Tags: #clang, #clang-format

Differential Revision: https://reviews.llvm.org/D79326
2020-05-07 19:52:12 +01:00

229 lines
7.4 KiB
Python
Executable File

#!/usr/bin/env python
# A tool to parse the FormatStyle struct from Format.h and update the
# documentation in ../ClangFormatStyleOptions.rst automatically.
# Run from the directory in which this file is located to update the docs.
import collections
import os
import re
CLANG_DIR = os.path.join(os.path.dirname(__file__), '../..')
FORMAT_STYLE_FILE = os.path.join(CLANG_DIR, 'include/clang/Format/Format.h')
INCLUDE_STYLE_FILE = os.path.join(CLANG_DIR, 'include/clang/Tooling/Inclusions/IncludeStyle.h')
DOC_FILE = os.path.join(CLANG_DIR, 'docs/ClangFormatStyleOptions.rst')
def substitute(text, tag, contents):
replacement = '\n.. START_%s\n\n%s\n\n.. END_%s\n' % (tag, contents, tag)
pattern = r'\n\.\. START_%s\n.*\n\.\. END_%s\n' % (tag, tag)
return re.sub(pattern, '%s', text, flags=re.S) % replacement
def doxygen2rst(text):
text = re.sub(r'<tt>\s*(.*?)\s*<\/tt>', r'``\1``', text)
text = re.sub(r'\\c ([^ ,;\.]+)', r'``\1``', text)
text = re.sub(r'\\\w+ ', '', text)
return text
def indent(text, columns, indent_first_line=True):
indent = ' ' * columns
s = re.sub(r'\n([^\n])', '\n' + indent + '\\1', text, flags=re.S)
if not indent_first_line or s.startswith('\n'):
return s
return indent + s
class Option(object):
def __init__(self, name, type, comment):
self.name = name
self.type = type
self.comment = comment.strip()
self.enum = None
self.nested_struct = None
def __str__(self):
s = '**%s** (``%s``)\n%s' % (self.name, self.type,
doxygen2rst(indent(self.comment, 2)))
if self.enum:
s += indent('\n\nPossible values:\n\n%s\n' % self.enum, 2)
if self.nested_struct:
s += indent('\n\nNested configuration flags:\n\n%s\n' %self.nested_struct,
2)
return s
class NestedStruct(object):
def __init__(self, name, comment):
self.name = name
self.comment = comment.strip()
self.values = []
def __str__(self):
return '\n'.join(map(str, self.values))
class NestedField(object):
def __init__(self, name, comment):
self.name = name
self.comment = comment.strip()
def __str__(self):
return '\n* ``%s`` %s' % (
self.name,
doxygen2rst(indent(self.comment, 2, indent_first_line=False)))
class Enum(object):
def __init__(self, name, comment):
self.name = name
self.comment = comment.strip()
self.values = []
def __str__(self):
return '\n'.join(map(str, self.values))
class NestedEnum(object):
def __init__(self, name, enumtype, comment, values):
self.name = name
self.comment = comment
self.values = values
self.type = enumtype
def __str__(self):
s = '\n* ``%s %s``\n%s' % (self.type, self.name,
doxygen2rst(indent(self.comment, 2)))
s += indent('\nPossible values:\n\n', 2)
s += indent('\n'.join(map(str, self.values)),2)
return s;
class EnumValue(object):
def __init__(self, name, comment, config):
self.name = name
self.comment = comment
self.config = config
def __str__(self):
return '* ``%s`` (in configuration: ``%s``)\n%s' % (
self.name,
re.sub('.*_', '', self.config),
doxygen2rst(indent(self.comment, 2)))
def clean_comment_line(line):
match = re.match(r'^/// \\code(\{.(\w+)\})?$', line)
if match:
lang = match.groups()[1]
if not lang:
lang = 'c++'
return '\n.. code-block:: %s\n\n' % lang
if line == '/// \\endcode':
return ''
return line[4:] + '\n'
def read_options(header):
class State(object):
BeforeStruct, Finished, InStruct, InNestedStruct, InNestedFieldComent, \
InFieldComment, InEnum, InEnumMemberComment = range(8)
state = State.BeforeStruct
options = []
enums = {}
nested_structs = {}
comment = ''
enum = None
nested_struct = None
for line in header:
line = line.strip()
if state == State.BeforeStruct:
if line == 'struct FormatStyle {' or line == 'struct IncludeStyle {':
state = State.InStruct
elif state == State.InStruct:
if line.startswith('///'):
state = State.InFieldComment
comment = clean_comment_line(line)
elif line == '};':
state = State.Finished
break
elif state == State.InFieldComment:
if line.startswith('///'):
comment += clean_comment_line(line)
elif line.startswith('enum'):
state = State.InEnum
name = re.sub(r'enum\s+(\w+)\s*\{', '\\1', line)
enum = Enum(name, comment)
elif line.startswith('struct'):
state = State.InNestedStruct
name = re.sub(r'struct\s+(\w+)\s*\{', '\\1', line)
nested_struct = NestedStruct(name, comment)
elif line.endswith(';'):
state = State.InStruct
field_type, field_name = re.match(r'([<>:\w(,\s)]+)\s+(\w+);',
line).groups()
option = Option(str(field_name), str(field_type), comment)
options.append(option)
else:
raise Exception('Invalid format, expected comment, field or enum')
elif state == State.InNestedStruct:
if line.startswith('///'):
state = State.InNestedFieldComent
comment = clean_comment_line(line)
elif line == '};':
state = State.InStruct
nested_structs[nested_struct.name] = nested_struct
elif state == State.InNestedFieldComent:
if line.startswith('///'):
comment += clean_comment_line(line)
else:
state = State.InNestedStruct
field_type, field_name = re.match(r'([<>:\w(,\s)]+)\s+(\w+);',line).groups()
if field_type in enums:
nested_struct.values.append(NestedEnum(field_name,field_type,comment,enums[field_type].values))
else:
nested_struct.values.append(NestedField(field_type + " " + field_name, comment))
elif state == State.InEnum:
if line.startswith('///'):
state = State.InEnumMemberComment
comment = clean_comment_line(line)
elif line == '};':
state = State.InStruct
enums[enum.name] = enum
else:
raise Exception('Invalid format, expected enum field comment or };')
elif state == State.InEnumMemberComment:
if line.startswith('///'):
comment += clean_comment_line(line)
else:
state = State.InEnum
val = line.replace(',', '')
pos = val.find(" // ")
if (pos != -1):
config = val[pos+4:]
val = val[:pos]
else:
config = val;
enum.values.append(EnumValue(val, comment,config))
if state != State.Finished:
raise Exception('Not finished by the end of file')
for option in options:
if not option.type in ['bool', 'unsigned', 'int', 'std::string',
'std::vector<std::string>',
'std::vector<IncludeCategory>',
'std::vector<RawStringFormat>']:
if option.type in enums:
option.enum = enums[option.type]
elif option.type in nested_structs:
option.nested_struct = nested_structs[option.type]
else:
raise Exception('Unknown type: %s' % option.type)
return options
options = read_options(open(FORMAT_STYLE_FILE))
options += read_options(open(INCLUDE_STYLE_FILE))
options = sorted(options, key=lambda x: x.name)
options_text = '\n\n'.join(map(str, options))
contents = open(DOC_FILE).read()
contents = substitute(contents, 'FORMAT_STYLE_OPTIONS', options_text)
with open(DOC_FILE, 'wb') as output:
output.write(contents.encode())