summaryrefslogtreecommitdiff
path: root/sandbox/blais/movesec/docutils-movesec
blob: cd8b8f951a58d904a8cc899191955a397c239580 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-
#******************************************************************************\
#* $Source$
#* $Id$
#*
#* Copyright (C) 2003, Martin Blais <blais@furius.ca>
#*
#* This program is free software; you can redistribute it and/or modify
#* it under the terms of the GNU General Public License as published by
#* the Free Software Foundation; either version 2 of the License, or
#* (at your option) any later version.
#*
#* This program is distributed in the hope that it will be useful,
#* but WITHOUT ANY WARRANTY; without even the implied warranty of
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#* GNU General Public License for more details.
#*
#* You should have received a copy of the GNU General Public License
#* along with this program; if not, write to the Free Software
#* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#*
#*****************************************************************************/

"""docutils-movesec [<options>] <newchar> <file>

Move docutils section underlines one level up or down.  This program can be used
to change the level of your sections within a restructured text file.

'newchar' is the character to use for the replacing the bottom-level (or
top-level if --down) section underlines.

Note: the whitespace characters that appear after the section underlines are
preserved.

Note2: this might screw up transition markers.

"""

__version__ = "$Revision$"
__author__ = "Martin Blais <blais@furius.ca>"

#===============================================================================
# EXTERNAL DECLARATIONS
#===============================================================================

import sys, re

#===============================================================================
# LOCAL DECLARATIONS
#===============================================================================

# regular expression to get lines with no whitespace in front and a single word.
secre = re.compile('^([^\s]+)\s*$')

#-------------------------------------------------------------------------------
#
def getlines( fn ):
    """Opens the given file and detects the section lines. Return the line
    numbers."""

    if fn == '-':
        f = sys.stdin
    else:
        try:
            f = open(fn, 'r')
        except IOError, e:
            raise SystemExit("Error: opening file '%s':\n%s" % (fn, str(e)))

    filelines = f.readlines()

    minthres = 3
    seclines = []
    lc = -1
    for l in filelines:
        lc += 1

        mo = secre.match(l)
        if not mo:
            continue

        l = mo.group(1)
        if len(l) < minthres:
            continue

        c = l[0]
        skip = 0
        for cc in l[1:]:
            if c != cc:
                skip = 1
                break
        if not skip:
            seclines.append(lc)

    return seclines, filelines

#-------------------------------------------------------------------------------
#
def compute_order( seclines, filelines ):
    """Compute ordering characters."""

    secorder = []
    prevord = -1
    for sl in seclines:
        l = filelines[sl]

        try:
            prevord = secorder.index(l[0])
        except ValueError:
            if prevord != len(secorder) - 1:
                raise SystemExit(\
                    "Error: sections are not consistent at line '%d'" % (sl+1))
            prevord = len(secorder)
            secorder.append(l[0])

    return secorder

#===============================================================================
# MAIN
#===============================================================================

#-------------------------------------------------------------------------------
#
def main():
    debug = False

    import optparse
    parser = optparse.OptionParser(__doc__)
    parser.add_option('-u', '--up', action="store_true", dest="direction",
                      default=True,
                      help="specify move sections up (default)")
    parser.add_option('-d', '--down', action="store_false", dest="direction",
                      help="specify move sections down")
    opts, args = parser.parse_args()

    if len(args) != 2:
        raise SystemExit('Error: please a new character and a single filename.')
    newchar, fn = args

    seclines, filelines = getlines(fn)

    secorder = compute_order(seclines, filelines)

    # create new output order.
    if opts.direction: # up
        neworder = [newchar] + secorder[:-1]
    else: # down
        neworder = secorder[1:] + [newchar]

    # compute a mapping of old to new
    omap = {}
    for i in xrange(len(secorder)):
        omap[ secorder[i] ] = neworder[i]

    if debug:
        for sl in seclines:
            l = filelines[sl]
            print secorder.index(l[0]), l,
        from pprint import pprint
        print secorder
        print neworder
        pprint(omap)
        
    # perform replacement of desired line, preserve original whitespace.
    for sl in seclines:
        l = filelines[sl]
        nc = len(l.strip())
        filelines[sl] = omap[ l[0] ] * nc + l[nc:]

    # output.
    for l in filelines:
        sys.stdout.write(l)

if __name__ == '__main__':
    main()