Skip to content

Instantly share code, notes, and snippets.

@westurner
Created September 16, 2012 23:21
Show Gist options
  • Save westurner/3734801 to your computer and use it in GitHub Desktop.
Save westurner/3734801 to your computer and use it in GitHub Desktop.
ReStructuredText Heading Shift
#!/usr/bin/env python
"""
A utility for modifying ReStructuredText headings by the
specified shift offset.
For example::
==========
Title
==========
Heading 1
=========
Heading 2
---------
Heading 3
~~~~~~~~~
Shifted by 1::
Title
======
Heading 1
----------
Heading 2
~~~~~~~~~~
Heading 3
'''''''''
"""
import re
import optparse
RE_TITLE = re.compile(r'^([\=|\-|\~]+)\n(.*)\n^([\=|\-|\~]+)\n', re.MULTILINE)
RE_HEADING = re.compile(r'^(?:[\=|\-|\~]?)(?:[\n]?)(.*)\n^([\=|\-|\~]+)\n',
re.MULTILINE)
STANDARD_HEADINGS = {
0: '==',
1: '=',
2: '-',
3: '~',
4: "'",
5: '"',
6: '`',
7: '^',
8: '_',
9: '*',
10: '+',
11: '#',
12: '<',
13: '>'}
def restructuredtext_shift(input_file, shiftby):
"""
Shift the headings of a restructuredtext document
:param input_file: ReStructuredText file to read
:type input_file: str
:param shiftby: offset to shift headings by
:type shiftby: signed int
"""
shiftfactor = shiftby
f = open(input_file,'r+')
lines = f.read()
depthcount = 0
headings = {}
sections = []
# Special case for title
title_obj = RE_TITLE.search(lines)
(overline, title, underline) = title_obj.groups()
#if len(overline) == len(underline):
headings[overline[0]*2] = 0
print 0, title
#sections.append((0, '\n'.join([overline, title, underline])))
# Extract section headings and existing section heading numbering
for g in RE_HEADING.finditer(lines):
(text, line) = g.groups()
underline_char = line[0]
depth = headings.get(underline_char)
if not depth:
depthcount += 1
headings[underline_char] = depthcount
heading_level = headings[underline_char]
sections.append(
(heading_level, underline_char, '\n'.join((text, line))))
print heading_level, heading_level * ' ', text
STD_HEADINGS = STANDARD_HEADINGS.values()
# Expand headings with elements from STD_HEADINGS
for char in sorted(headings.keys()):
STD_HEADINGS.remove(char)
for n in xrange(len(STD_HEADINGS)):
dest = depthcount + ((((shiftby > 0) and 1 or -1)) * n)
headings[STD_HEADINGS[n]] = dest
headings_by_n = dict(
(x[1], x[0]) for x in headings.items(),
)
#output = copy.copy(lines)
output = lines
# Perform underline replacements
for section in sections:
(level, char, text) = section
newlevel = level + shiftby
newchar = headings_by_n[newlevel]
section, underline = text.split('\n',1)
newtext = '\n'.join((section, underline.replace(char, newchar)))
output = output.replace(text, newtext, 1)
return output
def main():
prs = optparse.OptionParser()
prs.add_option('-i','--input-file',dest='input_file',action='store',
help='ReStructuredText file to reindent')
prs.add_option('-s','--shiftby',dest='shiftby',action='store',
help='Degrees to shift ReStructuredText headings by (eg 1, -1)')
(opts,args) = prs.parse_args()
if not (opts.input_file and opts.shiftby):
raise Exception("Must specify both --input-file and --shiftby")
exit(0)
print restructuredtext_shift(opts.input_file, int(opts.shiftby))
if __name__=="__main__":
main()
@westurner
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment