rst2.py :  » Development » Leo » Leo-4.7.1-final » leo » plugins » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Development » Leo 
Leo » Leo 4.7.1 final » leo » plugins » rst2.py
#@+leo-ver=4-thin
#@+node:ekr.20040331071319:@thin rst2.py
#@<< docstring >>
#@+node:ekr.20050420105800:<< docstring >>
"""A plugin to generate HTML or LaTeX files from reStructured Text
embedded in Leo outlines.

If a headline starts with @rst <filename>, double-clicking on it will 
write a file in outline order, with the headlines converted to reStructuredText 
section headings.

If the name of the <filename> has the extension .html, .htm or .tex, and if you have
docutils installed, it will generate HTML or LaTeX, respectively.

The following settings are presently available: They can be set in the rst2 code
itself, or (maybe?) using @settings trees:
    
rst2_bodyfilter = False
# True: call body_filter to massage text.
# Removes @ignore, @nocolor, @wrap directives.

rst2file = False
# Controls whether to use external file or string.

rst2_pure_document = False
# Controls the following code in writeTreeAsRst
if config.rst2_pure_document or g.match_word(h,0,"@rst"):
    < < handle an rst node > >
    # Removes @ignore, @nocolor, @wrap directives.
    # Removes code blocks if rst2_replace_code_blocks is True.
    # Formats headlines only if rst2_format_headlines is True.
else:
    < < handle a plain node > >
    # Deletes @others from output.
    # Formats headlines only if rst2_format_headlines is False. (opposite of meaning in rst nodes!)
        
rst2_format_headlines = False
# Used differently.  See rst2_pure_document.
    
rst2_http_server_support = True
# True:
# 1. add_node_marker writes a string using generate_node_marker.
# Generates 'http-node-marker-'+str(number), where number is config.node_counter (incremented each time add_node_marker is called.
# 2. Enables the following code in writeTreeAsRst
    if config.tag == 'open2':
        http_map = config.http_map
    else:
        http_map = {}
        config.anchormap = {}
       # maps v nodes to markers.
        config.node_counter = 0
    # [snip] code to write the tree
    if config.rst2_http_server_support:
        config.http_map = http_map
# 3. http_support_main does nothing unless this option is True.
# False: add_node_marker does nothing.

rst2_clear_attributes = False
    # Deletes p.v.rst2_http_attributename from all nodes after writing.
    # Deletes p.v.unknownAttributes if it then becomes empty.
    
rst2_run_on_window_open = False
# Runs an alternative 'main line' in onFileOpen when a new window is opened.

rst2_replace_code_blocks = True
# See notes for rst2_pure_document. (Uses do_replace_code_blocks)
< < define alternate code block implementation> >
# Don't know what to do here: Can someone make a suggestion?
#import docutils.parsers.rst.directives.admonitions
#import docutils.parsers.rst.directives.body
# docutils.parsers.rst.directives._directives['code-block'] = docutils.parsers.rst.directives.body.block 
# docutils.parsers.rst.directives.register_directive('code-block', docutils.parsers.rst.directives.admonitions.admonition)
#docutils.parsers.rst.directives._directives['code-block'] = docutils.parsers.rst.directives.body.pull_quote 
# g.es("Registered some alternate implementation for code-block directive")
config.do_replace_code_blocks = config.rst2_replace_code_blocks
        
rst2_install_menu_item_in_edit_menu = True
# True: install 'Transform rst text in subtree'command to Edit menu.

rst2_node_begin_marker = 'http-node-marker-'
rst2_http_attributename = 'rst_http_attribute'

"""
#@nonl
#@-node:ekr.20050420105800:<< docstring >>
#@nl

# By Josef Dalcolmo: contributed under the same licensed as Leo.py itself.

#@<< imports >>
#@+node:ekr.20050420105800.1:<< imports >>

import leoGlobals as g
import leoPlugins

import os
import ConfigParser
from HTMLParser import HTMLParser
from pprint import pprint
try:
    import sys
    dir = os.path.split(__file__)[0]
    if dir not in sys.path:
        sys.path.append(dir)
    import mod_http
    from mod_http import set_http_attribute,get_http_attribute,reconstruct_html_from_attrs
except:
    mod_http = None
#@nonl
#@-node:ekr.20050420105800.1:<< imports >>
#@nl
#@<< change log >>
#@+node:ekr.20040331071319.2:<< change log >>
#@+at 
#@nonl
# Change log:
# 
# - JD 2003-03-10 (rev 1.3): some more corrections to the unicode-> encoding 
# translation.
#   No only check for missing docutils (doesn't mask other errors any more).
# 
# - JD 2003-03-11 (rev 1.4): separated out the file launching code to a 
# different pluging.
# 
# - 2003-11-02 Added generation of LaTeX files, just make the extension of the 
# filename '.tex'. --Timo Honkasalo
# 
# - 2003-12-24 S Zatz modifications to introduce concept of plain @rst nodes 
# to improve program documentation
# 
# - 2004-04-08 EKR:
#     - Eliminated "comment" text at start of nodes.
#     - Rewrote code that strips @nocolor, @ignore and @wrap directives from 
# start of text.
#     - Changed code to show explicitly that it uses positions.
#     - Added comments to << define code-block>>
#     - Added div.code-block style to silver_city.css (see documentation)
#     - Rewrote documentation.
# 2.2 EKR 2005-03-07 changed publish() to publish(argv=[''])
# 
# 2.3 bwmulder
#     - added various options which can be set with @settings directive
#     - add support for mod_http plugin.
#     - added possible settings to the beginning of the Leo file
# 2.4 EKR:
#     - Call onFileOpen from both "new" and "open2" hooks.
#@-at
#@nonl
#@-node:ekr.20040331071319.2:<< change log >>
#@nl

#@+others
#@+node:bwmulder.20050319123911:http plugin interface
#@+doc
# if enabled, this plugin stores information for the http plugin.
# 
# Each node can have one additional attribute, with the name 
# config.rst_http_attributename, which is a list.
# 
# The first three elements are stack of tags, the rest is html code.
# 
# [<tag n start>, <tag n end>, <other stack elements>, <html line 1>, <html 
# line 2>, ...]
# 
# <other stack elements has the same structure:
#     [<tag n-1 start>, <tag n-1 end>, <other stack elements>]
#@-doc
#@nonl
#@-node:bwmulder.20050319123911:http plugin interface
#@+node:bwmulder.20050314132625:config
class config:
    # 
    rst2_bodyfilter = False
    rst2file = False
    rst2_pure_document = False
    rst2_http_server_support = True
    rst2_format_headlines = False
    # number_headlines = False
    # warnofdrags = False
    rst2_clear_attributes = False
    rst2_run_on_window_open = False
    rst2_replace_code_blocks = True
    
    # debug flags: ignore in normal use
    rst2_debug_handle_starttag = False
    rst2_debug_handle_endtag = False
    rst2_debug_store_lines = False
    rst2_debug_show_unknownattributes = False
    rst2_debug_node_html_1 = False
    rst2_debug_anchors = False
    rst2_debug_before_and_after_replacement = False
    rst2_install_menu_item_in_edit_menu = True
    
    # These are really configuration options.
    rst2_node_begin_marker = 'http-node-marker-'
    rst2_http_attributename = 'rst_http_attribute'
    
    firstCall = True
    # set to False after the optional menu is installed.
    
    # Some data is also stored in the config class
    http_map = None
    # Maps node anchors to node.
    # A node anchor is a marker beginning with
    # rst2_node_begin_marker.
    # It is currently assumed that such a marker
    # does not occur in the rst document.
    
    tag = None
    # either doubleclick or open2.
    
    current_file = None
    
    node_counter = 0
    # Used to mark the beginning of the html code for each new
    # node.
    
    do_replace_code_blocks = False
#@nonl
#@-node:bwmulder.20050314132625:config
#@+node:bwmulder.20050327204614:transform_rst2_text_in_subtree
def transform_rst2_text_in_subtree(c):
    
    class callOnFileOpen(object):
        """
        Call onFileOpen passing the commander c.
        """
        def __init__(self):
            self.c = c
        def __call__(self):
            onFileOpen("menuItem", {"new_c": self.c})
            
    editMenu = c.frame.menu.getMenu('Edit')
        
    newEntries = (
            ("-", None, None),
            ("Transform rst2 text in subtree", "", callOnFileOpen))
        
    c.frame.menu.createMenuEntries(editMenu, newEntries)
#@-node:bwmulder.20050327204614:transform_rst2_text_in_subtree
#@+node:bwmulder.20050320000243:onFileOpen
def onFileOpen(tag, keywords):
    # c,old_c,new_c,fileName):
    c = keywords["new_c"]
    applyConfiguration(c)
    if config.firstCall and config.rst2_install_menu_item_in_edit_menu:
        config.firstCall = False
        transform_rst2_text_in_subtree(c)
    ignoreset = {}
    if c.config.getBool("rst2_run_on_open_window"):
    # if config.rst2_run_on_window_open:
        http_map = {}
        anchormap = {}
        config.node_counter = 0
        found_rst_trees = False
        root = c.currentVnode()
        
        if tag == 'open2':
            iterator = c.allNodes_iter(root)
        else:
            iterator = root.self_and_subtree_iter()
        for p in iterator:
            if p.v not in ignoreset:
                s = p.v.headString()
                if s.startswith("@rst ") and len(s.split()) >= 2:
                    for p1 in p.subtree_iter():
                        ignoreset[p1.v] = True
                    try:
                        config.http_map = {}
                        config.anchormap = {}
                        onIconDoubleClick("open2", {"c": c, "p": p})
                        http_map.update(config.http_map)
                        anchormap.update(config.anchormap)
                        found_rst_trees = True
                    except SystemExit, s:
                        g.es("Formatting failed for %s" % p, color='red')
        if found_rst_trees:
            config.http_map = http_map
            config.anchormap = anchormap
            relocate_references() 
          
            config.http_map = None
            config.anchormap = None
            g.es('html updated for html plugin', color="blue")
        if config.rst2_clear_attributes:
            g.es("http attributes cleared")
#@-node:bwmulder.20050320000243:onFileOpen
#@+node:ekr.20040331071319.3:onIconDoubleClick
# by Josef Dalcolmo 2003-01-13
#
# this does not check for proper filename syntax.
# path is the current dir, or the place @folder points to
# this should probably be changed to @path or so.

def onIconDoubleClick(tag,keywords):

    c = keywords.get("c")
    p = keywords.get("p")
    # g.trace(c)

    if not c or not p:
        return
    
    applyConfiguration(c)
    config.tag = tag

    h = p.headString().strip()

    if g.match_word(h,0,"@rst"):
        if len(h) > 5:
            fname = h[5:]
            ext = os.path.splitext(fname)[1].lower()
            if ext in ('.htm','.html','.tex'):
                #@                << write rST as HTML/LaTeX >>
                #@+node:ekr.20040331071319.4:<< write rST as HTML/LaTeX >>
                try:
                    import docutils
                except:
                    g.es('HTML/LaTeX generation requires docutils')
                    return
                else:
                    import docutils.parsers.rst
                    from docutils.core import Publisher
                    from docutils.io import StringOutput,StringInput,FileOutput,FileInput
                    import StringIO
                    
                # Set the writer and encoding for the converted file
                if ext in ('.html','.htm'):
                    writer='html' ; enc="utf-8"
                else:
                    writer='latex' ; enc="iso-8859-1"
                
                syntax = False
                if writer == 'html':
                    try:
                        import SilverCity
                        #@        << define code-block >>
                        #@+node:ekr.20040331071319.5:<< define code-block >>
                        def code_block(name,arguments,options,content,lineno,content_offset,block_text,state,state_machine):
                            
                            """Create a code-block directive for docutils."""
                            
                            # See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/252170
                            language = arguments[0]
                            module = getattr(SilverCity,language)
                            generator = getattr(module,language+"HTMLGenerator")
                            io = StringIO.StringIO()
                            generator().generate_html(io, '\n'.join(content))
                            html = '<div class="code-block">\n%s\n</div>\n'%io.getvalue()
                            raw = docutils.nodes.raw('',html, format='html') #(self, rawsource='', text='', *children, **attributes):
                            return [raw]
                            
                        # These are documented at http://docutils.sourceforge.net/spec/howto/rst-directives.html.
                        code_block.arguments = (
                            1, # Number of required arguments.
                            0, # Number of optional arguments.
                            0) # True if final argument may contain whitespace.
                        
                        # A mapping from option name to conversion function.
                        code_block.options = {
                            'language' :
                            docutils.parsers.rst.directives.unchanged # Return the text argument, unchanged
                        }
                        
                        code_block.content = 1 # True if content is allowed.
                         
                        # Register the directive with docutils.
                        docutils.parsers.rst.directives.register_directive('code-block',code_block)
                        
                        config.do_replace_code_blocks = False
                        #@nonl
                        #@-node:ekr.20040331071319.5:<< define code-block >>
                        #@nl
                        syntax = True
                    except ImportError:
                        g.es('SilverCity not present so no syntax highlighting')
                        #@        << define alternate code block implementation>>
                        #@+node:bwmulder.20050326114320: << define alternate code block implementation>>
                        # Don't know what to do here: Can someone make a suggestion?
                        #import docutils.parsers.rst.directives.admonitions
                        #import docutils.parsers.rst.directives.body
                        # docutils.parsers.rst.directives._directives['code-block'] = docutils.parsers.rst.directives.body.block 
                        # docutils.parsers.rst.directives.register_directive('code-block', docutils.parsers.rst.directives.admonitions.admonition)
                        #docutils.parsers.rst.directives._directives['code-block'] = docutils.parsers.rst.directives.body.pull_quote 
                        # g.es("Registered some alternate implementation for code-block directive")
                        config.do_replace_code_blocks = config.rst2_replace_code_blocks
                        #@-node:bwmulder.20050326114320: << define alternate code block implementation>>
                        #@nl
                
                if config.rst2file:
                    rstFileName = os.path.splitext(fname)[0] + ".txt"
                    rstFile = file(rstFileName, "w")
                    g.es("Using %s as rst file" % rstFileName)
                else:
                    rstFile = StringIO.StringIO()
                config.current_file = fname
                writeTreeAsRst(rstFile,fname,p,c,syntax=syntax)
                if config.rst2file:
                    rstFile.close()
                else:
                    rstText = rstFile.getvalue()
                
                # This code snipped has been taken from code contributed by Paul Paterson 2002-12-05.
                pub = Publisher()
                if config.rst2file:
                    pub.source = FileInput(source_path=rstFileName)
                    pub.destination = FileOutput(destination_path=fname, encoding='unicode')
                else:
                    pub.source = StringInput(source=rstText)
                    pub.destination = StringOutput(pub.settings, encoding=enc)
                pub.set_reader('standalone', None, 'restructuredtext')
                pub.set_writer(writer)
                output = pub.publish(argv=[''])
                
                if config.rst2file:
                    pass
                else:
                    convertedFile = file(fname,'w')
                    convertedFile.write(output)
                    convertedFile.close()
                rstFile.close()
                writeFullFileName(fname)
                return http_support_main(tag, fname)
                
                #@-node:ekr.20040331071319.4:<< write rST as HTML/LaTeX >>
                #@nl
            else:
                #@                << write rST file >>
                #@+node:ekr.20040331071319.6:<< write rST file >>
                rstFile = file(fname,'w')
                writeTreeAsRst(rstFile,fname,p,c)
                rstFile.close()
                writeFullFileName(fname)
                #@nonl
                #@-node:ekr.20040331071319.6:<< write rST file >>
                #@nl
        else:
            # if the headline only contains @rst then open the node and its parent in text editor
            # this works for me but needs to be generalized and should probably be a component
            # of the open_with plugin.
            if 0:
                c.openWith(("os.startfile", None, ".txt"))
                c.selectVnode(p.parent())
                c.openWith(("os.startfile", None, ".tp"))
#@nonl
#@-node:ekr.20040331071319.3:onIconDoubleClick
#@+node:ekr.20040811064922:writeFullFileName
def writeFullFileName (fname):
    
    path = g.os_path_join(os.getcwd(),fname)
    path = g.os_path_abspath(path)
    
    g.es('rst written: ' + path,color="blue")
#@nonl
#@-node:ekr.20040811064922:writeFullFileName
#@+node:bwmulder.20050326125712:replace_code_block_directive
def replace_code_block_directive(line):
    if u"code-block::" in line:
        parts = line.split()
        if len(parts) == 3 and (parts[0] == '..') and (parts[1]=='code-block::'):
            line = '%s code::\n' % parts[2]
    return line
#@nonl
#@-node:bwmulder.20050326125712:replace_code_block_directive
#@+node:ekr.20040331071319.7:writeTreeAsRst & add_node_marker
def writeTreeAsRst(rstFile,fname,p,c,syntax=False):
    
    'Write the tree under position p to the file rstFile (fname is the filename)'
    
    def add_node_marker():
        if config.rst2_http_server_support:
            config.node_counter += 1
            marker = rst_htmlparser.generate_node_marker(config.node_counter)
            config.last_marker = marker
            http_map[marker] = p.copy()
            # the p.copy is necessary, since otherwise the
            # position is modified and unusable later.
            rstFile.write("\n\n.. _%s:\n\n" % marker)
            
    # we don't write a title, so the titlepage can be customized
    # use '#' for title under/overline
    directives = g.scanDirectives(c,p=p) # changed name because don't want to use keyword dict
    #@    << set encoding >>
    #@+node:ekr.20040408160625.1:<< set encoding >>
    encoding = directives.get("encoding",None)
    if encoding == None:
        encoding = c.config.default_derived_file_encoding
    #@nonl
    #@-node:ekr.20040408160625.1:<< set encoding >>
    #@nl
    #@    << set code_dir >>
    #@+node:ekr.20040403202850:<< set code_dir >>
    if syntax:
        lang_dict = {'python':'Python', 'ruby':'Ruby', 'perl':'Perl', 'c':'CPP'}
        language = directives['language']
        # SilverCity modules have first letter in caps
        if language in lang_dict:
            code_dir = '**code**:\n\n.. code-block:: %s\n\n' % lang_dict[language]
        else:
            code_dir = '**code**:\n\n.. class:: code\n..\n\n::\n\n'
    else:
        code_dir = '**code**:\n\n.. class:: code\n..\n\n::\n\n'
    #@nonl
    #@-node:ekr.20040403202850:<< set code_dir >>
    #@nl
    s = g.toEncodedString(fname,encoding,reportErrors=True)
    rstFile.write('.. filename: '+s+'\n')
    rstFile.write('\n')

    if config.rst2_bodyfilter:
        s = bodyfilter(p)
    else:
        s = p.bodyString()
    s = g.toEncodedString(s,encoding,reportErrors=True)
    rstFile.write(s+'\n')    # write body of titlepage.
    rstFile.write('\n')
    
    toplevel = p.level()+1 # Dec 20
    h = p.headString()
    
    if config.rst2_http_server_support:
        if config.tag == 'open2':
            http_map = config.http_map
        else:
            http_map = {}
            config.anchormap = {}
           # maps v nodes to markers.
            config.node_counter = 0
    for p in p.subtree_iter():
        h = p.headString().strip()
        if config.rst2_pure_document or g.match_word(h,0,"@rst"):
            #@            << handle an rst node >>
            #@+node:ekr.20040403202850.1:<< handle an rst node >>
            s = p.bodyString()
            s = g.toEncodedString(s,encoding,reportErrors=True)
            
            # Skip any leading @ignore, @nocolor, @wrap directives.
            while (
                g.match_word(s,0,"@ignore") or
                g.match_word(s,0,"@nocolor") or
                g.match_word(s,0,"@wrap")
            ):
                i = g.skip_line(s,0)
                s = s[i:]
            
            add_node_marker()    
            
            if config.rst2_format_headlines:
                rstFile.write(h+'\n')
                rstFile.write(underline(h,p.level()-toplevel))
                rstFile.write('\n')
            
            if config.do_replace_code_blocks and 'code-block::' in s:
                s = '\n'.join([replace_code_block_directive(line) for line in s.split("\n")])
            
            rstFile.write('%s\n\n'%s.strip())
            #@nonl
            #@-node:ekr.20040403202850.1:<< handle an rst node >>
            #@nl
        else:
            #@            << handle a plain node >>
            #@+node:ekr.20040403202850.2:<< handle a plain node >>
            if g.match_word(h,0,"@file-nosent"):
                h = h[13:]
            h = g.toEncodedString(h,encoding,reportErrors=True)
            if config.rst2_bodyfilter:
                s = bodyfilter(p)
            else:
                s = p.bodyString()
            s = g.toEncodedString(s,encoding,reportErrors=True)
            
            add_node_marker()    
            
            if config.rst2_format_headlines:
                pass
            else:
                rstFile.write(h+'\n')
                rstFile.write(underline(h,p.level()-toplevel))
                rstFile.write('\n')
            
            if s.strip():
                rstFile.write(code_dir)
                s = s.split('\n')
                for linenum,linetext in enumerate(s[:-1]):
                    if "@others" in linetext: #deleting lines with @other directive from output
                        continue
                    rstFile.write('\t%2d  %s\n'%(linenum+1,linetext))
            
            rstFile.write('\n')
            #@nonl
            #@-node:ekr.20040403202850.2:<< handle a plain node >>
            #@nl
        #@        << clear attributes >>
        #@+node:bwmulder.20050315150045:<< clear attributes >>
        if config.rst2_clear_attributes:
            if hasattr(p.v, 'unknownAttributes'):
                if p.v.unknownAttributes.has_key(config.rst2_http_attributename):
                    del p.v.unknownAttributes[config.rst2_http_attributename]
                    if p.v.unknownAttributes == {}:
                        del p.v.unknownAttributes
        #@nonl
        #@-node:bwmulder.20050315150045:<< clear attributes >>
        #@nl
    add_node_marker()
    if config.rst2_http_server_support:
        config.http_map = http_map
#@-node:ekr.20040331071319.7:writeTreeAsRst & add_node_marker
#@+node:bwmulder.20050314131619:applyConfiguration
def applyConfiguration (c):

    """Called when the user presses the "Apply" button on the Properties form.
 
    Default: behave like the previous plugin.
    """

    def getboolean(name):
        value = getattr(config, name)
        newvalue = c.config.getBool(name)
        if newvalue is not None:
            setattr(config, name, newvalue)
        
    getboolean("rst2file")
    getboolean("rst2_bodyfilter")
    getboolean("rst2_clear_attributes")
    getboolean("rst2_http_server_support")
    if config.rst2_http_server_support and not mod_http:
        g.es("Resetting rst2_http_server_support because mod_http plugin was not imported successfully", color='red')
        config.rst2_http_server_support = False
    getboolean("rst2_pure_document")
    getboolean("rst2_format_headlines")
    # getboolean("rst2_warnofdrags")
    getboolean("rst2_run_on_window_open")
    
    getboolean("rst2_debug_handle_endtag")
    getboolean("rst2_debug_store_lines")
    getboolean("rst2_debug_handle_starttag")
    getboolean("rst2_debug_show_unknownattributes")
    getboolean("rst2_debug_node_html_1")
    getboolean("rst2_debug_anchors")
    getboolean("rst2_debug_before_and_after_replacement")
    getboolean("rst2_install_menu_item_in_edit_menu")

#@-node:bwmulder.20050314131619:applyConfiguration
#@+node:bwmulder.20041011145032:bodyfilter
def bodyfilter(p):
    s = p.bodyString()
    while (s.startswith("@ignore") or
           s.startswith("@nocolor") or
           s.startswith("@wrap")):
       i = g.skip_line(s,0)
       s = s[i:]
    return s
#@-node:bwmulder.20041011145032:bodyfilter
#@+node:ekr.20040331071319.8:underline
# note the first character is intentionally unused, to serve as the underline
# character in a title (in the body of the @rst node)

def underline(h,level):
    
    """Return the underlining string to be used at the given level for headline h."""

    str = """#=+*^~"'`-:><_"""[level]

    return str*max(len(h),4)+'\n'
#@-node:ekr.20040331071319.8:underline
#@+node:bwmulder.20050319191929:http related stuff
#@+node:bwmulder.20050320092000:http_support_main
def http_support_main(tag, fname):
    if config.rst2_http_server_support:
        set_initial_http_attributes(fname)
        find_anchors()
        if tag == 'open2':
            return True
        
        # We relocate references here if we are only running
        # for one file, otherwise we must postpone the
        # relocation until we have processed all files.
        relocate_references() 
      
        config.http_map = None
        config.anchormap = None
        g.es('html updated for html plugin', color="blue")
        if config.rst2_clear_attributes:
            g.es("http attributes cleared")
#@nonl
#@-node:bwmulder.20050320092000:http_support_main
#@+node:bwmulder.20050319191929.1:general routines
#@+node:bwmulder.20050319181934:link_anchor_parser
class link_anchor_parser(HTMLParser):
    #@    @+others
    #@+node:bwmulder.20050319181934.1:docstring
    """
    This subclass of HTMLParser contains functions to recognise anchors and links.
    """
    #@nonl
    #@-node:bwmulder.20050319181934.1:docstring
    #@+node:bwmulder.20050319181934.2:is_anchor
    def is_anchor(self, tag, attrs):
        if tag != 'a':
            return False
        for name, value in attrs:
            if name == 'name':
                return True
        return False
      
    #@-node:bwmulder.20050319181934.2:is_anchor
    #@+node:bwmulder.20050323091905:is_link
    def is_link(self, tag, attrs):
        if tag != 'a':
            return False
        for name, value in attrs:
            if name == 'href':
                return True
        return False
      
    #@-node:bwmulder.20050323091905:is_link
    #@-others
#@nonl
#@-node:bwmulder.20050319181934:link_anchor_parser
#@+node:bwmulder.20050319152820.1:http_attribute_iter
def http_attribute_iter():
    for p in config.http_map.values():
        attr = get_http_attribute(p)
        if attr:
            yield (p, attr)
#@-node:bwmulder.20050319152820.1:http_attribute_iter
#@-node:bwmulder.20050319191929.1:general routines
#@+node:bwmulder.20050314184440:rst_htmlparser
class rst_htmlparser(link_anchor_parser):
    #@    @+others
    #@+node:bwmulder.20050319111254:docstring
    """
    The responsibility of the html parser is:
        1. to find out which html belongs to which node.
        2. to keep a stack of html markings which proceed each node.
        
    Later, we have to relocate inter-file links: if a reference to another location
    is in a file, we must change the link.
    
    """
    #@nonl
    #@-node:bwmulder.20050319111254:docstring
    #@+node:bwmulder.20050315115739:__init__
    def __init__(self, http_map):
        HTMLParser.__init__(self)
        self.stack = None
        # The stack contains lists of the form:
            # [text1, text2, previous].
            # text1 is the opening tag
            # text2 is the closing tag
            # previous points to the previous stack element
        
        self.http_map = http_map
        # see remark in config class
            
        self.node_marker_stack = []
        # self.node_marker_stack.pop() returns True for a closing
        # tag if the opening tag identified an anchor belonging to a vnode.
        
        self.node_code = []
        # Accumulated html code. Once the hmtl code is assigned a vnode,
        # it is deleted here.
        
        self.deleted_lines = 0
        # Number of lines deleted in self.node_code
        
        self.endpos_pending = False
        # self.node_code[0:self.endpos_pending] should not be included in
        # the html code stored in a vnode.
        
        self.last_position = None
        # Last vnode; we must attach html code to this node.
            
    
    #@-node:bwmulder.20050315115739:__init__
    #@+node:bwmulder.20050315115739.1:handle_starttag
    def handle_starttag(self, tag, attrs):
        """
        1. Find out if the current tag is an achor.
        2. If it is an anchor, we check if this anchor marks the beginning of a new 
           node
        3. If a new node begins, then we might have to store html code in the
           previous code.
        4. In any case, put the new tag on the stack.
        """
        is_node_marker = False
        if self.is_anchor(tag, attrs):
            for name, value in attrs:
                if name == 'name' and value.startswith(config.rst2_node_begin_marker):
                    is_node_marker = True
                    break
            if is_node_marker:
                is_node_marker = value
                line, column = self.getpos()
                if self.last_position:
                    lines = self.node_code[:]
                    lines[0] = lines[0][self.startpos:]
                    del lines[line - self.deleted_lines - 1:]
                    if config.rst2_debug_store_lines:
                        print "rst2: Storing in", self.last_position, ":"
                        print lines
                    get_http_attribute(self.last_position).extend(lines)
    
                    if config.rst2_debug_show_unknownattributes:
                        print "rst2: unknownAttributes[config.rst2_http_attributename]"
                        print "For:", self.last_position
                        pprint(get_http_attr(self.last_position))
                                
                if self.deleted_lines < line - 1:
                    del self.node_code[:line - 1 - self.deleted_lines]
                    self.deleted_lines = line - 1
                    self.endpos_pending = True
        if config.rst2_debug_handle_starttag:
            from pprint import pprint
            print "rst2: handle_starttag:", tag, attrs, is_node_marker
        starttag = self.get_starttag_text( ) 
        self.stack = [starttag, None, self.stack]
        self.node_marker_stack.append(is_node_marker)
                
    #@nonl
    #@-node:bwmulder.20050315115739.1:handle_starttag
    #@+node:bwmulder.20050315115739.2:handle_endtag
    def handle_endtag(self, tag):
        """
        1. Set the second element of the current top of stack.
        2. If this is the end tag for an anchor for a node,
           store the current stack for that node.
        """
        self.stack[1] = "</" + tag + ">"
        if config.rst2_debug_handle_endtag:
            from pprint import pprint
            print "rst2: handle_endtag:", tag
            pprint(self.stack)
        if self.endpos_pending:
            line, column = self.getpos()
            self.startpos = self.node_code[0].find(">", column) + 1
            self.endpos_pending = False
        is_node_marker = self.node_marker_stack.pop()
        if is_node_marker and not config.rst2_clear_attributes:
            self.last_position = self.http_map[is_node_marker]
            if is_node_marker != config.last_marker:
                set_http_attribute(self.http_map[is_node_marker], self.stack)
        self.stack = self.stack[2]
        
    #@nonl
    #@-node:bwmulder.20050315115739.2:handle_endtag
    #@+node:bwmulder.20050315115739.3:generate_node_marker
    def generate_node_marker(cls, number):
        
        """
        Generate a node marker for 
        """
        return config.rst2_node_begin_marker + ("%s" % number)
        
    generate_node_marker = classmethod(generate_node_marker)
        
    
    #@-node:bwmulder.20050315115739.3:generate_node_marker
    #@+node:bwmulder.20050315134705:feed
    def feed(self, line):
        self.node_code.append(line)
        HTMLParser.feed(self, line)
    #@-node:bwmulder.20050315134705:feed
    #@-others
#@nonl
#@-node:bwmulder.20050314184440:rst_htmlparser
#@+node:bwmulder.20050319180047:anchor_htmlparser
class anchor_htmlparser(link_anchor_parser):
    #@    @+others
    #@+node:bwmulder.20050319180047.1:docstring
    """
    This htmlparser does the first step of relocating: finding all the anchors within the html node.
    
    Each anchor is mapped to a tuple:
        (current_file, vnode).
        
    Filters out markers which mark the beginning of the html code for a node.
    """
    #@nonl
    #@-node:bwmulder.20050319180047.1:docstring
    #@+node:bwmulder.20050319235437:__init__
    def __init__(self, vnode, first_node):
        HTMLParser.__init__(self)
        self.vnode = vnode
        self.anchormap = config.anchormap
        self.first_node = first_node
    #@-node:bwmulder.20050319235437:__init__
    #@+node:bwmulder.20050319181934.3:handle_starttag
    def handle_starttag(self, tag, attrs):
        """
        1. Find out if the current tag is an achor.
        2. If the current tag is an anchor, update the mapping;
             anchor -> vnode
        """
        if not self.is_anchor(tag, attrs):
            return
        if self.first_node:
            self.anchormap[config.current_file] = (config.current_file, self.vnode)
            self.first_node = False
        for name, value in attrs:
            if name == 'name':
                if not value.startswith(config.rst2_node_begin_marker):
                    self.anchormap[value] = (config.current_file, self.vnode)
                  
    #@nonl
    #@-node:bwmulder.20050319181934.3:handle_starttag
    #@-others
#@-node:bwmulder.20050319180047:anchor_htmlparser
#@+node:bwmulder.20050320102006:link_htmlparser
class link_htmlparser(link_anchor_parser):
    #@    @+others
    #@+node:bwmulder.20050320102006.1:docstring
    """
    This html parser does the second step of relocating links:
        1. It scans the html code for links.
        2. If there is a link which links to a previously processed file, then
           this link is changed so that it now refers to the node.
    """
    #@nonl
    #@-node:bwmulder.20050320102006.1:docstring
    #@+node:bwmulder.20050320102006.2:__init__
    def __init__(self, vnode):
        HTMLParser.__init__(self)
        self.vnode = vnode
        self.anchormap = config.anchormap
        self.replacements = []
    #@-node:bwmulder.20050320102006.2:__init__
    #@+node:bwmulder.20050320102006.3:handle_starttag
    def handle_starttag(self, tag, attrs):
        """
        1. Find out if the current tag is an achor.
        2. If the current tag is an anchor, update the mapping;
             anchor -> vnode
        """
        if not self.is_link(tag, attrs):
            return
        for name, value in attrs:
            if name == 'href':
                href = value
                href_parts = href.split("#")
                if len(href_parts) == 1:
                    href_a = href_parts[0]
                else:
                    href_a = href_parts[1]
                if not href_a.startswith(config.rst2_node_begin_marker):
                    if href_a in self.anchormap:
                        href_file, href_node = self.anchormap[href_a]
                        http_node_ref = mod_http.node_reference(href_node)
                        line, column = self.getpos()
                        self.replacements.append((line, column, href, href_file, http_node_ref))
                                
    #@nonl
    #@-node:bwmulder.20050320102006.3:handle_starttag
    #@+node:bwmulder.20050320155022:get_replacements
    def get_replacements(self):
        return self.replacements
    #@nonl
    #@-node:bwmulder.20050320155022:get_replacements
    #@-others
#@-node:bwmulder.20050320102006:link_htmlparser
#@+node:bwmulder.20050319131813:set_initial_http_attributes
def set_initial_http_attributes(filename):
    http_map = config.http_map
    f = file(filename)
    parser = rst_htmlparser(config.http_map)
    line = f.readline()
    while line:
        parser.feed(line)
        line = f.readline()

#@-node:bwmulder.20050319131813:set_initial_http_attributes
#@+node:bwmulder.20050319131813.1:relocate_references
def relocate_references():
    relocate_references_using_anchormap()
#@-node:bwmulder.20050319131813.1:relocate_references
#@+node:bwmulder.20050319152820:find_anchors
def find_anchors():
    """
    Find the anchors in all the nodes.
    """
    first_node = True
    for vnode, attrs in http_attribute_iter():
        html = reconstruct_html_from_attrs(attrs)
        if config.rst2_debug_node_html_1:
            pprint(html)
        parser = anchor_htmlparser(vnode, first_node)
        for line in html:
            parser.feed(line)
        first_node = parser.first_node
    if config.rst2_debug_anchors:
        print "Anchors found:"
        pprint(config.anchormap)
#@-node:bwmulder.20050319152820:find_anchors
#@+node:bwmulder.20050319153321:relocate_references_using_anchormap
def relocate_references_using_anchormap():
    for vnode, attr in http_attribute_iter():
        if config.rst2_debug_before_and_after_replacement:
            print "Before replacement:", vnode
            pprint (attr)
        http_lines = attr[3:]
        parser = link_htmlparser(vnode)
        for line in attr[3:]:
            parser.feed(line)
        replacements = parser.get_replacements()
        replacements.reverse()
        for line, column, href, href_file, http_node_ref in replacements:
            marker_parts = href.split("#")
            if len(marker_parts) == 2:
                marker = marker_parts[1]
                replacement = "%s#%s" % (http_node_ref, marker)
                attr[line+2] = attr[line+2].replace('href="%s"' % href, 'href="%s"' % replacement)
            else:
                filename = marker_parts[0]
                attr[line+2] = attr[line+2].replace('href="%s"' % href, 'href="%s"' % http_node_ref)
    if config.rst2_debug_before_and_after_replacement:
            print "After replacement"
            pprint (attr)
            for i in range(3): print
            

#@-node:bwmulder.20050319153321:relocate_references_using_anchormap
#@-node:bwmulder.20050319191929:http related stuff
#@-others

if 1: # Ok for unit testing.
    leoPlugins.registerHandler("icondclick1",onIconDoubleClick)
    leoPlugins.registerHandler(("new","open2"), onFileOpen)
    __version__ = "2.4"

    g.plugin_signon(__name__)
#@nonl
#@-node:ekr.20040331071319:@thin rst2.py
#@-leo
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.