dump_format_style.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  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. import collections
  6. import re
  7. import urllib2
  8. FORMAT_STYLE_FILE = '../../include/clang/Format/Format.h'
  9. DOC_FILE = '../ClangFormatStyleOptions.rst'
  10. def substitute(text, tag, contents):
  11. replacement = '\n.. START_%s\n\n%s\n\n.. END_%s\n' % (tag, contents, tag)
  12. pattern = r'\n\.\. START_%s\n.*\n\.\. END_%s\n' % (tag, tag)
  13. return re.sub(pattern, '%s', text, flags=re.S) % replacement
  14. def doxygen2rst(text):
  15. text = re.sub(r'<tt>\s*(.*?)\s*<\/tt>', r'``\1``', text)
  16. text = re.sub(r'\\c ([^ ,;\.]+)', r'``\1``', text)
  17. text = re.sub(r'\\\w+ ', '', text)
  18. return text
  19. def indent(text, columns):
  20. indent = ' ' * columns
  21. s = re.sub(r'\n([^\n])', '\n' + indent + '\\1', text, flags=re.S)
  22. if s.startswith('\n'):
  23. return s
  24. return indent + s
  25. class Option:
  26. def __init__(self, name, type, comment):
  27. self.name = name
  28. self.type = type
  29. self.comment = comment.strip()
  30. self.enum = None
  31. def __str__(self):
  32. s = '**%s** (``%s``)\n%s' % (self.name, self.type,
  33. doxygen2rst(indent(self.comment, 2)))
  34. if self.enum:
  35. s += indent('\n\nPossible values:\n\n%s\n' % self.enum, 2)
  36. return s
  37. class Enum:
  38. def __init__(self, name, comment):
  39. self.name = name
  40. self.comment = comment.strip()
  41. self.values = []
  42. def __str__(self):
  43. return '\n'.join(map(str, self.values))
  44. class EnumValue:
  45. def __init__(self, name, comment):
  46. self.name = name
  47. self.comment = comment.strip()
  48. def __str__(self):
  49. return '* ``%s`` (in configuration: ``%s``)\n%s' % (
  50. self.name,
  51. re.sub('.*_', '', self.name),
  52. doxygen2rst(indent(self.comment, 2)))
  53. def clean_comment_line(line):
  54. return line[3:].strip() + '\n'
  55. def read_options(header):
  56. class State:
  57. BeforeStruct, Finished, InStruct, InFieldComment, InEnum, \
  58. InEnumMemberComment = range(6)
  59. state = State.BeforeStruct
  60. options = []
  61. enums = {}
  62. comment = ''
  63. enum = None
  64. for line in header:
  65. line = line.strip()
  66. if state == State.BeforeStruct:
  67. if line == 'struct FormatStyle {':
  68. state = State.InStruct
  69. elif state == State.InStruct:
  70. if line.startswith('///'):
  71. state = State.InFieldComment
  72. comment = clean_comment_line(line)
  73. elif line == '};':
  74. state = State.Finished
  75. break
  76. elif state == State.InFieldComment:
  77. if line.startswith('///'):
  78. comment += clean_comment_line(line)
  79. elif line.startswith('enum'):
  80. state = State.InEnum
  81. name = re.sub(r'enum\s+(\w+)\s*\{', '\\1', line)
  82. enum = Enum(name, comment)
  83. elif line.endswith(';'):
  84. state = State.InStruct
  85. field_type, field_name = re.match(r'([<>:\w]+)\s+(\w+);', line).groups()
  86. option = Option(str(field_name), str(field_type), comment)
  87. options.append(option)
  88. else:
  89. raise Exception('Invalid format, expected comment, field or enum')
  90. elif state == State.InEnum:
  91. if line.startswith('///'):
  92. state = State.InEnumMemberComment
  93. comment = clean_comment_line(line)
  94. elif line == '};':
  95. state = State.InStruct
  96. enums[enum.name] = enum
  97. else:
  98. raise Exception('Invalid format, expected enum field comment or };')
  99. elif state == State.InEnumMemberComment:
  100. if line.startswith('///'):
  101. comment += clean_comment_line(line)
  102. else:
  103. state = State.InEnum
  104. enum.values.append(EnumValue(line.replace(',', ''), comment))
  105. if state != State.Finished:
  106. raise Exception('Not finished by the end of file')
  107. for option in options:
  108. if not option.type in ['bool', 'unsigned', 'int', 'std::string',
  109. 'std::vector<std::string>']:
  110. if enums.has_key(option.type):
  111. option.enum = enums[option.type]
  112. else:
  113. raise Exception('Unknown type: %s' % option.type)
  114. return options
  115. options = read_options(open(FORMAT_STYLE_FILE))
  116. options = sorted(options, key=lambda x: x.name)
  117. options_text = '\n\n'.join(map(str, options))
  118. contents = open(DOC_FILE).read()
  119. contents = substitute(contents, 'FORMAT_STYLE_OPTIONS', options_text)
  120. with open(DOC_FILE, 'w') as output:
  121. output.write(contents)