Attachment 'TocOf.py'
Download 1 # -*- coding: iso-8859-1 -*-
2 """
3 MoinMoin - TableOfContents Macro
4
5 Optional integer argument: maximal depth of listing.
6
7 Modified by Steve Tindle at SQI, Inc. (steve.tindle@sqi-inc.com) on 2006-06-26. When
8 section-numbers are used, numbered lists replaced with actual section numbering
9 (1.2.3, etc). TableOfContents acts normally if section-numbers are disabled.
10
11 Further modifications to have it generate the TOC of a remote page
12
13 @copyright: 2000, 2001, 2002 by Jrgen Hermann <jh@web.de>
14 @license: GNU GPL, see COPYING for details.
15 """
16
17 import re, sha
18 from MoinMoin import config, wikiutil
19 from MoinMoin.Page import Page
20
21 #Dependencies = ["page"]
22 Dependencies = ["time"] # works around MoinMoinBugs/TableOfContentsLacksLinks
23
24 # from macro Include (keep in sync!)
25 _arg_heading = r'(?P<heading>,)\s*(|(?P<hquote>[\'"])(?P<htext>.+?)(?P=hquote))'
26 _arg_level = r',\s*(?P<level>\d*)'
27 _arg_from = r'(,\s*from=(?P<fquote>[\'"])(?P<from>.+?)(?P=fquote))?'
28 _arg_to = r'(,\s*to=(?P<tquote>[\'"])(?P<to>.+?)(?P=tquote))?'
29 _arg_sort = r'(,\s*sort=(?P<sort>(ascending|descending)))?'
30 _arg_items = r'(,\s*items=(?P<items>\d+))?'
31 _arg_skipitems = r'(,\s*skipitems=(?P<skipitems>\d+))?'
32 _arg_titlesonly = r'(,\s*(?P<titlesonly>titlesonly))?'
33 _arg_editlink = r'(,\s*(?P<editlink>editlink))?'
34 _args_re_pattern = r'^(?P<name>[^,]+)(%s(%s)?%s%s%s%s%s%s%s)?$' % (
35 _arg_heading, _arg_level, _arg_from, _arg_to, _arg_sort, _arg_items,
36 _arg_skipitems, _arg_titlesonly, _arg_editlink)
37
38 # from Include, too, but with extra htext group around header text
39 _title_re = r"^(?P<heading>(?P<hmarker>=+)\s(?P<htext>.*)\s(?P=hmarker))$"
40
41 class TableOfContents:
42 """
43 TOC Macro wraps all global variables without disturbing threads
44 """
45
46 def __init__(self, macro, args):
47 self.macro = macro
48
49 self.inc_re = re.compile(r"^\[\[Include\((.*)\)\]\]")
50 self.arg_re = re.compile(_args_re_pattern)
51 self.head_re = re.compile(_title_re) # single lines only
52 self.pre_re = re.compile(r'\{\{\{.+?\}\}\}', re.S)
53
54 self.result = []
55 self.baseindent = 0
56 self.indent = 0
57 self.lineno = 0
58 self.titles = {}
59
60 self.include_macro = None
61
62 self._base_depth = None
63 self._show_section_numbers = None
64 self.request = self.macro.request
65 self._fmt_hd_counters = []
66 self.setsec_re = re.compile(r"^\[\[SetSection\((.*)\)\]\]")
67
68
69 # check numbering, possibly changing the default
70 if self._show_section_numbers is None:
71 #self._show_section_numbers = self.macro.cfg.show_section_numbers
72 numbering = self.request.getPragma('section-numbers', '').lower()
73 if numbering in ['0', 'off']:
74 self._show_section_numbers = 0
75 elif numbering in ['1', 'on']:
76 self._show_section_numbers = 1
77 elif numbering in ['2', '3', '4', '5', '6']:
78 # explicit base level for section number display
79 self._show_section_numbers = int(numbering)
80
81 try:
82 self.mindepth = int(macro.request.getPragma('section-numbers', 1))
83 except (ValueError, TypeError):
84 self.mindepth = 1
85
86 #try:
87 # self.maxdepth = max(int(args), 1)
88 #except (ValueError, TypeError):
89 self.maxdepth = 99
90 self.pages = args.split(',')
91
92 def IncludeMacro(self, *args, **kwargs):
93 if self.include_macro is None:
94 self.include_macro = wikiutil.importPlugin(self.macro.request.cfg,
95 'macro', "Include")
96 return self.pre_re.sub('',apply(self.include_macro, args, kwargs)).split('\n')
97
98 def run(self):
99 for page in self.pages:
100 self.baseindent = 0
101 self.indent = 0
102 self.lineno = 0
103 self.titles = {}
104 lines = self.IncludeMacro(self.macro, page, called_by_toc=1)
105 self.process_lines(lines, page)
106 # Close pending lists
107 for i in range(self.baseindent, self.indent):
108 self.result.append(self.macro.formatter.listitem(0))
109 if self._show_section_numbers:
110 self.result.append(self.macro.formatter.bullet_list(0))
111 else:
112 self.result.append(self.macro.formatter.number_list(0))
113
114 return self.macro.formatter.div(1,attr={'class':"tableOfContents"}) \
115 + ''.join(self.result) + self.macro.formatter.div(0)
116
117 def process_lines(self, lines, pagename):
118 for line in lines:
119 # Filter out the headings
120 self.lineno = self.lineno + 1
121
122 match = self.setsec_re.match(line)
123 if match:
124 # [[SetSection]]
125 section = int(match.group(1)) - 1
126 self._fmt_hd_counters = [section]
127
128 match = self.inc_re.match(line)
129 if match:
130 # this is an [[Include()]] line.
131 # now parse the included page and do the work on it.
132
133 ## get heading and level from Include() line.
134 tmp = self.arg_re.match(match.group(1))
135 if tmp and tmp.group("name"):
136 inc_pagename = tmp.group("name")
137 else:
138 # no pagename? ignore it
139 continue
140 if tmp.group("heading"):
141 if tmp.group("htext"):
142 heading = tmp.group("htext")
143 else:
144 heading = inc_pagename
145 if tmp.group("level"):
146 level = int(tmp.group("level"))
147 else:
148 level = 1
149 inc_page_lines = ["%s %s %s" %("=" * level, heading, "=" * level)]
150 else:
151 inc_page_lines = []
152
153 inc_page_lines = inc_page_lines + self.IncludeMacro(self.macro, match.group(1), called_by_toc=1)
154
155 self.process_lines(inc_page_lines, inc_pagename)
156 else:
157 self.parse_line(line, pagename)
158
159 def parse_line(self, line, pagename):
160 # FIXME this also finds "headlines" in {{{ code sections }}}:
161 match = self.head_re.match(line)
162 if not match: return
163 title_text = match.group('htext').strip()
164 pntt = pagename + title_text
165 self.titles.setdefault(pntt, 0)
166 self.titles[pntt] += 1
167
168 # Get new indent level
169 newindent = len(match.group('hmarker'))
170
171 if not self._base_depth:
172 self._base_depth = newindent
173
174 if newindent > self.maxdepth: return
175 if newindent < self.mindepth:
176 self._fmt_hd_counters = self.request._fmt_hd_counters[:]
177 return
178 if not self.indent:
179 self.baseindent = newindent - 1
180 self.indent = self.baseindent
181
182 number = self.heading(newindent)
183
184 # Close lists
185 for i in range(0,self.indent-newindent):
186 self.result.append(self.macro.formatter.listitem(0))
187 if self._show_section_numbers:
188 self.result.append(self.macro.formatter.bullet_list(0))
189 else:
190 self.result.append(self.macro.formatter.number_list(0))
191
192 # Open Lists
193 for i in range(0,newindent-self.indent):
194 if self._show_section_numbers:
195 self.result.append(self.macro.formatter.bullet_list(1, type="none"))
196 else:
197 self.result.append(self.macro.formatter.number_list(1))
198 self.result.append(self.macro.formatter.listitem(1))
199
200 # Add the heading
201 unique_id = ''
202 if self.titles[pntt] > 1:
203 unique_id = '-%d' % (self.titles[pntt],)
204
205 # close last listitem if same level
206 if self.indent == newindent:
207 self.result.append(self.macro.formatter.listitem(0))
208
209 if self.indent >= newindent:
210 self.result.append(self.macro.formatter.listitem(1))
211
212 linkpage = Page(self.macro.request, pagename, formatter=self.macro.formatter)
213 anchor = "head-" + sha.new(pntt.encode(config.charset)).hexdigest() + unique_id
214 linkOn = linkpage.link_to(self.macro.request, on=1, anchor=anchor)
215 linkOff = linkpage.link_to(self.macro.request, on=0, anchor=anchor)
216
217 self.result.append(linkOn +
218 self.macro.formatter.text(number + title_text) +
219 linkOff)
220
221 # Set new indent level
222 self.indent = newindent
223
224 def heading(self, depth, **kw):
225 # remember depth of first heading, and adapt counting depth accordingly
226 if not self._base_depth:
227 self._base_depth = depth
228
229 count_depth = max(depth - (self._base_depth - 1), 1)
230
231
232 heading_depth = depth # + 1
233
234 # create section number
235 number = ''
236 if self._show_section_numbers:
237 # count headings on all levels
238 self._fmt_hd_counters = self._fmt_hd_counters[:count_depth]
239 while len(self._fmt_hd_counters) < count_depth:
240 self._fmt_hd_counters.append(0)
241 self._fmt_hd_counters[-1] = self._fmt_hd_counters[-1] + 1
242 number = '.'.join(map(str, self._fmt_hd_counters[self._show_section_numbers-1:]))
243 if number: number += ". "
244
245 return number
246
247 def execute(macro, args):
248 toc=TableOfContents(macro,args)
249 return toc.run()
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.