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.
  • [get | view] (2006-06-28 20:14:16, 9.5 KB) [[attachment:TocOf.py]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.