reflow.py
1 #!/usr/bin/python3 2 # 3 # Copyright (c) 2016-2019 The Khronos Group Inc. 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # you may not use this file except in compliance with the License. 7 # You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 17 # Used for automatic reflow of Vulkan spec to satisfy the agreed layout to 18 # minimize git churn. Most of the logic has to do with detecting asciidoc 19 # markup or block types that *shouldn't* be reflowed (tables, code) and 20 # ignoring them. It's very likely there are many asciidoc constructs not yet 21 # accounted for in the script, our usage of asciidoc markup is intentionally 22 # somewhat limited. 23 # 24 # Also used to insert identifying tags on explicit Valid Usage statements. 25 26 # Usage: reflow.py [-noflow] [-tagvu] [-nextvu #] [-overwrite] [-out dir] [-suffix str] files 27 # -noflow acts as a passthrough, instead of reflowing text. Other 28 # processing may occur. 29 # -tagvu generates explicit VUID tag for Valid Usage statements which 30 # don't already have them. 31 # -nextvu # starts VUID tag generation at the specified # instead of 32 # the value wired into the reflow.py script. 33 # -overwrite updates in place (can be risky, make sure there are backups) 34 # -out specifies directory to create output file in, default 'out' 35 # -suffix specifies suffix to add to output files, default '' 36 # files are asciidoc source files from the Vulkan spec to reflow. 37 38 # For error and file-loading interfaces only 39 import argparse 40 import os 41 import re 42 import sys 43 from reflib import loadFile, logDiag, logWarn, setLogFile 44 from reflow_count import startVUID 45 46 # Vulkan-specific - will consolidate into scripts/ like OpenXR soon 47 sys.path.insert(0, 'xml') 48 49 import vkapi as api 50 from vkconventions import VulkanConventions as APIConventions 51 conventions = APIConventions() 52 53 # Markup that always ends a paragraph 54 # empty line or whitespace 55 # [block options] 56 # [[anchor]] 57 # // comment 58 # <<<< page break 59 # :attribute-setting 60 # macro-directive::terms 61 # + standalone list item continuation 62 # label:: labelled list - label must be standalone 63 endPara = re.compile(r'^( *|\[.*\]|//.*|<<<<|:.*|[a-z]+::.*|\+|.*::)$') 64 65 # Special case of markup ending a paragraph, used to track the current 66 # command/structure. This allows for either OpenXR or Vulkan API path 67 # conventions. Nominally it should use the file suffix defined by the API 68 # conventions (conventions.file_suffix), except that XR uses '.txt' for 69 # generated API include files, not '.adoc' like its other includes. 70 includePat = re.compile( 71 r'include::(?P<directory_traverse>((../){1,4}|\{INCS-VAR\}/|\{generated\}/)(generated/)?)(?P<generated_type>[\w]+)/(?P<category>\w+)/(?P<entity_name>[^./]+).txt[\[][\]]') 72 73 # Find the first pname: pattern in a Valid Usage statement 74 pnamePat = re.compile(r'pname:(?P<param>\w+)') 75 76 # Markup that's OK in a contiguous paragraph but otherwise passed through 77 # .anything 78 # === Section Titles 79 endParaContinue = re.compile(r'^(\..*|=+ .*)$') 80 81 # Markup for block delimiters whose contents *should* be reformatted 82 # -- (exactly two) (open block) 83 # **** (4 or more) (sidebar block - why do we have these?!) 84 # ==== (4 or more) (example block) 85 # ____ (4 or more) (quote block) 86 blockReflow = re.compile(r'^(--|[*=_]{4,})$') 87 # Fake block delimiters for "common" VU statements 88 blockCommonReflow = '// Common Valid Usage\n' 89 import pdb 90 91 # Markup for block delimiters whose contents should *not* be reformatted 92 # |=== (3 or more) (table) 93 # ++++ (4 or more) (passthrough block) 94 # .... (4 or more) (literal block) 95 # //// (4 or more) (comment block) 96 # ---- (4 or more) (listing block) 97 # **** (4 or more) (sidebar block) 98 blockPassthrough = re.compile(r'^(\|={3,}|[-+./]{4,})$') 99 100 # Markup for introducing bullet points (hanging paragraphs) 101 # * bullet 102 # ** bullet 103 # -- bullet 104 # . bullet 105 # :: bullet 106 beginBullet = re.compile(r'^ *([*-.]+|::) ') 107 108 # Text that (may) not end sentences 109 110 # A single letter followed by a period, typically a middle initial. 111 endInitial = re.compile(r'^[A-Z]\.$') 112 # An abbreviation, which doesn't (usually) end a line. 113 endAbbrev = re.compile(r'(e\.g|i\.e|c\.f)\.$', re.IGNORECASE) 114 115 # State machine for reflowing. 116 # 117 # blockStack - The last element is a line with the asciidoc block delimiter 118 # that's currently in effect, such as 119 # '--', '----', '****', '======', or '+++++++++'. 120 # This affects whether or not the block contents should be formatted. 121 # reflowStack - The last element is True or False if the current blockStack 122 # contents should be reflowed. 123 # vuStack - the last element is True or False if the current blockStack 124 # contents are an explicit Valid Usage block. 125 # margin - margin to reflow text to. 126 # para - list of lines in the paragraph being accumulated. When this is 127 # non-empty, there is a current paragraph. 128 # lastTitle - true if the previous line was a document title line (e.g. 129 # :leveloffset: 0 - no attempt to track changes to this is made). 130 # leadIndent - indent level (in spaces) of the first line of a paragraph. 131 # hangIndent - indent level of the remaining lines of a paragraph. 132 # file - file pointer to write to. 133 # filename - base name of file being read from. 134 # lineNumber - line number being read from the input file. 135 # breakPeriod - True if justification should break to a new line after 136 # the end of a sentence. 137 # breakInitial - True if justification should break to a new line after 138 # something that appears to be an initial in someone's name. **TBD** 139 # reflow - True if text should be reflowed, False to pass through unchanged. 140 # vuPrefix - Prefix of generated Valid Usage tags 141 # vuFormat - Format string for generating Valid Usage tags. First argument 142 # is vuPrefix, second is command/struct name, third is parameter name, 143 # fourth is the tag number. 144 # nextvu - Integer to start tagging un-numbered Valid Usage statements with, 145 # or None if no tagging should be done. 146 # apiName - String name of a Vulkan structure or command for VUID tag 147 # generation, or None if one hasn't been included in this file yet. 148 class ReflowState: 149 """Represents the state of the reflow operation""" 150 def __init__(self, 151 filename, 152 margin = 76, 153 file = sys.stdout, 154 breakPeriod = True, 155 reflow = True, 156 nextvu = None): 157 self.blockStack = [ None ] 158 self.reflowStack = [ True ] 159 self.vuStack = [ False ] 160 self.margin = margin 161 self.para = [] 162 self.lastTitle = False 163 self.leadIndent = 0 164 self.hangIndent = 0 165 self.file = file 166 self.filename = filename 167 self.lineNumber = 0 168 self.breakPeriod = breakPeriod 169 self.breakInitial = True 170 self.reflow = reflow 171 self.vuPrefix = 'VUID' 172 self.vuFormat = '{0}-{1}-{2}-{3:0>5d}' 173 self.nextvu = nextvu 174 self.apiName = '' 175 176 def incrLineNumber(self): 177 self.lineNumber = self.lineNumber + 1 178 179 # Print an array of lines with newlines already present 180 def printLines(self, lines): 181 logDiag(':: printLines:', len(lines), 'lines: ', lines[0], end='') 182 for line in lines: 183 print(line, file=self.file, end='') 184 185 # Returns True if word ends with a sentence-period, False otherwise. 186 # Allows for contraction cases which won't end a line: 187 # - A single letter (if breakInitial is True) 188 # - Abbreviations: 'c.f.', 'e.g.', 'i.e.' (or mixed-case versions) 189 def endSentence(self, word): 190 if (word[-1:] != '.' or 191 endAbbrev.search(word) or 192 (self.breakInitial and endInitial.match(word))): 193 return False 194 195 return True 196 197 # Returns True if word is a Valid Usage ID Tag anchor. 198 def vuidAnchor(self, word): 199 return (word[0:7] == '[[VUID-') 200 201 # Reflow the current paragraph, respecting the paragraph lead and 202 # hanging indentation levels. The algorithm also respects trailing '+' 203 # signs that indicate embedded newlines, and will not reflow a very long 204 # word immediately after a bullet point. 205 # Just return the paragraph unchanged if the -noflow argument was 206 # given. 207 def reflowPara(self): 208 if not self.reflow: 209 return self.para 210 211 logDiag('reflowPara lead indent = ', self.leadIndent, 212 'hangIndent =', self.hangIndent, 213 'para:', self.para[0], end='') 214 215 # Total words processed (we care about the *first* word vs. others) 216 wordCount = 0 217 218 # Tracks the *previous* word processed. It must not be empty. 219 prevWord = ' ' 220 221 #import pdb; pdb.set_trace() 222 223 for line in self.para: 224 line = line.rstrip() 225 words = line.split() 226 227 # logDiag('reflowPara: input line =', line) 228 numWords = len(words) - 1 229 230 for i in range(0, numWords + 1): 231 word = words[i] 232 wordLen = len(word) 233 wordCount += 1 234 235 endEscape = False 236 if i == numWords and word == '+': 237 # Trailing ' +' must stay on the same line 238 endEscape = word 239 # logDiag('reflowPara last word of line =', word, 'prevWord =', prevWord, 'endEscape =', endEscape) 240 else: 241 pass 242 # logDiag('reflowPara wordCount =', wordCount, 'word =', word, 'prevWord =', prevWord) 243 244 if wordCount == 1: 245 # The first word of the paragraph is treated specially. 246 # The loop logic becomes trickier if all this code is 247 # done prior to looping over lines and words, so all the 248 # setup logic is done here. 249 250 outPara = [] 251 outLine = ''.ljust(self.leadIndent) + word 252 outLineLen = self.leadIndent + wordLen 253 254 # If the paragraph begins with a bullet point, generate 255 # a hanging indent level if there isn't one already. 256 if beginBullet.match(self.para[0]): 257 bulletPoint = True 258 if len(self.para) > 1: 259 logDiag('reflowPara first line matches bullet point', 260 'but indent already hanging @ input line', 261 self.lineNumber) 262 else: 263 logDiag('reflowPara first line matches bullet point -' 264 'single line, assuming hangIndent @ input line', 265 self.lineNumber) 266 self.hangIndent = outLineLen + 1 267 else: 268 bulletPoint = False 269 else: 270 # Possible actions to take with this word 271 # 272 # addWord - add word to current line 273 # closeLine - append line and start a new (null) one 274 # startLine - add word to a new line 275 276 # Default behavior if all the tests below fail is to add 277 # this word to the current line, and keep accumulating 278 # that line. 279 (addWord, closeLine, startLine) = (True, False, False) 280 281 # How long would this line be if the word were added? 282 newLen = outLineLen + 1 + wordLen 283 284 # Are we on the first word following a bullet point? 285 firstBullet = (wordCount == 2 and bulletPoint) 286 287 if endEscape: 288 # If the new word ends the input line with ' +', 289 # add it to the current line. 290 291 (addWord, closeLine, startLine) = (True, True, False) 292 elif self.vuidAnchor(word): 293 # If the new word is a Valid Usage anchor, break the 294 # line afterwards. Note that this should only happen 295 # immediately after a bullet point, but we don't 296 # currently check for this. 297 (addWord, closeLine, startLine) = (True, True, False) 298 elif newLen > self.margin: 299 if firstBullet: 300 # If the word follows a bullet point, add it to 301 # the current line no matter its length. 302 303 (addWord, closeLine, startLine) = (True, True, False) 304 else: 305 # The word overflows, so add it to a new line. 306 307 (addWord, closeLine, startLine) = (False, True, True) 308 elif (self.breakPeriod and 309 (wordCount > 2 or not firstBullet) and 310 self.endSentence(prevWord)): 311 # If the previous word ends a sentence and 312 # breakPeriod is set, start a new line. 313 # The complicated logic allows for leading bullet 314 # points which are periods (implicitly numbered lists). 315 # @@@ But not yet for explicitly numbered lists. 316 317 (addWord, closeLine, startLine) = (False, True, True) 318 319 # Add a word to the current line 320 if addWord: 321 if outLine: 322 outLine += ' ' + word 323 outLineLen = newLen 324 else: 325 # Fall through to startLine case if there's no 326 # current line yet. 327 startLine = True 328 329 # Add current line to the output paragraph. Force 330 # starting a new line, although we don't yet know if it 331 # will ever have contents. 332 if closeLine: 333 if outLine: 334 outPara.append(outLine + '\n') 335 outLine = None 336 337 # Start a new line and add a word to it 338 if startLine: 339 outLine = ''.ljust(self.hangIndent) + word 340 outLineLen = self.hangIndent + wordLen 341 342 # Track the previous word, for use in breaking at end of 343 # a sentence 344 prevWord = word 345 346 # Add this line to the output paragraph. 347 if outLine: 348 outPara.append(outLine + '\n') 349 350 return outPara 351 352 # Emit a paragraph, possibly reflowing it depending on the block 353 # context. Reset the paragraph accumulator. 354 def emitPara(self): 355 if self.para != []: 356 if self.vuStack[-1] and self.nextvu is not None: 357 # If: 358 # - this paragraph is in a Valid Usage block, 359 # - VUID tags are being assigned, 360 # Try to assign VUIDs 361 362 if nestedVuPat.search(self.para[0]): 363 # Check for nested bullet points. These should not be 364 # assigned VUIDs, nor present at all, because they break 365 # the VU extractor. 366 logWarn(self.filename + ': Invalid nested bullet point in VU block:', self.para[0]) 367 elif self.vuPrefix not in self.para[0]: 368 # If: 369 # - a tag is not already present, and 370 # - the paragraph is a properly marked-up list item 371 # Then add a VUID tag starting with the next free ID. 372 373 # Split the first line after the bullet point 374 matches = vuPat.search(self.para[0]) 375 if matches is not None: 376 logDiag('findRefs: Matched vuPat on line:', self.para[0], end='') 377 head = matches.group('head') 378 tail = matches.group('tail') 379 380 # Use the first pname: statement in the paragraph as 381 # the parameter name in the VUID tag. This won't always 382 # be correct, but should be highly reliable. 383 for vuLine in self.para: 384 matches = pnamePat.search(vuLine) 385 if matches is not None: 386 break 387 388 if matches is not None: 389 paramName = matches.group('param') 390 else: 391 paramName = 'None' 392 logWarn(self.filename, 393 'No param name found for VUID tag on line:', 394 self.para[0]) 395 396 newline = (head + ' [[' + 397 self.vuFormat.format(self.vuPrefix, 398 self.apiName, 399 paramName, 400 self.nextvu) + ']] ' + tail) 401 402 logDiag('Assigning', self.vuPrefix, self.apiName, self.nextvu, 403 ' on line:', self.para[0], '->', newline, 'END') 404 405 self.para[0] = newline 406 self.nextvu = self.nextvu + 1 407 # else: 408 # There are only a few cases of this, and they're all 409 # legitimate. Leave detecting this case to another tool 410 # or hand inspection. 411 # logWarn(self.filename + ': Unexpected non-bullet item in VU block (harmless if following an ifdef):', 412 # self.para[0]) 413 414 if self.reflowStack[-1]: 415 self.printLines(self.reflowPara()) 416 else: 417 self.printLines(self.para) 418 419 # Reset the paragraph, including its indentation level 420 self.para = [] 421 self.leadIndent = 0 422 self.hangIndent = 0 423 424 # 'line' ends a paragraph and should itself be emitted. 425 # line may be None to indicate EOF or other exception. 426 def endPara(self, line): 427 logDiag('endPara line', self.lineNumber, ': emitting paragraph') 428 429 # Emit current paragraph, this line, and reset tracker 430 self.emitPara() 431 432 if line: 433 self.printLines( [ line ] ) 434 435 # 'line' ends a paragraph (unless there's already a paragraph being 436 # accumulated, e.g. len(para) > 0 - currently not implemented) 437 def endParaContinue(self, line): 438 self.endPara(line) 439 440 # 'line' begins or ends a block. If beginning a block, tag whether or 441 # not to reflow the contents. 442 # vuBlock is True if the previous line indicates this is a Valid Usage 443 # block. 444 def endBlock(self, line, reflow = False, vuBlock = False): 445 self.endPara(line) 446 447 if self.blockStack[-1] == line: 448 logDiag('endBlock line', self.lineNumber, 449 ': popping block end depth:', len(self.blockStack), 450 ':', line, end='') 451 self.blockStack.pop() 452 self.reflowStack.pop() 453 self.vuStack.pop() 454 else: 455 # Start a block 456 self.blockStack.append(line) 457 self.reflowStack.append(reflow) 458 self.vuStack.append(vuBlock) 459 460 logDiag('endBlock reflow =', reflow, ' line', self.lineNumber, 461 ': pushing block start depth', len(self.blockStack), 462 ':', line, end='') 463 464 # 'line' begins or ends a block. The paragraphs in the block *should* be 465 # reformatted (e.g. a NOTE). 466 def endParaBlockReflow(self, line, vuBlock): 467 self.endBlock(line, reflow = True, vuBlock = vuBlock) 468 469 # 'line' begins or ends a block. The paragraphs in the block should 470 # *not* be reformatted (e.g. a NOTE). 471 def endParaBlockPassthrough(self, line): 472 self.endBlock(line, reflow = False) 473 474 # 'line' starts or continues a paragraph. 475 # Paragraphs may have "hanging indent", e.g. 476 # * Bullet point... 477 # ... continued 478 # In this case, when the higher indentation level ends, so does the 479 # paragraph. 480 def addLine(self, line): 481 logDiag('addLine line', self.lineNumber, ':', line, end='') 482 483 # See https://stackoverflow.com/questions/13648813/what-is-the-pythonic-way-to-count-the-leading-spaces-in-a-string 484 indent = len(line) - len(line.lstrip()) 485 486 # A hanging paragraph ends due to a less-indented line. 487 if self.para != [] and indent < self.hangIndent: 488 logDiag('addLine: line reduces indentation, emit paragraph') 489 self.emitPara() 490 491 # A bullet point (or something that looks like one) always ends the 492 # current paragraph. 493 if beginBullet.match(line): 494 logDiag('addLine: line matches beginBullet, emit paragraph') 495 self.emitPara() 496 497 if self.para == []: 498 # Begin a new paragraph 499 self.para = [ line ] 500 self.leadIndent = indent 501 self.hangIndent = indent 502 else: 503 # Add a line to a paragraph. Increase the hanging indentation 504 # level - once. 505 if self.hangIndent == self.leadIndent: 506 self.hangIndent = indent 507 self.para.append(line) 508 509 def reflowFile(filename, args): 510 logDiag('reflow: filename', filename) 511 512 lines = loadFile(filename) 513 if lines is None: 514 return 515 516 # Output file handle and reflow object for this file. There are no race 517 # conditions on overwriting the input, but it's not recommended unless 518 # you have backing store such as git. 519 520 if args.overwrite: 521 outFilename = filename 522 else: 523 outFilename = args.outDir + '/' + os.path.basename(filename) + args.suffix 524 525 try: 526 fp = open(outFilename, 'w', encoding='utf8') 527 except: 528 logWarn('Cannot open output file', filename, ':', sys.exc_info()[0]) 529 return 530 531 state = ReflowState(filename, 532 file = fp, 533 reflow = not args.noflow, 534 nextvu = args.nextvu) 535 536 for line in lines: 537 state.incrLineNumber() 538 539 # Is this a title line (leading '= ' followed by text)? 540 thisTitle = False 541 542 # The logic here is broken. If we're in a non-reflowable block and 543 # this line *doesn't* end the block, it should always be 544 # accumulated. 545 546 # Test for a blockCommonReflow delimiter comment first, to avoid 547 # treating it solely as a end-Paragraph marker comment. 548 if line == blockCommonReflow: 549 # Starting or ending a pseudo-block for "common" VU statements. 550 551 # Common VU statements use an Asciidoc variable as the apiName, 552 # instead of inferring it from the most recent API include. 553 state.apiName = '{refpage}' 554 state.endParaBlockReflow(line, vuBlock = True) 555 556 elif blockReflow.match(line): 557 # Starting or ending a block whose contents may be reflowed. 558 # Blocks cannot be nested. 559 560 # Is this is an explicit Valid Usage block? 561 vuBlock = (state.lineNumber > 1 and 562 lines[state.lineNumber-2] == '.Valid Usage\n') 563 564 state.endParaBlockReflow(line, vuBlock) 565 566 elif endPara.match(line): 567 # Ending a paragraph. Emit the current paragraph, if any, and 568 # prepare to begin a new paragraph. 569 570 state.endPara(line) 571 572 # If this is an include:: line starting the definition of a 573 # structure or command, track that for use in VUID generation. 574 575 matches = includePat.search(line) 576 if matches is not None: 577 include_type = matches.group('category') 578 if include_type in ('protos', 'structs'): 579 state.apiName = matches.group('entity_name') 580 581 elif endParaContinue.match(line): 582 # For now, always just end the paragraph. 583 # Could check see if len(para) > 0 to accumulate. 584 585 state.endParaContinue(line) 586 587 # If it's a title line, track that 588 if line[0:2] == '= ': 589 thisTitle = True 590 591 elif blockPassthrough.match(line): 592 # Starting or ending a block whose contents must not be reflowed. 593 # These are tables, etc. Blocks cannot be nested. 594 595 state.endParaBlockPassthrough(line) 596 elif state.lastTitle: 597 # The previous line was a document title line. This line 598 # is the author / credits line and must not be reflowed. 599 600 state.endPara(line) 601 else: 602 # Just accumulate a line to the current paragraph. Watch out for 603 # hanging indents / bullet-points and track that indent level. 604 605 state.addLine(line) 606 607 state.lastTitle = thisTitle 608 609 # Cleanup at end of file 610 state.endPara(None) 611 612 # Sanity check on block nesting 613 if len(state.blockStack) > 1: 614 logWarn('file', filename, 615 'mismatched asciidoc block delimiters at EOF:', 616 state.blockStack[-1]) 617 618 fp.close() 619 620 # Update the 'nextvu' value 621 if args.nextvu != state.nextvu: 622 logWarn('Updated nextvu to', state.nextvu, 'after file', filename) 623 args.nextvu = state.nextvu 624 625 def reflowAllAdocFiles(folder_to_reflow, args): 626 for root, subdirs, files in os.walk(folder_to_reflow): 627 for file in files: 628 if file.endswith(conventions.file_suffix): 629 file_path = os.path.join(root, file) 630 reflowFile(file_path, args) 631 for subdir in subdirs: 632 sub_folder = os.path.join(root, subdir) 633 print('Sub-folder = %s' % sub_folder) 634 if subdir.lower() not in conventions.spec_no_reflow_dirs: 635 print(' Parsing = %s' % sub_folder) 636 reflowAllAdocFiles(sub_folder, args) 637 else: 638 print(' Skipping = %s' % sub_folder) 639 640 # Patterns used to recognize interesting lines in an asciidoc source file. 641 # These patterns are only compiled once. 642 643 # Explicit Valid Usage list item with one or more leading asterisks 644 # The re.DOTALL is needed to prevent vuPat.search() from stripping 645 # the trailing newline. 646 vuPat = re.compile(r'^(?P<head> [*]+)( *)(?P<tail>.*)', re.DOTALL) 647 648 # Pattern matching leading nested bullet points 649 global nestedVuPat 650 nestedVuPat = re.compile(r'^ \*\*') 651 652 if __name__ == '__main__': 653 parser = argparse.ArgumentParser() 654 655 parser.add_argument('-diag', action='store', dest='diagFile', 656 help='Set the diagnostic file') 657 parser.add_argument('-warn', action='store', dest='warnFile', 658 help='Set the warning file') 659 parser.add_argument('-log', action='store', dest='logFile', 660 help='Set the log file for both diagnostics and warnings') 661 parser.add_argument('-overwrite', action='store_true', 662 help='Overwrite input filenames instead of writing different output filenames') 663 parser.add_argument('-out', action='store', dest='outDir', 664 default='out', 665 help='Set the output directory in which updated files are generated (default: out)') 666 parser.add_argument('-tagvu', action='store_true', 667 help='Tag un-tagged Valid Usage statements starting at the value wired into reflow.py') 668 parser.add_argument('-nextvu', action='store', dest='nextvu', type=int, 669 default=None, 670 help='Tag un-tagged Valid Usage statements starting at the specified base VUID instead of the value wired into reflow.py') 671 parser.add_argument('-noflow', action='store_true', dest='noflow', 672 help='Do not reflow text. Other actions may apply.') 673 parser.add_argument('-suffix', action='store', dest='suffix', 674 default='', 675 help='Set the suffix added to updated file names (default: none)') 676 parser.add_argument('files', metavar='filename', nargs='*', 677 help='a filename to reflow text in') 678 parser.add_argument('--version', action='version', version='%(prog)s 1.0') 679 680 args = parser.parse_args() 681 682 setLogFile(True, True, args.logFile) 683 setLogFile(True, False, args.diagFile) 684 setLogFile(False, True, args.warnFile) 685 686 if args.overwrite: 687 logWarn('reflow.py: will overwrite all input files') 688 689 if args.tagvu and args.nextvu is None: 690 args.nextvu = startVUID 691 692 if args.nextvu is not None: 693 logWarn('Tagging untagged Valid Usage statements starting at', args.nextvu) 694 695 # If no files are specified, reflow the entire specification chapters folder 696 if not args.files: 697 folder_to_reflow = os.getcwd() 698 folder_to_reflow += '/' + conventions.spec_reflow_path 699 reflowAllAdocFiles(folder_to_reflow, args) 700 else: 701 for file in args.files: 702 reflowFile(file, args) 703 704 if args.nextvu is not None and args.nextvu != startVUID: 705 try: 706 reflow_count_file_path = os.path.dirname(os.path.realpath(__file__)) 707 reflow_count_file_path += '/reflow_count.py' 708 reflow_count_file = open(reflow_count_file_path, 'w', encoding='utf8') 709 print('# The value to start tagging VU statements at, unless overridden by -nextvu\n', file=reflow_count_file, end='') 710 count_string = 'startVUID = %d\n' % args.nextvu 711 print(count_string, file=reflow_count_file, end='') 712 reflow_count_file.close() 713 except: 714 logWarn('Cannot open output count file reflow_count.py', ':', sys.exc_info()[0])