1 2# Copyright Aleksey Gurtovoy 2004-2009 3# 4# Distributed under the Boost Software License, Version 1.0. 5# (See accompanying file LICENSE_1_0.txt or copy at 6# http://www.boost.org/LICENSE_1_0.txt) 7 8from docutils.writers import html4_frames 9from docutils.writers import html4css1 10from docutils import nodes 11 12import re 13import string 14 15 16class Writer(html4_frames.Writer): 17 18 def __init__(self): 19 self.__super = html4_frames.Writer 20 self.__super.__init__(self) 21 self.translator = refdoc_translator 22 23 24class refdoc_translator(html4_frames.frame_pages_translator): 25 26 tocframe_width = 25 27 re_include = re.compile(r'(\s*#include\s+<)(.*?\.hpp)?(>\s*)?') 28 re_identifier = re.compile(r'(.*?\W*)(\w+)(\W.*?)?') 29 re_modtime = re.compile(r'\s*modtime:\s*(.*)') 30 re_auto_id = re.compile(r'^id(\d+)$') 31 in_literal_block = 0 32 in_reference = 0 33 34 def __init__(self, document, index_page, page_files_dir, extension): 35 self.docframe += ' refmanual' 36 self.__super = html4_frames.frame_pages_translator 37 self.__super.__init__(self, document, index_page, page_files_dir, extension) 38 39 40 def visit_section( self, node ): 41 base = self 42 self = self.active_visitor() 43 if self.section_level == 1: 44 self.section_level = 2 45 base.__super.visit_section( base, node ) 46 47 def depart_section( self, node ): 48 self.__super.depart_section( self, node ) 49 self = self.active_visitor() 50 if self.section_level == 2: 51 self.section_level = 1 52 53 54 def visit_title( self, node ): 55 self.__super.visit_title( self, node ) 56 if self.re_auto_id.match( self._node_id( node.parent ) ): 57 name = nodes.make_id( node.astext() ) 58 self = self.active_visitor() 59 self.body.append( self.starttag( 60 {}, 'a', '', name=name, href='#%s' % name, CLASS='subsection-title' 61 ) ) 62 63 def depart_title( self, node ): 64 base = self 65 if self.re_auto_id.match( self._node_id( node.parent ) ): 66 self = self.active_visitor() 67 self.body.append( '</a>') 68 69 base.__super.depart_title( base, node ) 70 71 72 def visit_table(self, node): 73 self = self.active_visitor() 74 self.body.append( 75 self.starttag(node, 'table', CLASS='docutils table', border="1")) 76 77 78 def visit_reference(self, node): 79 self.in_reference = 1 80 if len(node) == 1 and isinstance(node[0], nodes.literal) and node[0].has_key('class'): 81 if node.has_key('class') and node['class'].find(node[0]['class']) == -1: 82 node['class'] += ' %s' % node[0]['class'] 83 else: 84 node['class'] = node[0]['class'] 85 86 self.__super.visit_reference(self, node) 87 88 89 def depart_reference(self, node): 90 self.__super.depart_reference(self, node) 91 self.in_reference = 0 92 93 94 def visit_literal(self, node): 95 if self.in_reference: 96 self.__super.visit_literal(self, node) 97 98 base = self 99 self = self.active_visitor() 100 101 self.body.append(self.starttag(node, 'tt', '', CLASS='literal')) 102 text = node.astext() 103 104 if base.re_include.search(text): 105 text = base.re_include.sub(lambda m: base._handle_include_sub(self, m), text) 106 self.body.append('<span class="pre">%s</span>' % text) 107 else: 108 for token in self.words_and_spaces.findall(text): 109 if token.strip(): 110 if base.re_identifier.search(token): 111 token = base.re_identifier.sub(lambda m: base._handle_id_sub(self, m), token) 112 else: 113 token = self.encode(token) 114 115 self.body.append('<span class="pre">%s</span>' % token) 116 elif token in ('\n', ' '): 117 # Allow breaks at whitespace: 118 self.body.append(token) 119 else: 120 # Protect runs of multiple spaces; the last space can wrap: 121 self.body.append(' ' * (len(token) - 1) + ' ') 122 123 self.body.append('</tt>') 124 # Content already processed: 125 raise nodes.SkipNode 126 127 128 def visit_literal_block(self, node): 129 self.__super.visit_literal_block(self, node) 130 self.in_literal_block = True 131 132 def depart_literal_block(self, node): 133 self.__super.depart_literal_block(self, node) 134 self.in_literal_block = False 135 136 137 def visit_license_and_copyright(self, node): 138 self = self.active_visitor() 139 self.context.append( len( self.body ) ) 140 141 def depart_license_and_copyright(self, node): 142 self = self.active_visitor() 143 start = self.context.pop() 144 self.footer = self.body[start:] 145 del self.body[start:] 146 147 148 def visit_Text(self, node): 149 if not self.in_literal_block: 150 self.__super.visit_Text(self, node) 151 else: 152 base = self 153 self = self.active_visitor() 154 155 text = node.astext() 156 if base.re_include.search(text): 157 text = base.re_include.sub(lambda m: base._handle_include_sub(self, m), text) 158 elif base.re_identifier.search(text): 159 text = base.re_identifier.sub(lambda m: base._handle_id_sub(self, m), text) 160 else: 161 text = self.encode(text) 162 163 self.body.append(text) 164 165 166 def depart_Text(self, node): 167 pass 168 169 170 def visit_substitution_reference(self, node): 171 # debug help 172 print 'Unresolved substitution_reference:', node.astext() 173 raise nodes.SkipNode 174 175 176 def _footer_content(self): 177 self = self.active_visitor() 178 parts = ''.join( self.footer ).split( '\n' ) 179 parts = [ '<div class="copyright">%s</div>' % x if x.startswith( 'Copyright' ) else x for x in parts ] 180 return '<td><div class="copyright-footer">%s</div></td>' % '\n'.join( parts ) if len( parts ) else '' 181 182 183 def _toc_as_text( self, visitor ): 184 footer_end = visitor.body.pop() 185 visitor.body.append( self._footer_content() ) 186 visitor.body.append( footer_end ) 187 return visitor.astext() 188 189 190 def _handle_include_sub(base, self, match): 191 if not match.group(2) or not match.group(): 192 return self.encode(match.group(0)) 193 194 header = match.group(2) 195 result = self.encode(match.group(1)) 196 result += '<a href="%s" class="header">%s</a>' \ 197 % ( '../../../../%s' % header 198 , self.encode(header) 199 ) 200 201 result += self.encode(match.group(3)) 202 return result 203 204 205 def _handle_id_sub(base, self, match): 206 identifier = match.group(2) 207 208 if not base.document.has_name( identifier.lower() ): 209 return self.encode(match.group(0)) 210 211 def get_section_id( id ): 212 node = base.document.ids[ id ] 213 if isinstance( node, nodes.section ): 214 return id 215 216 if isinstance( node, nodes.target ): 217 return get_section_id( node.get( 'refid' ) ) 218 219 return None 220 221 id = get_section_id( base.document.nameids[ identifier.lower() ] ) 222 if not id: 223 return self.encode(match.group(0)) 224 225 if id == 'inserter': 226 id = 'inserter-class' 227 228 result = self.encode(match.group(1)) 229 result += '<a href="%s" class="identifier">%s</a>' \ 230 % ( base._chunk_ref( base._active_chunk_id(), base._make_chunk_id( id ) ) 231 , self.encode(identifier) 232 ) 233 234 if match.group(3): 235 result += self.encode(match.group(3)) 236 237 return result 238 239 240 def _make_chunk_id( self, node_id ): 241 if self.re_auto_id.match( node_id ): 242 node = self.document.ids[ node_id ] 243 return '%s-%s' % ( self._node_id( node.parent ), node['dupnames'][0] ) 244 245 if node_id.startswith( 'boost-mpl-' ): 246 return node_id[ len( 'boost-mpl-' ): ] 247 248 return node_id 249 250