Source code
Revision control
Copy as Markdown
Other Tools
#!/usr/bin/env vpython3
# Copyright (c) 2026 The WebRTC project authors. All Rights Reserved.
#
# Use of this source code is governed by a BSD-style license
# that can be found in the LICENSE file in the root of the source
# tree. An additional intellectual property rights grant can be found
# in the file PATENTS. All contributing project authors may
# be found in the AUTHORS file in the root of the source tree.
"""Wraps a CL description (commit message) at 72 columns.
Usage:
wrap_lines.py [FILE ...]
Reads the draft from the given files (or stdin) and prints the wrapped
description to stdout. Rules:
- The subject (first line) is kept on one line with whitespace normalized
and a single trailing period removed.
- Body paragraphs are re-flowed at 72 columns.
- Bullet items ("-", "*", "+", "1.", "1)") are re-flowed with continuation
lines aligned to the bullet text.
- Indented lines that are not bullets (code, logs, quotes) are kept as is.
- The trailing footer block (e.g. "Bug:", "Change-Id:") is kept as is, one
footer per line.
"""
import fileinput
import re
import textwrap
_BULLET_RE = re.compile(r'^(\s*(?:[-*+]|\d+[.)])\s+)(.*)$')
_FOOTER_RE = re.compile(r'^[A-Za-z][A-Za-z0-9_-]*(?:\[[^\]]+\])?[:=]\s*\S')
_INDENTED_RE = re.compile(r'^(?:\s{2,}|\t)\S')
def _strip_trailing_period(text):
if text.endswith('.') and not text.endswith('..'):
return text[:-1]
return text
def _fill(text, width, initial_indent='', subsequent_indent=''):
wrapper = textwrap.TextWrapper(width=width,
initial_indent=initial_indent,
subsequent_indent=subsequent_indent,
break_long_words=False,
break_on_hyphens=False)
return wrapper.fill(' '.join(text.split()))
def _is_footer_block(paragraph):
lines = paragraph.splitlines()
return bool(lines) and all(_FOOTER_RE.match(line) for line in lines)
def _wrap_paragraph(paragraph, width):
"""Wraps a paragraph that may mix prose, bullets and indented blocks."""
# Each item is (kind, lines) where kind is 'text', 'bullet' or 'verbatim'.
items = []
for line in paragraph.splitlines():
if _BULLET_RE.match(line):
items.append(('bullet', [line]))
elif _INDENTED_RE.match(line) and (not items
or items[-1][0] != 'bullet'):
items.append(('verbatim', [line]))
elif items and items[-1][0] in ('text', 'bullet'):
items[-1][1].append(line)
else:
items.append(('text', [line]))
out = []
for kind, lines in items:
if kind == 'verbatim':
out.append('\n'.join(line.rstrip() for line in lines))
elif kind == 'bullet':
marker = _BULLET_RE.match(lines[0]).group(1)
content = ' '.join(lines)[len(marker):]
out.append(_fill(content, width, marker, ' ' * len(marker)))
else:
out.append(_fill(' '.join(lines), width))
return '\n'.join(out)
def wrap_text(text, width=72):
"""Returns `text` formatted as a CL description wrapped at `width`."""
if not text.strip():
return ''
parts = text.strip('\n').split('\n', 1)
subject = _strip_trailing_period(' '.join(parts[0].split()))
body = parts[1].strip('\n') if len(parts) > 1 else ''
if not body.strip():
return subject
paragraphs = [p for p in re.split(r'\n\s*\n', body) if p.strip()]
footer = None
if paragraphs and _is_footer_block(paragraphs[-1]):
footer = '\n'.join(line.strip()
for line in paragraphs.pop().splitlines())
blocks = [_wrap_paragraph(p, width) for p in paragraphs]
if footer:
blocks.append(footer)
return subject + '\n\n' + '\n\n'.join(blocks)
if __name__ == '__main__':
print(wrap_text(''.join(fileinput.input())))