Attachment 'EnhancedTableOfContents.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     @copyright: 2000, 2001, 2002 by Jrgen Hermann <jh@web.de>
  12     @license: GNU GPL, see COPYING for details.
  13 """
  14 
  15 import re, sha
  16 from MoinMoin import config, wikiutil
  17 
  18 #Dependencies = ["page"]
  19 Dependencies = ["time"] # works around MoinMoinBugs/TableOfContentsLacksLinks
  20 
  21 # from macro Include (keep in sync!)
  22 _arg_heading = r'(?P<heading>,)\s*(|(?P<hquote>[\'"])(?P<htext>.+?)(?P=hquote))'
  23 _arg_level = r',\s*(?P<level>\d*)'
  24 _arg_from = r'(,\s*from=(?P<fquote>[\'"])(?P<from>.+?)(?P=fquote))?'
  25 _arg_to = r'(,\s*to=(?P<tquote>[\'"])(?P<to>.+?)(?P=tquote))?'
  26 _arg_sort = r'(,\s*sort=(?P<sort>(ascending|descending)))?'
  27 _arg_items = r'(,\s*items=(?P<items>\d+))?'
  28 _arg_skipitems = r'(,\s*skipitems=(?P<skipitems>\d+))?'
  29 _arg_titlesonly = r'(,\s*(?P<titlesonly>titlesonly))?'
  30 _arg_editlink = r'(,\s*(?P<editlink>editlink))?'
  31 _args_re_pattern = r'^(?P<name>[^,]+)(%s(%s)?%s%s%s%s%s%s%s)?$' % (
  32     _arg_heading, _arg_level, _arg_from, _arg_to, _arg_sort, _arg_items,
  33     _arg_skipitems, _arg_titlesonly, _arg_editlink)
  34 
  35 # from Include, too, but with extra htext group around header text
  36 _title_re = r"^(?P<heading>(?P<hmarker>=+)\s(?P<htext>.*)\s(?P=hmarker))$"
  37 
  38 class TableOfContents:
  39     """
  40     TOC Macro wraps all global variables without disturbing threads
  41     """
  42 
  43     def __init__(self, macro, args):
  44         self.macro = macro
  45         
  46         self.inc_re = re.compile(r"^\[\[Include\((.*)\)\]\]")
  47         self.arg_re = re.compile(_args_re_pattern)
  48         self.head_re = re.compile(_title_re) # single lines only
  49         self.pre_re = re.compile(r'\{\{\{.+?\}\}\}', re.S)
  50         
  51         self.result = []
  52         self.baseindent = 0
  53         self.indent = 0
  54         self.lineno = 0
  55         self.titles = {}
  56 
  57         self.include_macro = None
  58         
  59         self._base_depth = None
  60         self._show_section_numbers = None
  61         self.request = self.macro.request
  62         self._fmt_hd_counters = []
  63         try:
  64             # If SetSection
  65             self._fmt_hd_counters = [self.request._fmt_hd_counters_toc_start]
  66         except:
  67             pass
  68         
  69 
  70         # check numbering, possibly changing the default
  71         if self._show_section_numbers is None:
  72             #self._show_section_numbers = self.macro.cfg.show_section_numbers
  73             numbering = self.request.getPragma('section-numbers', '').lower()
  74             if numbering in ['0', 'off']:
  75                 self._show_section_numbers = 0
  76             elif numbering in ['1', 'on']:
  77                 self._show_section_numbers = 1
  78             elif numbering in ['2', '3', '4', '5', '6']:
  79                 # explicit base level for section number display
  80                 self._show_section_numbers = int(numbering)
  81                 
  82         try:
  83             self.mindepth = int(macro.request.getPragma('section-numbers', 1))
  84         except (ValueError, TypeError):
  85             self.mindepth = 1
  86 
  87         try:
  88             self.maxdepth = max(int(args), 1)
  89         except (ValueError, TypeError):
  90             self.maxdepth = 99
  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         self.process_lines(self.pre_re.sub('',self.macro.parser.raw).split('\n'),
 100                            self.macro.formatter.page.page_name)
 101         # Close pending lists
 102         for i in range(self.baseindent, self.indent):
 103             self.result.append(self.macro.formatter.listitem(0))
 104             if self._show_section_numbers:
 105                 self.result.append(self.macro.formatter.bullet_list(0))
 106             else:
 107                 self.result.append(self.macro.formatter.number_list(0))
 108         
 109         return self.macro.formatter.div(1,attr={'class':"tableOfContents"}) \
 110                + ''.join(self.result) + self.macro.formatter.div(0)
 111 
 112     def process_lines(self, lines, pagename):
 113         for line in lines:
 114             # Filter out the headings
 115             self.lineno = self.lineno + 1
 116             match = self.inc_re.match(line)
 117             if match:
 118                 # this is an [[Include()]] line.
 119                 # now parse the included page and do the work on it.
 120 
 121                 ## get heading and level from Include() line.
 122                 tmp = self.arg_re.match(match.group(1))
 123                 if tmp and tmp.group("name"):
 124                     inc_pagename = tmp.group("name")
 125                 else:
 126                     # no pagename?  ignore it
 127                     continue
 128                 if tmp.group("heading"):
 129                     if tmp.group("htext"):
 130                         heading = tmp.group("htext")
 131                     else:
 132                         heading = inc_pagename
 133                     if tmp.group("level"):
 134                         level = int(tmp.group("level"))
 135                     else:
 136                         level = 1
 137                     inc_page_lines = ["%s %s %s" %("=" * level, heading, "=" * level)]
 138                 else:
 139                     inc_page_lines = []
 140 
 141                 inc_page_lines = inc_page_lines + self.IncludeMacro(self.macro, match.group(1), called_by_toc=1)
 142                 
 143                 self.process_lines(inc_page_lines, inc_pagename)
 144             else:
 145                 self.parse_line(line, pagename)
 146 
 147     def parse_line(self, line, pagename):
 148         # FIXME this also finds "headlines" in {{{ code sections }}}:
 149         match = self.head_re.match(line)
 150         if not match: return
 151         title_text = match.group('htext').strip()
 152         pntt = pagename + title_text
 153         self.titles.setdefault(pntt, 0)
 154         self.titles[pntt] += 1
 155 
 156         # Get new indent level
 157         newindent = len(match.group('hmarker'))
 158         
 159         if not self._base_depth:
 160             self._base_depth = newindent
 161         
 162         if newindent > self.maxdepth: return
 163         if newindent < self.mindepth: 
 164             self._fmt_hd_counters = self.request._fmt_hd_counters[:]
 165             return
 166         if not self.indent:
 167             self.baseindent = newindent - 1
 168             self.indent = self.baseindent
 169         
 170         number = self.heading(newindent)
 171 
 172         # Close lists
 173         for i in range(0,self.indent-newindent):
 174             self.result.append(self.macro.formatter.listitem(0))
 175             if self._show_section_numbers:
 176                 self.result.append(self.macro.formatter.bullet_list(0))
 177             else:
 178                 self.result.append(self.macro.formatter.number_list(0))
 179 
 180         # Open Lists
 181         for i in range(0,newindent-self.indent):
 182             if self._show_section_numbers:
 183                 self.result.append(self.macro.formatter.bullet_list(1, type="none"))
 184             else:
 185                 self.result.append(self.macro.formatter.number_list(1))
 186             self.result.append(self.macro.formatter.listitem(1))
 187 
 188         # Add the heading
 189         unique_id = ''
 190         if self.titles[pntt] > 1:
 191             unique_id = '-%d' % (self.titles[pntt],)
 192 
 193         # close last listitem if same level
 194         if self.indent == newindent:
 195             self.result.append(self.macro.formatter.listitem(0))
 196             
 197         if self.indent >= newindent:
 198             self.result.append(self.macro.formatter.listitem(1))
 199             
 200         self.result.append(self.macro.formatter.anchorlink(1,
 201             "head-" + sha.new(pntt.encode(config.charset)).hexdigest() + unique_id) +
 202                            self.macro.formatter.text(number + title_text) +
 203                            self.macro.formatter.anchorlink(0))
 204 
 205         # Set new indent level
 206         self.indent = newindent
 207         
 208     def heading(self, depth, **kw):
 209         # remember depth of first heading, and adapt counting depth accordingly
 210         if not self._base_depth:
 211             self._base_depth = depth
 212 
 213         count_depth = max(depth - (self._base_depth - 1), 1)
 214 
 215 
 216         heading_depth = depth # + 1
 217 
 218         # create section number
 219         number = ''
 220         if self._show_section_numbers:
 221             # count headings on all levels
 222             self._fmt_hd_counters = self._fmt_hd_counters[:count_depth]
 223             while len(self._fmt_hd_counters) < count_depth:
 224                 self._fmt_hd_counters.append(0)
 225             self._fmt_hd_counters[-1] = self._fmt_hd_counters[-1] + 1
 226             number = '.'.join(map(str, self._fmt_hd_counters[self._show_section_numbers-1:]))
 227             if number: number += ". "
 228 
 229         return number
 230 
 231 def execute(macro, args):
 232     toc=TableOfContents(macro,args)
 233     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:55, 8.8 KB) [[attachment:EnhancedTableOfContents.py]]
 All files | Selected Files: delete move to page copy to page

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