Nathan Grigg

Number lines

I was reading Dr. Drang’s recent post about numbering lines starting at something other than one, and my first thought was, “That’s silly, the script already does that.” Because my script does, and I was certain I was using his line-numbering Python script.

Apparently I wrote my own. It’s nothing complicated, but it is full featured. It parses the first line to get the starting line number, then replaces any existing line numbers with the correct ones.

My script changes

9: a
b
c

to

 9: a
10: b
11: c

and

 1: a
10: b
11: c

to

1: a
2: b
3: c

It can even remove line numbers if the first line starts with “X:”, so it changes

X: a
2: b
3: c

to

a
b
c

I save it as ~/bin/numberlines, so I can use it on the command line or by selecting lines and typing ':!numberlines in Vim.

#!/usr/bin/python
"""
Number lines on a block of text, preserving indentation.
Allow the first line to indicate start number.
If the first lines starts with 'X:' then remove line numbers.
"""
import re
import sys

LINE_NO = re.compile(r"^\s*(\d*|[Xx]*):( |$)")

lines = sys.stdin.readlines()
first_line_number = LINE_NO.match(lines[0])

# Determine indentation.
indent = min(
    (re.match(" *|\t*", line).group(0)
            for line in lines if line.strip()),
    key=len)

if first_line_number and first_line_number.group(1)[0] in "Xx":
    formatter = "{indent}{line}"
    # These are irrelevant.
    start = 1
    padding = 0
else:
    formatter = "{indent}{number:>{padding}}: {line}"
    start = int(first_line_number.group(1)) if first_line_number else 1
    padding = len(str(start + len(lines)))

for i, line in enumerate(lines):
    line = LINE_NO.sub("", line[len(indent):])
    if not line: line = "\n"
    sys.stdout.write(formatter.format(
        indent=indent,
        number=start + i,
        padding=padding,
        line=line))