aboutsummaryrefslogtreecommitdiff
path: root/.vim/ftplugin/latex-suite/outline.py
blob: 7bb68961e0dca6e0ec7cbbff5bc9ab0db8fa1310 (plain) (blame)
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
176
177
178
179
180
181
182
183
184
185
186
#!/usr/bin/python

# Part of Latex-Suite
#
# Copyright: Srinath Avadhanula
# Description:
#   This file implements a simple outline creation for latex documents.

import re
import os
import sys
import StringIO

# getFileContents {{{
def getFileContents(argin, ext=''):
    if type(argin) is str:
        fname = argin + ext
    else:
            fname = argin.group(3) + ext

    # This longish thing is to make sure that all files are converted into
    # \n seperated lines.
    contents = '\n'.join(open(fname).read().splitlines())

    # TODO what are all the ways in which a tex file can include another?
    pat = re.compile(r'^\\(@?)(include|input){(.*?)}', re.M)
    contents = re.sub(pat, lambda input: getFileContents(input, ext), contents)

    return ('%%==== FILENAME: %s' % fname) + '\n' + contents

# }}}
# stripComments {{{
def stripComments(contents):
    # remove all comments except those of the form
    # %%==== FILENAME: <filename.tex>
    uncomm = [re.sub('%(?!==== FILENAME: ).*', '', line) for line in contents.splitlines()]
    # also remove all only-whitespace lines.
    nonempty = [line for line in uncomm if line.strip()]

    return nonempty
# }}}
# addFileNameAndNumber {{{
def addFileNameAndNumber(lines):
    filename = ''
    retval = ''
    for line in lines:
        if re.match('%==== FILENAME: ', line):
            filename = line.split('%==== FILENAME: ')[1]
        else:
            retval += '<%s>%s\n' % (filename, line)

    return retval
# }}}
# getSectionLabels_Root {{{
def getSectionLabels_Root(lineinfo, section_prefix, label_prefix):
    prev_txt = ''
    inside_env = 0
    prev_env = ''
    outstr = StringIO.StringIO('')
    pres_depth = len(section_prefix)

    #print '+getSectionLabels_Root: lineinfo = [%s]' % lineinfo
    for line in lineinfo.splitlines():
        if not line:
            continue

        # throw away leading white-space
        m = re.search('<(.*?)>(.*)', line)

        fname = m.group(1)
        line = m.group(2).lstrip()

        # we found a label!
        m = re.search(r'\\label{(%s.*?)}' % label_prefix, line)
        if m:
            # add the current line (except the \label command) to the text
            # which will be displayed below this label
            prev_txt += re.search(r'(^.*?)\\label{', line).group(1)

            # for the figure environment however, just display the caption.
            # instead of everything since the \begin command.
            if prev_env == 'figure':
                cm = re.search(r'\caption(\[.*?\]\s*)?{(.*?)}', prev_txt)
                if cm:
                    prev_txt = cm.group(2)

            # print a nice formatted text entry like so
            #
            # >        eqn:label
            # :          e^{i\pi} + 1 = 0
            #
            # Use the current "section depth" for the leading indentation.
            print >>outstr, '>%s%s\t\t<%s>' % (' '*(2*pres_depth+2),
                    m.group(1), fname)
            print >>outstr, ':%s%s' % (' '*(2*pres_depth+4), prev_txt)
            prev_txt = ''

        # If we just encoutered the start or end of an environment or a
        # label, then do not remember this line. 
        # NOTE: This assumes that there is no equation text on the same
        # line as the \begin or \end command. The text on the same line as
        # the \label was already handled.
        if re.search(r'\\begin{(equation|eqnarray|align|figure)', line):
            prev_txt = ''
            prev_env = re.search(r'\\begin{(.*?)}', line).group(1)
            inside_env = 1

        elif re.search(r'\\label', line):
            prev_txt = ''

        elif re.search(r'\\end{(equation|eqnarray|align|figure)', line):
            inside_env = 0
            prev_env = ''

        else:
            # If we are inside an environment, then the text displayed with
            # the label is the complete text within the environment,
            # otherwise its just the previous line.
            if inside_env:
                prev_txt += line
            else:
                prev_txt = line

    return outstr.getvalue()
    
# }}}
# getSectionLabels {{{
def getSectionLabels(lineinfo, 
        sectypes=['chapter', 'section', 'subsection', 'subsubsection'], 
        section_prefix='', label_prefix=''):

    if not sectypes:
        return getSectionLabels_Root(lineinfo, section_prefix, label_prefix)

    ##print 'sectypes[0] = %s, section_prefix = [%s], lineinfo = [%s]' % (
    ##        sectypes[0], section_prefix, lineinfo)

    sections = re.split(r'(<.*?>\\%s{.*})' % sectypes[0], lineinfo)
    
    # there will 1+2n sections, the first containing the "preamble" and the
    # others containing the child sections as paris of [section_name,
    # section_text]

    rettext = getSectionLabels(sections[0], sectypes[1:], section_prefix, label_prefix)
    
    for i in range(1,len(sections),2):
        sec_num = (i+1)/2
        section_name = re.search(r'\\%s{(.*?)}' % sectypes[0], sections[i]).group(1)
        section_label_text = getSectionLabels(sections[i] + sections[i+1], sectypes[1:], 
                                    section_prefix+('%d.' % sec_num), label_prefix)

        if section_label_text:
            sec_heading = 2*' '*len(section_prefix) + section_prefix
            sec_heading += '%d. %s' % (sec_num, section_name)
            sec_heading += '<<<%d\n' % (len(section_prefix)/2+1)

            rettext += sec_heading + section_label_text

    return rettext
    
# }}}

# main {{{
def main(fname, label_prefix):
    [head, tail] = os.path.split(fname)
    if head:
        os.chdir(head)

    [root, ext] = os.path.splitext(tail)
    contents = getFileContents(root, ext)
    nonempty = stripComments(contents)
    lineinfo = addFileNameAndNumber(nonempty)

    return getSectionLabels(lineinfo, label_prefix=label_prefix)
# }}}

if __name__ == "__main__":
    if len(sys.argv) > 2:
        prefix = sys.argv[2]
    else:
        prefix = ''

    print main(sys.argv[1], prefix)


# vim: fdm=marker